当前位置:   article > 正文

Flutter:Slivers大家族,让滑动视图的组合变得很简单!_flutter slivers

flutter slivers

文中所有示例代码请点击: gitee.com/yumi0629/Fl…

今天呢,我小拉面主要想给大家讲一讲Flutter中的 Slivers 大家族的使用场景和方法。开发过列表布局的同学们应该对 Slivers 系列的控件不陌生,或多或少都用过这个库中的控件,来解决复杂的滑动嵌套布局。

比如之前讲Hero的时候提到的下面这个界面,使用普通的GridView的话是没法实现的,我们选择使用 CustomScrollView ,然后在 slivers 属性中添加子控件,在这个例子里,我们可以用SliverToBoxAdapter来做HeaderView,GridView来做主体布局,整体为一个CustomScrollView,完全不会出现任何滑动冲突的问题。

 

Flutter中的 Slivers 大家族基本都是配合 CustomScrollView 来实现的,除了上面提到的滑动布局嵌套,你还可以使用 Slivers

来实现页面头部展开/收起、 AppBar随手势变换等等功能。官方的Sliver库里面的控件很多,可以去Flutter API网站搜一下,这篇文章我只讲一些常用的控件。 OK, Let's start !!

SliverAppBar

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

  1. CustomScrollView(
  2. slivers: <Widget>[
  3. SliverAppBar(
  4. actions: <Widget>[
  5. _buildAction(),
  6. ],
  7. title: Text('SliverAppBar'),
  8. backgroundColor: Theme.of(context).accentColor,
  9. expandedHeight: 200.0,
  10. flexibleSpace: FlexibleSpaceBar(
  11. background: Image.asset('images/food01.jpeg', fit: BoxFit.cover),
  12. ),
  13. // floating: floating,
  14. // snap: snap,
  15. // pinned: pinned,
  16. ),
  17. SliverFixedExtentList(
  18. itemExtent: 120.0,
  19. delegate: SliverChildListDelegate(
  20. products.map((product) {
  21. return _buildItem(product);
  22. }).toList(),
  23. ),
  24. ),
  25. ],
  26. );

如果设置 floating 属性为 true

,那么AppBar会在你做出下拉手势时就立即展开(即使ListView并没有到达顶部),该展开状态不显示flexibleSpace:


 

 如果同时设置 floating 和 snap 属性为 true

,那么AppBar会在你做出下拉手势时就立即全部展开(即使ListView并没有到达顶部),该展开状态显示flexibleSpace:

如果不想AppBar消失,则设置 pinned 属性为 true 即可:


SliverList

SliverList 的使用非常简单,只需设置 delegate 属性即可,我们一般使用 SliverChildBuilderDelegate ,注意记得设置 childCount ,否则Flutter没法知道怎么绘制:

  1. CustomScrollView(
  2. slivers: <Widget>[
  3. SliverList(
  4. delegate: SliverChildBuilderDelegate(
  5. (BuildContext context, int index) {
  6. return _buildItem(context, products[index]);
  7. },
  8. childCount: 3,
  9. ),
  10. )
  11. ],
  12. );

 

你也可以通过下面的方式来设置childCount,如果不设置childCount,Flutter一旦发现delegate的某个index返回了null,就会认为childCount就是这个index。

  1. delegate: SliverChildBuilderDelegate(
  2. (BuildContext context, int index) {
  3. if(index>products.length){
  4. return null;
  5. }
  6. return _buildItem(context, products[index]);
  7. },

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

  1. delegate: SliverChildListDelegate([
  2. _buildItem(),
  3. _buildItem(),
  4. _buildItem(),
  5. ]),

SliverChildListDelegate 和 SliverChildBuilderDelegate 的区别:

  • SliverChildListDelegate一般用来构item建数量明确的列表,会提前build好所有的子item,所以在效率上会有问题,适合item数量不多的情况(不超过一屏)。
  • SliverChildBuilderDelegate构建的列表理论上是可以无限长的,因为使用来lazily construct优化。 (两者的区别有些类似于ListView和ListView.builder()的区别。)

SliverGrid

SliverGrid 有三个构造函数: SliverGrid.count() 、 SliverGrid.extent 和 SliverGrid() 。

  • 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怎么排列:
  1. SliverGrid(
  2. gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
  3. crossAxisCount: products.length,
  4. ),
  5. delegate: SliverChildBuilderDelegate(
  6. (BuildContext context, int index) {
  7. return _buildItem(products[index]);;
  8. }
  9. );

 

SliverPersistentHeader

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

 

  1. SliverPersistentHeader(
  2. pinned: pinned,
  3. floating: floating,
  4. delegate: _SliverAppBarDelegate(
  5. minHeight: 60.0,
  6. maxHeight: 180.0,
  7. child: Container(),
  8. ),
  9. );

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

  1. class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
  2. _SliverAppBarDelegate({
  3. @required this.minHeight,
  4. @required this.maxHeight,
  5. @required this.child,
  6. });
  7. final double minHeight;
  8. final double maxHeight;
  9. final Widget child;
  10. @override
  11. double get minExtent => minHeight;
  12. @override
  13. double get maxExtent => math.max(maxHeight, minHeight);
  14. @override
  15. Widget build(
  16. BuildContext context, double shrinkOffset, bool overlapsContent) {
  17. return new SizedBox.expand(child: child);
  18. }
  19. @override
  20. bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
  21. return maxHeight != oldDelegate.maxHeight ||
  22. minHeight != oldDelegate.minHeight ||
  23. child != oldDelegate.child;
  24. }
  25. }

写一个自定义SliverPersistentHeaderDelegate很简单,只需重写 build() 、 get maxExtent 、 get minExtent 和 shouldRebuild() 这四个方法,上面就是一个最简单的SliverPersistentHeaderDelegate的实现。其中, maxExtent 表示header完全展开时的高度, minExtent 表示header在收起时的最小高度。因此,对于我们上面的那个自定义Delegate,如果将 minHeight 和 maxHeight 的值设置为相同时,header就不会收缩了,这样的Header跟我们平常理解的Header更像。

之前也提到了,实际使用时,header不一定要放在slivers列表的最前面,可以随意混搭,当然,一般来说不会有这种视觉需求的:

  1. CustomScrollView(
  2. slivers: <Widget>[
  3. _buildHeader(0),
  4. SliverGrid.count(
  5. crossAxisCount: 3,
  6. children: _products.map((product) {
  7. return _buildItemGrid(product);
  8. }).toList(),
  9. ),
  10. _buildHeader(1),
  11. SliverFixedExtentList(
  12. itemExtent: 100.0,
  13. delegate: SliverChildListDelegate(
  14. products.map((product) {
  15. return _buildItemList(product);
  16. }).toList(),
  17. ),
  18. ),
  19. _buildHeader(2),
  20. SliverGrid(
  21. gridDelegate: new SliverGridDelegateWithMaxCrossAxisExtent(
  22. maxCrossAxisExtent: 200.0,
  23. mainAxisSpacing: 10.0,
  24. crossAxisSpacing: 10.0,
  25. childAspectRatio: 3.0,
  26. ),
  27. delegate: new SliverChildBuilderDelegate(
  28. (BuildContext context, int index) {
  29. return _buildItemGrid2(_products2[index]);
  30. },
  31. childCount: _products2.length,
  32. ),
  33. ),
  34. ],
  35. );

SliverToBoxAdapter

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

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

 

上图中框起来的部分全部都是SliverToBoxAdapter,结合SliverToBoxAdapter,滚动视图可以任意组合:

  1. CustomScrollView(
  2. physics: ScrollPhysics(),
  3. slivers: <Widget>[
  4. SliverToBoxAdapter(
  5. child: _buildHeader(),
  6. ),
  7. SliverGrid.count(
  8. crossAxisCount: 3,
  9. children: products.map((product) {
  10. return _buildItemGrid(product);
  11. }).toList(),
  12. ),
  13. SliverToBoxAdapter(
  14. child: _buildSearch(),
  15. ),
  16. SliverFixedExtentList(
  17. itemExtent: 100.0,
  18. delegate: SliverChildListDelegate(
  19. products.map((product) {
  20. return _buildItemList(product);
  21. }).toList(),
  22. ),
  23. ),
  24. SliverToBoxAdapter(
  25. child: _buildFooter(),
  26. ),
  27. ],
  28. );

 



本文来源:码农网
本文链接:https://www.codercto.com/a/34161.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/463057
推荐阅读
相关标签
  

闽ICP备14008679号