在开发的过程中比较常用的是ListView和GridView,但是如果是复杂一些的页面,单纯的ListView和GridView就不够用了。而Flutter提供了Sliver系列组件来实现复杂的滚动页面,这里就是学习的过程中所作的记录,都是入门级别的,比较简单。
Demo地址位于文章末尾。
下面就开始正文了。
SliverList和SliverGrid
SliverList只有一个属性:delegate,类型是SliverChildDelegate。SliverChildDelegate是一个抽象类,不能直接使用。Flutter中定义好了两个继承于SliverChildDelegate的类对象,可以直接用,分别是:SliverChildListDelegate和SliverChildBuilderDelegate。
先来看SliverChildListDelegate,声明如下:
只有一个必填属性children,是一个类型为Widget的List集合。其他属性几乎不用,暂时忽略。跟ListView构造函数相同,会将所有的子组件一次性的全部渲染出来。
SliverChildBuilderDelegate则跟ListView.build构造函数类似,需要时才会创建,提高了性能。
主要参数是builder,是一个返回值为Widget的函数,原型如下:
这个比较简单就不记录了。
SliverFixedExtentList是固定item高度的SliverList,只是比SliverList多了一个参数itemExtent来设置item高度,其用法跟SliverList一致。
SliverGrid有两个属性:delegate和gridDelegate。其中delegate跟上面的用法一样,gridDelegate的类型是SliverGridDelegate。
SliverGridDelegate是一个抽象类,定义了子控件Layout相关的接口。Flutter中提供了两个SliverGridDelegate的子类SliverGridDelegateWithFixedCrossAxisCount和SliverGridDelegateWithMaxCrossAxisExtent,可以直接使用。下面分别介绍一下。
SliverGridDelegateWithFixedCrossAxisCount
该类实现了一个横轴方向上固定子控件数量的layout的算法,构造函数为:
-
crossAxisCount
:横轴子元素的数量。此属性值确定后子元素在横轴的长度就确定了,即横轴长度除以crossAxisCount的商。 -
mainAxisSpacing
:主轴方向的间距。 -
crossAxisSpacing
:横轴方向子元素的间距。 -
childAspectRatio
:子元素在横轴长度和主轴长度的比例。由于crossAxisCount指定后,子元素横轴长度就确定了,然后通过此参数值就可以确定子元素在竖轴的长度。
子元素的大小是通过crossAxisCount和childAspectRatio两个参数决定的。
SliverGridDelegateWithMaxCrossAxisExtent
该类实现了一个横轴方向上子元素为固定最大长度的layout算法,其构造函数为:
maxCrossAxisExtent
是子元素在横轴方向上的最大长度。之所以是最大长度而不是最终长度,这是因为子元素在横轴方向上的长度仍然是等分的。所以子元素在横轴方向上的元素个数num = 横轴长度 / maxCrossAxisExtent + 1,最终的子元素的宽度= 横轴长度 / num。其他参数和SliverGridDelegateWithFixedCrossAxisCount相同。
SliverGrid的使用方法跟GridView的使用方法保持一致。
SliverAnimatedList
SliverAnimatedList是带有动画的SliverList,先来看构造函数:
-
initialItemCount:item
的个数。 -
itemBuilder
:是一个AnimatedListItemBuilder
函数,原型如下:
使用SliverAnimatedList在添加或删除item的时候,需要通过一下方式来操作:
- 定义一个GlobalKey
- 将key赋值给SliverAnimatedList
- 通过key.currentState.insertItem或key.currentState.removeItem来进行添加或删除。
_buildItem
函数原型如下:
如果想修改动画类型,就需要修改_buildItem
中的动画方式。
SliverPersistentHeader
这个组件可以实现控件吸顶的效果。先来看构造函数:
其中pinned
的效果就是控制header是否保持吸顶效果。另一个重要的属性则是delegate
,它的类型是SliverPersistentHeaderDelegate
,这是一个抽象类,所以要使用的话,需要自己定义一个子类。
子类需要重写4个父类的函数:
其中build
返回header显示的内容,maxExtent
和minExtent
表示最大值和最小值,即header展开和闭合时的高度,若相同则header高度保持不变,若不同,则滚动的时候header的高度会自动在两只之间进行变化。shouldRebuild
表示是否需要重新绘制,需要的话则返回true。
代码如下:
通过SliverPersistentHeader可以实现SliverAppBar的效果。
SliverAppBar
先来看构造函数:
其他的参数都跟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用来设置子控件透明度,构造函数如下:
SliverPadding是设置需要padding的Sliver控件,其构造函数如下:
SliverOpacity 和SliverPadding 中的sliver属性的值必须是Sliver系列的Widget。
SliverPrototypeExtentList
跟SliverList用法基本一致,但是子控件的高度是由prototypeItem的控件高度决定的。构造函数如下:
SliverLayoutBuilder
理解有限,就不记录贻笑大方了。
可查看大佬的文章: Flutter Sliver一辈子之敌 (ExtendedList)
如果在CustomScrollView中用到了其他非Sliver系列的组件,需要使用SliverToBoxAdapter将这些组件包裹起来。
参考文章如下:
1、在Flutter中创建有意思的滚动效果 - Sliver系列 2、Slivers, Demystified
Demo地址:点击跳转
延伸
在Flutter中创建有意思的滚动效果 - Sliver系列
1. 前言
Flutter
作为时下最流行的技术之一,凭借其出色的性能以及抹平多端的差异优势,早已引起大批技术爱好者的关注,甚至一些闲鱼
,美团
,腾讯
等大公司均已投入生产使用。虽然目前其生态还没有完全成熟,但身靠背后的Google
加持,其发展速度已经足够惊人,可以预见将来对Flutter
开发人员的需求也会随之增长。
无论是为了技术尝鲜还是以后可能的工作机会,都9102年了,作为一个前端开发者,似乎没有理由不去尝试它。正是带着这样的心理,笔者也开始学习Flutter
,同时建了一个用于练习的仓库,后续所有代码都会托管在上面,欢迎star,一起学习。这是我写的Flutter系列文章:
- 用Flutter构建漂亮的UI界面 - 基础组件篇
- Flutter滚动型容器组件 - ListView篇
- Flutter网格型布局 - GridView篇
- 在Flutter中使用自定义Icon
在之前的文章中,我们学习了如何使用ListView
和GridView
这两个滚动类型组件。今天,我们就来学习另一个滚动组件CustomScrollView
及其搭配使用的Sliver
系列组件。掌握了它们,你就可以做一些有趣的滚动效果啦~
2. 必备知识
在进入今天的正题之前,我们先来简单了解下今天的两个主角CustomScrollView
和Sliver
:CustomScrollView
是Flutter
提供的可以用来自定义滚动效果的组件,它可以像胶水一样将多个Sliver
粘合在一起。
什么意思呢?举个栗子(你也可以点击这里看youtube
上的一个视频):
假如页面中同时存在一个List
和一个Grid
,虽然它们看起来是一个整体,但是由于各自的滚动效果是分离的,所以没法保证一致的滚动效果。
而使用CustomScrollView
组件作为滚动容器,SliverList
和SliverGrid
分别替代List
和Grid
作为CustomScrollView
的子组件,滚动效果再由CustomScrollView
统一控制,这样就可以了。
其中SliverList
和SliverGrid
就是我们前面提到的Sliver
系列中的两员,除此之外,Sliver
家族还有常用的几个:
-
SliverAppBar
:Creates a material design app bar that can be placed in a CustomScrollView. -
SliverPersistentHeader
:Creates a sliver that varies its size when it is scrolled to the start of a viewport. -
SliverFillRemaining
:Creates a sliver that fills the remaining space in the viewport. -
SliverToBoxAdapter
:Creates a sliver that contains a single box widget. -
SliverPadding
:Creates a sliver that applies padding on each side of another sliver.
注意:由于CustomeScrollView
的子组件只能是Sliver
系列,所以如果你想将一个普通组件塞进CustomScrollView
,那么务必将该组件用SliverToBoxAdapter
包裹。
3. 热身:SliverList / SliverGrid
前面讲了那么多的概念似乎有些枯燥,接下来就让我们从最简单的一个例子入手来看看如何使用CustomScrollView
和SliverList
/SliverGrid
。
其实CustomScrollView
的用法很简单,它有一个slivers
属性,是一个Widget
数组,将子组件都放在里面就可以了,其他的一些滚动相关的属性基本和我们之前学到的ListView
差不多。
再来看看SliverList
,它只有一个delegate
属性,可以用SliverChildListDelegate
或SliverChildBuilderDelegate
这两个类实现。前者将会一次性全部渲染子组件,后者将会根据视窗渲染当前出现的元素,其效果可以和ListView
和ListView.build
这两个构造函数类比。
通过上面的例子我们发现SliverList
的使用方式和ListView
大同小异,而SliverGrid
也是如此,这里就不再过多赘述,来看个两列网格的例子:
接下来,就让我们通过一个实际例子将上面的三点结合在一起。
代码(完整版看这里):
效果图:
上面的例子中还有一点需要注意的是:我们将标题组件放在了SliverToBoxAdapter
内,因为CustomScrollView
只接受Sliver
系列的组件。
4. 眼前一亮的SliverAppBar
AppBar
是常用来构建一个页面头部Bar
的组件,在CustomScrollView
中与其对应的是SliverAppBar
组件。它有什么神奇之处呢?随着页面的滚动,头部Bar
将会有一个收起过渡的效果。我们先来看下效果:
float效果 |
snap效果 |
pinned效果 |
|
|
通过上面的预览图,想必你肯定很好奇SliverAppBar
中的过渡效果是如何实现的~先别急,我们先来看下应该如何使用它:
SliverAppBar
最重要的几个属性在上面的例子中罗列出来。其中:
-
expandedHeight
:展开状态下appBar
的高度,即图中图片所占空间; -
flexibleSpace
:空间大小可变的组件,Flutter
给我们提供了一个现成的FlexibleSpaceBar
组件,给我们处理好了title
过渡的效果。
另外,floating
/snap
/pinned
这三个属性可以指定SliverAppBar
内容滑出屏幕之后的表现形式。
-
float
:向下滑动时,即使当前CustomScrollView
不在顶部,SliverAppBar
也会跟着一起向下出现; -
snap
:当手指放开时,SliverAppBar
会根据当前的位置进行调整,始终保持展开
或收起
的状态; -
pinned
:不同于float
效果,当SliverAppBar
内容滑出屏幕时,将始终渲染一个固定在顶部的收起状态组件。
需要注意的是:snap
效果一定要在float
为true
时才会生效。另外,你也可以将这三者进行组合使用。
5. 花样多变的SliverPersistentHeader
在上一小节中我们见识到了SliverAppBar
的神奇之处,其实它就是基于SliverPersistentHeader
实现的。通过SliverPersistentHeader
,我们还可以实现sticky
吸顶的效果。
SliverPersistentHeader
最重要的一个属性是SliverPersistentHeaderDelegate
,为此我们需要实现一个类继承自SliverPersistentHeaderDelegate
。
可以看到,SliverPersistentHeaderDelegate
的实现类必须实现其4个方法。其中:
-
minExtent
:收起状态下组件的高度; -
maxExtent
:展开状态下组件的高度; -
shouldRebuild
:类似于react
中的shouldComponentUpdate
; -
build
:构建渲染的内容。
接下来,我们就来实现一个TabBar
吸顶的效果。
代码(完整版看这里):
效果图:
根据上面的图我们可以看到,当下方tab
内容滑出屏幕后,tabBar
并没有跟着一起滑走,而是粘在了顶部。可见SliverPersistentHeader
的确可以满足我们的sticky
效果。
不过SliverPersistentHeader
的神奇可远不止如此哦~我们可以通过它自定义一些头部的过渡效果,毕竟SliverAppBar
也是通过它实现的。就比如下方这个电影详情页的头部过渡效果,这在一般的app种还是比较常见的。
那么这种效果要如何实现呢?关键就在于build
方法中的shrinkOffset
属性,它代表当前头部的滚动偏移量。我们可以根据它计算得到当前收起头部的背景颜色
以及图标和文案的字体颜色
,这样就能根据当前位置得到过渡效果啦~
代码(完整版看这里):
上面的代码虽然很长,但大部分是构建widget
的代码。所以,我们重点关注makeStickyHeaderTextColor
和makeStickyHeaderBgColor
即可。这两个方法都是根据当前的shrinkOffset
值计算过渡过程中的颜色值。另外,这里需要注意头部在iPhoneX
及以上的刘海头涉及,可以用SafeArea
组件解决问题。
6. 总结
本文首先介绍了CustomScrollView
和Sliver
系列组件的概念及其关系,接着以SliverList
和SliverGrid
结合的示例说明了其使用方法。然后,又介绍了较常用的SliverAppBar
组件,分别解释了其float
/snap
/pinned
各自的效果。最后,讲解了SliverPersistentHeader
组件的使用方法,并用实际例子加以说明其自定义过渡效果的用法。希望通过本文的介绍,你可以用CustomScrollView
和Sliver
系列组件创建出更有意思的滚动效果~