Sliver系列组件入门使用记录

在开发的过程中比较常用的是ListView和GridView,但是如果是复杂一些的页面,单纯的ListView和GridView就不够用了。而Flutter提供了Sliver系列组件来实现复杂的滚动页面,这里就是学习的过程中所作的记录,都是入门级别的,比较简单。
Demo地址位于文章末尾。
下面就开始正文了。


SliverList和SliverGrid

SliverList只有一个属性:delegate,类型是SliverChildDelegate。SliverChildDelegate是一个抽象类,不能直接使用。Flutter中定义好了两个继承于SliverChildDelegate的类对象,可以直接用,分别是:SliverChildListDelegateSliverChildBuilderDelegate

先来看SliverChildListDelegate,声明如下:

SliverChildListDelegate(
    this.children, {
    ...
  })

只有一个必填属性children,是一个类型为Widget的List集合。其他属性几乎不用,暂时忽略。跟ListView构造函数相同,会将所有的子组件一次性的全部渲染出来。
SliverChildBuilderDelegate则跟ListView.build构造函数类似,需要时才会创建,提高了性能。

const SliverChildBuilderDelegate(
    this.builder, {
    this.childCount,
    ...
  })

主要参数是builder,是一个返回值为Widget的函数,原型如下:

Widget Function(BuildContext context, int index)

这个比较简单就不记录了。

SliverFixedExtentList是固定item高度的SliverList,只是比SliverList多了一个参数itemExtent来设置item高度,其用法跟SliverList一致。

SliverGrid有两个属性:delegategridDelegate。其中delegate跟上面的用法一样,gridDelegate的类型是SliverGridDelegate
SliverGridDelegate是一个抽象类,定义了子控件Layout相关的接口。Flutter中提供了两个SliverGridDelegate的子类SliverGridDelegateWithFixedCrossAxisCount和SliverGridDelegateWithMaxCrossAxisExtent,可以直接使用。下面分别介绍一下。

SliverGridDelegateWithFixedCrossAxisCount

该类实现了一个横轴方向上固定子控件数量的layout的算法,构造函数为:

SliverGridDelegateWithFixedCrossAxisCount({
  @required double crossAxisCount, 
  double mainAxisSpacing = 0.0,
  double crossAxisSpacing = 0.0,
  double childAspectRatio = 1.0,
})
  • crossAxisCount:横轴子元素的数量。此属性值确定后子元素在横轴的长度就确定了,即横轴长度除以crossAxisCount的商。
  • mainAxisSpacing:主轴方向的间距。
  • crossAxisSpacing:横轴方向子元素的间距。
  • childAspectRatio:子元素在横轴长度和主轴长度的比例。由于crossAxisCount指定后,子元素横轴长度就确定了,然后通过此参数值就可以确定子元素在竖轴的长度。

子元素的大小是通过crossAxisCount和childAspectRatio两个参数决定的。

SliverGridDelegateWithMaxCrossAxisExtent

该类实现了一个横轴方向上子元素为固定最大长度的layout算法,其构造函数为:

SliverGridDelegateWithMaxCrossAxisExtent({
  double maxCrossAxisExtent,
  double mainAxisSpacing = 0.0,
  double crossAxisSpacing = 0.0,
  double childAspectRatio = 1.0,
})

maxCrossAxisExtent是子元素在横轴方向上的最大长度。之所以是最大长度而不是最终长度,这是因为子元素在横轴方向上的长度仍然是等分的。所以子元素在横轴方向上的元素个数num = 横轴长度 / maxCrossAxisExtent + 1,最终的子元素的宽度= 横轴长度 / num。其他参数和SliverGridDelegateWithFixedCrossAxisCount相同。

SliverGrid的使用方法跟GridView的使用方法保持一致。

SliverAnimatedList

SliverAnimatedList是带有动画的SliverList,先来看构造函数:

SliverAnimatedList({
    Key key,
    @required this.itemBuilder,
    this.initialItemCount = 0,
  })
  • initialItemCount:item的个数。
  • itemBuilder:是一个AnimatedListItemBuilder函数,原型如下:
Widget Function(BuildContext context, int index, Animation<double> animation)

使用SliverAnimatedList在添加或删除item的时候,需要通过一下方式来操作:

  1. 定义一个GlobalKey
GlobalKey<SliverAnimatedListState> _listKey = GlobalKey<SliverAnimatedListState>(); 
  1. 将key赋值给SliverAnimatedList
SliverAnimatedList(
              key: _listKey,
              initialItemCount: _list.length,
              itemBuilder: _buildItem,
            )
  1. 通过key.currentState.insertItem或key.currentState.removeItem来进行添加或删除。
_listKey.currentState.insertItem(_index);
_listKey.currentState.removeItem(_index,
        (context, animation) => _buildItem(context, item, animation));

_buildItem函数原型如下:

Widget _buildItem(BuildContext context, int index, Animation<double> animation) {
    return SizeTransition(
      sizeFactor: animation,
      child: Card(
        child: ListTile(
          title: Text(
            'Item $index',
          ),
        ),
      ),
    );
  }

如果想修改动画类型,就需要修改_buildItem中的动画方式。

SliverPersistentHeader

这个组件可以实现控件吸顶的效果。先来看构造函数:

SliverPersistentHeader({
    Key key,
    @required this.delegate,
    this.pinned = false,
    this.floating = false,
  })

其中pinned的效果就是控制header是否保持吸顶效果。另一个重要的属性则是delegate,它的类型是SliverPersistentHeaderDelegate,这是一个抽象类,所以要使用的话,需要自己定义一个子类。
子类需要重写4个父类的函数:

@override
  Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
    // TODO: implement build
    throw UnimplementedError();
  }

  @override
  // TODO: implement maxExtent
  double get maxExtent => throw UnimplementedError();

  @override
  // TODO: implement minExtent
  double get minExtent => throw UnimplementedError();

  @override
  bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {
    // TODO: implement shouldRebuild
    throw UnimplementedError();
  }

其中build返回header显示的内容,maxExtentminExtent表示最大值和最小值,即header展开和闭合时的高度,若相同则header高度保持不变,若不同,则滚动的时候header的高度会自动在两只之间进行变化。shouldRebuild表示是否需要重新绘制,需要的话则返回true。
代码如下:

CustomScrollView(
        slivers: <Widget>[
          // makeHeader('Header Section 1'),
          SliverPersistentHeader(
            pinned: true,
            delegate: _SliverAppBarDelegate(// 自定义的delegate
              minHeight: 60.0,
              maxHeight: 60.0,
              child: Container(
                color: Colors.white,
                child: Center(
                  child: Text('Header Section 1'),
                ),
              ),
            ),
          ),
          SliverGrid.count(
            crossAxisCount: 3,
            children: [
              Container(color: Colors.red, height: 150.0),
              Container(color: Colors.purple, height: 150.0),
              Container(color: Colors.green, height: 150.0),
              Container(color: Colors.orange, height: 150.0),
              Container(color: Colors.yellow, height: 150.0),
              Container(color: Colors.pink, height: 150.0),
              Container(color: Colors.cyan, height: 150.0),
              Container(color: Colors.indigo, height: 150.0),
              Container(color: Colors.blue, height: 150.0),
            ],
          ),
        ],
      )

通过SliverPersistentHeader可以实现SliverAppBar的效果。

SliverAppBar

先来看构造函数:

SliverAppBar({
    this.flexibleSpace,
    this.forceElevated = false,
    this.expandedHeight,
    this.floating = false,
    this.pinned = false,
    this.snap = false,
    this.stretch = false,
    this.stretchTriggerOffset = 100.0,
    this.onStretchTrigger,
    ...
  })

其他的参数都跟AppBar是一致的,就忽略了。其中有一些重要的参数:

  • expandedHeight:展开时AppBar的高度。
  • flexibleSpace:空间大小可变的组件。
  • floating:向上滚动时,AppBar会跟随着滑出屏幕;向下滚动时,会有限显示AppBar,只有当AppBar展开时才会滚动ListView。
  • pinned:当SliverAppBar内容滑出屏幕时,将始终渲染一个固定在顶部的收起状态AppBar。
  • snap:当手指离开屏幕时,AppBar会保持跟手指滑动方向相一致的状态,即手指上滑则AppBar收起,手指下滑则AppBar展开。
  • stretch:是否拉伸。

只有floating设置为true时,snap才可以设置为true。

FlexibleSpaceBar是Flutter提供的一个现成的空间大小可变的组件,并且处理好了title和background的过渡效果。重点说一下其中的stretchModes属性,这个是用来设置AppBar拉伸效果的,有三个枚举值,可以互相搭配使用,但是前提是stretch为true。

  • blurBackground:拉伸时使用模糊效果。
  • fadeTitle:拉伸时标题将消失。
  • zoomBackground默认值,拉伸时widget将填充额外的空间。

SliverFillRemaining和SliverFillViewport

SliverFillRemaining会自动充满视图的全部空间,通常用于slivers的最后一个。
SliverFillViewport 生成的每一个item都占满全屏。
用法都比较简单,就没有记录。
详细可查看demo

SliverOpacity和SliverPadding

SliverOpacity用来设置子控件透明度,构造函数如下:

SliverOpacity({
    Key key,
    @required this.opacity,
    this.alwaysIncludeSemantics = false,
    Widget sliver,
  })

SliverPadding是设置需要padding的Sliver控件,其构造函数如下:

SliverPadding({
    Key key,
    @required this.padding,
    Widget sliver,
  })

SliverOpacity 和SliverPadding 中的sliver属性的值必须是Sliver系列的Widget。

SliverPrototypeExtentList

跟SliverList用法基本一致,但是子控件的高度是由prototypeItem的控件高度决定的。构造函数如下:

SliverPrototypeExtentList({
    Key key,
    @required SliverChildDelegate delegate,
    @required this.prototypeItem,
  })

SliverLayoutBuilder

理解有限,就不记录贻笑大方了。
可查看大佬的文章: Flutter Sliver一辈子之敌 (ExtendedList)

如果在CustomScrollView中用到了其他非Sliver系列的组件,需要使用SliverToBoxAdapter将这些组件包裹起来。

参考文章如下:
1、在Flutter中创建有意思的滚动效果 - Sliver系列
2、Slivers, Demystified

Demo地址点击跳转

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342