Flutter:Slivers你们族,让滑动视图的组合变得很简单!

更新:

  这篇文章写的比较早了,只是讲了些基础的东西,粗略扫一扫就好了。实际开发中会遇到更多的问题,好比头部自定义动画、上拉刷新/下拉加载、和TabBar组合使用等等场景,所以建议你们可看下法海大佬的扩展ListView的文章:Flutter 扩展NestedScrollView,能够知足大部分开发场景。git

文中全部示例代码请点击:gitee.com/yumi0629/Fl…程序员

  今天呢,我小拉面主要想给你们讲一讲Flutter中的Slivers你们族的使用场景和方法。开发过列表布局的同窗们应该对Slivers系列的控件不陌生,或多或少都用过这个库中的控件,来解决复杂的滑动嵌套布局。
  好比以前讲Hero的时候提到的下面这个界面,使用普通的GridView的话是无法实现的,咱们选择使用CustomScrollView,而后在slivers属性中添加子控件,在这个例子里,咱们能够用SliverToBoxAdapter来作HeaderView,GridView来作主体布局,总体为一个CustomScrollView,彻底不会出现任何滑动冲突的问题。
bash

  Flutter中的 Slivers你们族基本都是配合 CustomScrollView来实现的,除了上面提到的滑动布局嵌套,你还可使用 Slivers来实现页面头部展开/收起、 AppBar随手势变换等等功能。官方的Sliver库里面的控件不少,能够去Flutter API网站搜一下,这篇文章我只讲一些经常使用的控件。   OK, Let's start !!

SliverAppBar

  若是你是一名Android开发者,必定使用过CollapsingToolbarLayout这个布局来实现AppBar展开/收起的功能,在Flutter里面则对应SliverAppBar控件。给SliverAppBar设置flexibleSpaceexpandedHeight属性,就能够轻松完成AppBar展开/收起的功能:app

CustomScrollView(
        slivers: <Widget>[
          SliverAppBar(
            actions: <Widget>[
              _buildAction(),
            ],
            title: Text('SliverAppBar'),
            backgroundColor: Theme.of(context).accentColor,
            expandedHeight: 200.0,
            flexibleSpace: FlexibleSpaceBar(
              background: Image.asset('images/food01.jpeg', fit: BoxFit.cover),
            ),
            // floating: floating,
            // snap: snap,
            // pinned: pinned,
          ),
          SliverFixedExtentList(
            itemExtent: 120.0,
            delegate: SliverChildListDelegate(
              products.map((product) {
                return _buildItem(product);
              }).toList(),
            ),
          ),
        ],
      );
复制代码

sliver_app_bar_01.gif
  若是设置 floating属性为 true,那么AppBar会在你作出下拉手势时就当即展开(即便ListView并无到达顶部),该展开状态不显示flexibleSpace:
sliver_app_bar_02.gif
  若是同时设置 floatingsnap属性为 true,那么AppBar会在你作出下拉手势时就当即所有展开(即便ListView并无到达顶部),该展开状态显示flexibleSpace:
sliver_app_bar_03.gif

  若是不想AppBar消失,则设置pinned属性为true便可:ide

sliver_app_bar_04.gif

SliverList

  SliverList的使用很是简单,只需设置delegate属性便可,咱们通常使用SliverChildBuilderDelegate,注意记得设置childCount,不然Flutter无法知道怎么绘制:函数

CustomScrollView(
        slivers: <Widget>[
          SliverList(
            delegate: SliverChildBuilderDelegate(
                  (BuildContext context, int index) {
                return _buildItem(context, products[index]);
              },
              childCount: 3,
            ),
          )
        ],
      );
复制代码

sliver_list.png

  你也能够经过下面的方式来设置childCount,若是不设置childCount,Flutter一旦发现delegate的某个index返回了null,就会认为childCount就是这个index。布局

delegate: SliverChildBuilderDelegate(
                  (BuildContext context, int index) {
                    if(index>products.length){
                      return null;
                    }
                return _buildItem(context, products[index]);
              },
复制代码

  你也可使用SliverChildListDelegate来构建delegate:post

delegate: SliverChildListDelegate([
              _buildItem(),
              _buildItem(),
              _buildItem(),
            ]),
复制代码

SliverChildListDelegateSliverChildBuilderDelegate的区别:flex

  • SliverChildListDelegate通常用来构item建数量明确的列表,会提早build好全部的子item,因此在效率上会有问题,适合item数量很少的状况(不超过一屏)。
  • SliverChildBuilderDelegate构建的列表理论上是能够无限长的,由于使用来lazily construct优化。 (二者的区别有些相似于ListView和ListView.builder()的区别。)

SliverGrid

  SliverGrid有三个构造函数:SliverGrid.count()SliverGrid.extentSliverGrid()优化

  • SliverGrid.count()指定了一行展现多少个item,下面的例子表示一行展现4个:
SliverGrid.count(children: scrollItems, crossAxisCount: 4)
复制代码
  • SliverGrid.extent能够指定item的最大宽度,而后让Flutter本身决定一行展现多少个item:
SliverGrid.extent(children: scrollItems, maxCrossAxisExtent: 90.0)
复制代码
  • SliverGrid()则是须要指定一个gridDelegate,它提供给了程序员一个自定义Delegate的入口,你能够本身决定每个item怎么排列:
SliverGrid(
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: products.length,
  ),
  delegate: SliverChildBuilderDelegate(
    (BuildContext context, int index) {
      return _buildItem(products[index]);;
    }
);
复制代码

sliver_grid.png

SliverPersistentHeader

  SliverPersistentHeader顾名思义,就是给一个可滑动的视图添加一个头(实际上,在CustomScrollView的slivers列表中,header能够出如今视图的任意位置,不必定要是在顶部)。这个Header会随着滑动而展开/收起,使用pinnedfloating属性来控制收起时Header是否展现(pinnedfloating属性不能够同时为true),pinnedfloating属性的具体意义和SliverAppBar中相同,这里就再也不次解释了。

sliver_persistent_header.gif

SliverPersistentHeader(
      pinned: pinned,
      floating: floating,
      delegate: _SliverAppBarDelegate(
        minHeight: 60.0,
        maxHeight: 180.0,
        child: Container(),
      ),
    );
复制代码

  构建一个SliverPersistentHeader须要传入一个delegate,这个delegate是SliverPersistentHeaderDelegate类型的,而SliverPersistentHeaderDelegate是一个abstract类,咱们不能直接new一个SliverPersistentHeaderDelegate出来,所以,咱们须要自定义一个delegate来实现SliverPersistentHeaderDelegate类:

class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
  _SliverAppBarDelegate({
    @required this.minHeight,
    @required this.maxHeight,
    @required this.child,
  });

  final double minHeight;
  final double maxHeight;
  final Widget child;

  @override
  double get minExtent => minHeight;

  @override
  double get maxExtent => math.max(maxHeight, minHeight);

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    return new SizedBox.expand(child: child);
  }

  @override
  bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
    return maxHeight != oldDelegate.maxHeight ||
        minHeight != oldDelegate.minHeight ||
        child != oldDelegate.child;
  }
}
复制代码

  写一个自定义SliverPersistentHeaderDelegate很简单,只需重写build()get maxExtentget minExtentshouldRebuild()这四个方法,上面就是一个最简单的SliverPersistentHeaderDelegate的实现。其中,maxExtent表示header彻底展开时的高度,minExtent表示header在收起时的最小高度。所以,对于咱们上面的那个自定义Delegate,若是将minHeightmaxHeight的值设置为相同时,header就不会收缩了,这样的Header跟咱们日常理解的Header更像。
  以前也提到了,实际使用时,header不必定要放在slivers列表的最前面,能够随意混搭,固然,通常来讲不会有这种视觉需求的:

CustomScrollView(
        slivers: <Widget>[
          _buildHeader(0),
          SliverGrid.count(
            crossAxisCount: 3,
            children: _products.map((product) {
              return _buildItemGrid(product);
            }).toList(),
          ),
          _buildHeader(1),
          SliverFixedExtentList(
            itemExtent: 100.0,
            delegate: SliverChildListDelegate(
              products.map((product) {
                return _buildItemList(product);
              }).toList(),
            ),
          ),
          _buildHeader(2),
          SliverGrid(
            gridDelegate: new SliverGridDelegateWithMaxCrossAxisExtent(
              maxCrossAxisExtent: 200.0,
              mainAxisSpacing: 10.0,
              crossAxisSpacing: 10.0,
              childAspectRatio: 3.0,
            ),
            delegate: new SliverChildBuilderDelegate(
              (BuildContext context, int index) {
                return _buildItemGrid2(_products2[index]);
              },
              childCount: _products2.length,
            ),
          ),
        ],
      );
复制代码

SliverToBoxAdapter

  SliverPersistentHeader通常来讲都是会展开/收起的(除非minExtent和maxExtent值相同),那么若是想要在滚动视图中添加一个普通的控件,那么就可使用SliverToBoxAdapter来将各类视图组合在一块儿,放在CustomListView中。

sliver_adapter.png
  上图中框起来的部分所有都是SliverToBoxAdapter,结合SliverToBoxAdapter,滚动视图能够任意组合:

CustomScrollView(
        physics: ScrollPhysics(),
        slivers: <Widget>[
          SliverToBoxAdapter(
            child: _buildHeader(),
          ),
          SliverGrid.count(
            crossAxisCount: 3,
            children: products.map((product) {
              return _buildItemGrid(product);
            }).toList(),
          ),
          SliverToBoxAdapter(
            child: _buildSearch(),
          ),
          SliverFixedExtentList(
            itemExtent: 100.0,
            delegate: SliverChildListDelegate(
              products.map((product) {
                return _buildItemList(product);
              }).toList(),
            ),
          ),
          SliverToBoxAdapter(
            child: _buildFooter(),
          ),
        ],
      );
复制代码
相关文章
相关标签/搜索