Android开发经验谈FlutterFlutter学习

Flutter Candies 一桶天下

2020-07-08  本文已影响0人  Alvin老师

Flutter Candies 介绍

这是一个集合了Flutter开发中常用的组件/插件🍬的全家桶,大家可以很方便的集成到自己的项目当中,下面我按照作者来介绍Flutter Candies 的各个项目。篇幅会有点长,请准备好瓜子汽水坐好,开车了,滴滴滴滴滴。

AlexV525

从开始在群里提需求,到可以在群里解答问题 ;从一个养鱼专业的学生,到入行Flutter岗位工作。4月份入群到现在,看到了他的变化,也看到🍬组织的意义。入坑之后就住在了Github上面。

OpenJMU校园应用

精美的校园App,整个应用的完成度非常高,功能完整,因为账号权限的问题,无法登录。如果你看到某个功能,可以去找这个功能的实现代码,找不到的话可以来群里问问。

| | |

|
| --- | --- | --- |

| | |

|

| | |

|

GitHub 项目

从0搭建的Flutter实战项目,项目代码继续更新中,感兴趣的小伙可以关注, 掘金文章地址,跟着从0开始写Flutter项目。

波纹扩散高斯模糊动画

波纹扩散,并且带高斯模糊,按钮跳动效果,喜欢骚的童鞋可以收藏起来了。掘金文章地址

CrazyQ1

Flutter教程网的作者,这是一个Flutter全方面信息的聚合网站.

Flutter版微信

从0搭建的Flutter版微信实战项目,项目功能代码继续更新中,感兴趣的小伙可以关注他以及后续相关文章

| | |

|
| --- | --- | --- |
| | | |

| | |

|
| | | |

| | |

入魔的冬瓜

最近刚入桶的兄弟,有责任心的开发者,对自己的项目会不断进行优化,达到最完美的状态

自定义日历组件

主要功能

| | |

|
| --- | --- | --- |
| | | |

| | |

|

| |

| |

| |

Flutter 版本的网易云音乐

| | |

|
| --- | --- | --- |

| | |

|

| | |

|

| |

| |

学习Flutter的一个途径就是模仿现有的App,在Flutter上面进行实现。这是一个从0搭建的网易云音乐。大家可以跟随着作者的思路一步步完成一个完整的Flutter项目。界面很舒服,功能持续更新中,大家快来收藏吧 掘金文章地址

仿微信长按弹出菜单

目前最好用的仿微信聊天长按弹出框,喜欢微信风格的小伙伴记得收藏起来掘金文章地址

caijinglong

低调,老乡,不能吃辣的四川人(偷笑.gif), 反正就很牛逼就是了。Flutter 小白鼠,任何新东西他都要去玩一下。我也跟着踩他填好的坑,在Flutter的路上一去不回。Pub 25个。。可怕。。都是高分作品

flutter_image_editor

flutter_image_editor可以说是低调为extended_image量身打造的原生插件,支持旋转裁剪翻转,extended_image负责图片编辑UI,flutter_image_editor提供原生裁剪图片数据能力。由于dart image库在处理图片的效率问题,原生库(期待纯C++库)就有了很大的优势(大图片可以有10倍速度的提升)。正是因为teamwork才能使flutter上面对于图片编辑处理最终达到一个完美的状态。

法的空间

法法,200,Flutter Candies 🍬 全家桶的楼主。在感受到大佬们的感化之后,也希望能帮助到更多的Flutter开发者。pub目前13个,属于项目中比较常用的组件。

extended_nested_scroll_view

做项目遇到的第一个遇到的问题就是官方的NestedScrollView,各方求证以及查看源码之后发现应该只是as design。如果你即将使用官方的NestedScrollView,强力建议先看一下,你必定会遇到下面的问题 掘金文章地址

问题1. NestedScrollView的Header中有锁定(Pinned=true)的Sliver组件的时候,body的滚动问题

其实官方对这个是有处理的,是通过SliverOverlapAbsorber包裹SliverAppbar,代码不上了,其实NestedScrollView的注释里面就有.

但是问题在,于如果header里面有多个pinned=true的sliver,我们该怎么处理了?为此我扩展了官方的组件。你可以通过设置pinnedHeaderSliverHeightBuilder回调来解决这个问题,在下面的代码中,pinnedHeaderHeight其实就是SliverAppbar最后折叠起来之后高度=状态栏高度+导航栏高度.如果你header中有其他锁定的sliver,你可以再把它们的高度加进去。

      var pinnedHeaderHeight =
          //statusBar height
          statusBarHeight +
              //pinned SliverAppBar height in header
              kToolbarHeight;

 return NestedScrollView(
        pinnedHeaderSliverHeightBuilder: () {
          return pinnedHeaderHeight;
        },

问题2. NestedScrollView的body中TabView里面的列表会滚动同步互相影响

由于NestedScrollView内部里面有一个ScrollController,TabView里面的列表的ScrollPosition都将会attach到这个sc上面,在滚动其中一个的时候,同步全部的ScrollPositions。由于篇幅原因,我这里直接放之前的文章了。

问题3. 不能给body里面的列表设置ScrollController

由于body里面的列表必须共用同一个ScrollController,所以你是不能给列表单独设置ScrollController。如果设置了会导致内部的outer_scroll_controller和inner_scroll_controller无法协同工作。

大家想使用ScrollController无非3点。

1.下拉刷新,已提供demo

2.加载更多,已提供demo

3.控制列表滚动,已将属性暴露出来

使用常见问题
import 'package:flutter/widgets.dart' hide NestedScrollView;

extended_image

Flutter上面最能打的图片库(这样说没人打我吧) 掘金文章地址

主要功能

使用常见问题

设计原则是,如果你需要重写一个状态,就返回你想要的效果,否则请返回null。下面是一个重写“正在加载”的例子

    ExtendedImage.network(
      item.imageUrl,
      loadStateChanged: (ExtendedImageState state) {
        if (state.extendedImageLoadState == LoadState.loading) {
          return Container();
        }
        return null;
      },
    );

很多人因为在重写了completed状态而丢失了手势和裁剪功能,为此我新增了ExtendedImageState.completedWidget。下面是重写completed状态而不丢失裁剪手势功能的例子。

    ExtendedImage.network(
     item.imageUrl,
     loadStateChanged: (ExtendedImageState state) {
       if (state.extendedImageLoadState == LoadState.completed) {
         return Padding(
           padding: EdgeInsets.all(10.0),
           child: state.completedWidget,
         );
       }
       return null;
     },
   );

请自行查看issue

extended_image_library

extended_image的基础库,如果你只需要网络图片缓存功能,你可以只引用这个库

    Image(
      image: ExtendedNetworkImageProvider("", cache: true),
    );

extended_text

富有中国特色强大功能的富文本 掘金文章地址

主要功能

使用常见问题

为了快速构建富文本,比如把 '[love]' 这种文字变成表情图片,提供了SpecialTextSpanBuilder。其原理很简单,就是根据你自己的规则,将文字转换成各种InlineSapn(TextSpan,ImageSpan,WidgetSpan).

这里build方法是用入栈的方式遍历文字,createSpecialText方法是用来根据你自己的规则创建SpecialText的。有一些用户可能觉得太复杂了,但是我这样考虑就是为了大家能根据自己业务的需求来自定义自己的规则。耐心看懂demo,你可以快速的自定义自己的富文本

abstract class SpecialTextSpanBuilder
{
    TextSpan build(String data,
      {TextStyle textStyle, SpecialTextGestureTapCallback onTap})

    SpecialText createSpecialText(String flag,
      {TextStyle textStyle, SpecialTextGestureTapCallback onTap, int index});  
}

extended_text_field

Flutters上面唯一能支持输入内容是WidgetSpan的输入框 掘金文章地址

主要功能

仿掘金推特点赞按钮

仿掘金推特点赞按钮,带数量滚动动画掘金文章地址

下拉刷新

可以在任何滚动场景中使用(包括NestedScrollView)的自定义下拉刷新组件, 只提供了下拉刷新的状态,你可以最大化的自定义自己的效果而不用拘泥于组件本身提供。你可以根据状态距离,构建出任何你想要的效果。demo里面的4种效果只是例子,欢迎大家pr更多更有趣的效果。掘金文章地址

使用常见问题

这2个问题都可以通过设置列表的physics来解决,我这里提供了AlwaysScrollableClampingScrollPhysics。

///in case list is not full screen and remove ios Bouncing
class AlwaysScrollableClampingScrollPhysics extends ClampingScrollPhysics {
  const AlwaysScrollableClampingScrollPhysics({ScrollPhysics parent})
      : super(parent: parent);

  @override
  AlwaysScrollableClampingScrollPhysics applyTo(ScrollPhysics ancestor) {
    return AlwaysScrollableClampingScrollPhysics(parent: buildParent(ancestor));
  }

  @override
  bool shouldAcceptUserOffset(ScrollMetrics position) {
    return true;
  }
}

扩展列表

支持特殊布局,内存清除以及可视区域Indexes变化追踪。更多详情

回收图片缓存

              collectGarbage: (List<int> indexes) {
                        ///collectGarbage
                        indexes.forEach((index) {
                          final item = listSourceRepository[index];
                          if (item.hasImage) {
                            item.images.forEach((image) {
                              final provider = ExtendedNetworkImageProvider(
                                image.imageUrl,
                              );
                              provider.evict();
                            });
                          }
                        });
                      },
| | |

|
| --- | --- | --- |

瀑布流

高性能的瀑布流布局,喜欢的瀑布流的小伙伴赶快收藏起来。

| |

|
| --- | --- |

列表加载更多

继承了UWP的加载更多思想的作品,将UI跟数据源的契约完美打通,你只要写过一次,你就会爱上这种方式,新增支持瀑布流。掘金文章地址

extended_tabs

支持TabBarView的联动,就是说二级TabBarView不能滑动了,就看一级能不能滑动,能滑动就滑动一级的 掘金文章地址

路由注解

轻巧灵活的路由注解工具,一行命令完成路由映射. 掘金文章地址

Json转换Dart实体类工具

使用C#以及Flutter构建的json转换dart实体类工具,提供超级多的平台选择,大气的ui界面(偷笑.gif) 掘金文章地址

平台 语言 描述 代码/安装包地址
windows C# uwp构建,运行环境windows10,x86/x64 windows-uwp.zip
windows C# wpf构建,运行环境windows10/windows8/widnows7,x86/x64 windows-wpf.zip
windows dart flutter构建, 使用官方方式编译,x64 ,debug版本 windows-x64-flutter.zip
windows dart flutter构建, 使用go-flutter编译,x64 ,debug版本 windows-x64-go-flutter.zip
mac dart flutter构建,使用go-flutter编译(官方方式,未找到产物) mac-go-flutter.zip
web C# silverlight构建, 需要安装silverlight插件,有浏览器限制 网页地址带字体文件网页地址
web dart flutter-web构建 网页地址
linux dart flutter构建, 使用官方方式编译,(没有环境测试,假装可以用) 代码地址

Flutter常见问题

从哪里上手Flutter

Flutter中国官方网站,Flutter官方明媒正娶,从入门到深入,各种资源应有尽有。如果你准备入手Flutter,这应该是你必看的网站。

萌新问问题

每个人都从萌新而来,如果你想开森的写代码,自己解决问题是必须的

怎么找插件

把你的想要效果的插件的名字或者其他平台的组件叫什么名字,转为为英文,到pub 上面搜索,分高的使用的人多,相对就比较稳定更好。

安卓下载各种东西慢

我们经常运行别人组件的时候,发现各种下载失败,各种下载缓慢 请使用国内镜像,飞一般的赶脚。

google()

jcenter()

替换为下面

maven { url 'https://maven.aliyun.com/repository/google' }

maven { url 'https://maven.aliyun.com/repository/jcenter' }

maven { url 'http://maven.aliyun.com/nexus/content/groups/public' }

#distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
#csdn镜像
distributionUrl=https\://downloads.gradle-dn.com/distributions/gradle-5.1.1-all.zip

为啥设置Size没有效果

在开发中,我们发现给组件设置了大小,但是却没有效果。举个栗子,在Appbar的leading/actions中,我们想直接设置宽高是不可能的。 我们需要使用UnconstrainedBox来移除父widget对子大小的限制,如下代码

      AppBar(
        title: Text(widget.title),
        leading: UnconstrainedBox(
          child: Container(
            width: 50.0,
            height: 10.0,
            color: Colors.red,
          ),
        ),
      ),

键盘弹出的时候,怎么把布局顶上去,而不遮住布局

double keyboardHeight = MediaQuery.of(context).viewInsets.bottom;
    Column(
      children: <Widget>[
        ///你的布局
        Expanded(
          child: Container(),
        ),
        ///顶起你的布局
        Container(
          height: keyboardHeight,
        )
      ],
    );

其实这个我也是在源码里面scaffold.dart中看到的,抄作业很重要

关键代码

并且在_ScaffoldLayout中增加了这个高度

pub.dev 慢,打不开?!

中国镜像你在等什么呢,学起来

国际化之后设置hintText,光标位置不一致

issue地址 通过设置下面代码解决,全局设置文字主题

 textTheme: theme.textTheme.copyWith(
          subhead: theme.textTheme.subhead
              .copyWith(textBaseline: TextBaseline.alphabetic)),

我的文本设置了溢出,为啥没有效果

我们经常听到的就是为什么我的文本设置了溢出显示,但是它却显示黄色溢出警告了? 对于文本Text来说,如果它的父Widget没有给它做大小限制,它默认是无限长的 我们解决的方法如下。

     return Row(
      children: <Widget>[
        Flexible(
          child: Text(
            "我是一个很短的文本",
            overflow: TextOverflow.ellipsis,
            style: TextStyle(fontSize: 12),
          ),
        ),
        Container(
          color: Colors.red,
          width: 100.0,
        )
      ],
    );
    return Row(
      children: <Widget>[
        Expanded(
          child: Text(
            "我是一个很短的文本",
            overflow: TextOverflow.ellipsis,
            style: TextStyle(fontSize: 12),
          ),
        ),
        Container(
          color: Colors.red,
          width: 100.0,
        )
      ],
    );

Expaned/Flexible 两者的区别就在于,Expanded会强制占100剩余的空间,而Flexible可以自适应。当然他们最大也都是占100剩余的空间。

获取滚动组件中孩子的位置

demo

更多的细节,你可以查看文章

官方的Appbar不好用

由于官方的Appbar增加了对material风格的设置,所以很多地方都有一些限制。其实也不能怪官方,这是一种设计规范。但是我们就是想自己骚怎么办呢?? 我之前写了个 my_app_bar.dart,之前一直放gists上面,不方便大家查看,现在也移入了🍬。这是一个简单的实现,看明白了代码原理之后,你可以扩展出更多适合自己的appbar。

掘金斗鱼的那种首页滚动效果怎么做?

因为有好几个人问过,所以我写了个简单的demo。主要是靠NotificationListener来监听滚动来控制头部。

黄色警告 ⚠ 一定要管吗?

不是每一种警告,都是一定要管的。警告只是官方告诉你需要注意这点,这就是为什么Release下面不会出现警告。警告不等于错误。

我这里举一个栗子。NestedScrollView的body其实是SliverFillRemaining包住的,它会跟着一起滚动。但是我们这里布局里面是一个Tabbar,它是一个有固定高度的组件,当它超出屏幕的时候就会出现警告。但是事实上,这种警告并不能影响我们的使用。

push/pop的时候页面重新build

如果你是因为页面重新build就影响你的业务,那应该好好检查下业务逻辑代码。build的原因是route变化时候的动画,以及NavigatorState中Overlay(实际就是Stack),route位置变化而引起的页面build。

ListView嵌套,shrinkWrap 的使用

Listview里面嵌套Listview,是我经常看到过的,会报错。然后就会有人说 把这个设置成shrinkWrap 设置成true。下面我们来看看这个属性的意思。

  /// Whether the extent of the scroll view in the [scrollDirection] should be determined by the contents being viewed.
  ///
  /// If the scroll view does not shrink wrap, then the scroll view will expand to the maximum allowed size in the [scrollDirection]. If the scroll view has unbounded constraints in the [scrollDirection], then [shrinkWrap] must be true.
  ///
  /// Shrink wrapping the content of the scroll view is significantly more expensive than expanding to the maximum allowed size because the content can expand and contract during scrolling, which means the size of the scroll view needs to be recomputed whenever the scroll position changes.
  ///
  /// Defaults to false.
  final bool shrinkWrap;

Sliver 一生之敌?一生之爱?

群里萌萌说的时候,我有点惊讶道,但是Sliver确实是经常有人问到的。

Sliver系列的东东很多,我们下面来一一介绍一下。

    SliverPadding(
      sliver: SliverList(),
      padding: EdgeInsets.all(5.0),
    );

其实Sliver系列没有那么可怕,如果你弄懂每一个Sliver的使用场景,你可以完成其他平台可能需要很费劲才能做出来的效果。下面举一些常用的栗子吧。

    return CustomScrollView(
      slivers: <Widget>[
        SliverList(),
        SliverList(),
      ],
    );

如果你是水平垂直嵌套的话,可以设置内部那个Listview的高度或者宽度

    return ListView(
      children: <Widget>[
        SizedBox(
          height: 60.0,
          child: ListView.builder(
              itemBuilder: (c, index) {}, scrollDirection: Axis.horizontal),
        )
      ],
    );

注意这个NestedScrollView不是官方的,是我扩展的。详细情况请看上面


///因为SliverAppBar是pinned:true,所以我们需要算上这部分高度
      var pinnedHeaderHeight =
          //statusBar height
          statusBarHeight +
              //pinned SliverAppBar height in header
              kToolbarHeight;

 return NestedScrollView(
        pinnedHeaderSliverHeightBuilder: () {
          return pinnedHeaderHeight;
        },
      headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled)
      {
        return <Widget>[
         SliverAppBar(pinned:true),
         //可是banner,也可以是广告位之类的
         SliverToBoxAdapter(),
         //当然可以个列表
         SliverList(),
         //我们也可以在这里创建可以吸顶的内容
         //如果这个是pinned等于true的,记得把这部分高度增加到pinnedHeaderHeight当中
         SliverPersistentHeader()
        ];
      },
      body: Column(
        children: <Widget>[
          ///最终Tabbar会被Pinned在SliverAppbar(pinned:true)下面。
          ///当然,比如说sliverappbar下面 有另外一个pinned为ture的 ///sliver。那边tabbar最后会在这个pinned ///true的sliver下面。(总的pinnedHeaderHeight高度应该是(状///态栏高度+导航栏高度=pinned为true的SliverAppbar)+pinne///d为ture的Sliver的高度)
          TabBar(),
          Expanded(
            child: TabBarView(),
          )
        ],
      ),
    );

这样写的一个好处,内容可以被回收,性能好。

    return CustomScrollView(
      slivers: <Widget>[
        //分组头部1,
        SliverToBoxAdapter(),
        //分组1
        //分组2
        当然我们在SliverList的第一item里面特殊处理下,增加分组头
        SliverList(),
              SliverList(delegate: SliverChildBuilderDelegate((c, index) {
          ///item 内容
          Widget item = Container();
          if (index == 0) {
            item = Column(
              children: <Widget>[
                ///分组2头部
                Container(),
                item
              ],
            );
          }
          return item;
        })),
      ],
    );

当然栗子是举不完的,但是当你学会使用之后,终于有一天,一生之敌会变成你的一生之爱的。

其他常见问题

之前收集的一些问题

作者:法的空间
链接:https://juejin.im/post/5d8d5a105188256ec311fa99

上一篇下一篇

猜你喜欢

热点阅读