flutter中有个很好用的Sliver CupertinoSliverRefreshControl
,但是没有提供对应的上拉组件,三方库中有不少不错的支持的刷新组件,比如flutter_easy_refresh。但是基本是需要再滚动视图外包上一个另外的监听组件,似乎没有以Sliver方式实现的简单的上拉组件,所以我按照CupertinoSliverRefreshControl
的实现原理,简单实现一个上拉sliver组件。
CupertinoSliverRefreshControl
实现原理
先回顾下Sliver的布局逻辑:
1、Viewport 将当前布局和配置信息通过 SliverConstraints 传递给 Sliver。
2、Sliver 确定自身的位置、绘制等信息,保存在 geometry 中(一个 SliverGeometry 类型的对象)。
3、Viewport 读取 geometry 中的信息来对 Sliver 进行布局和绘制。
而该段逻辑主要在RenderSliver的performLayout
方法中,需要用到SliverConstraints
的如下几个属性
1 | class SliverConstraints extends Constraints { |
当用户滑动列表,传递给Sliver的约束会不断变化,设置新的geometry和重新布局子组件
1 | const SliverGeometry({ |
CupertinoSliverRefreshControl
结构
先看下CupertinoSliverRefreshControl
的两个主要组成元素。
CupertinoSliverRefreshControl
一个StatefulWidget,他管理的状态就是RefreshIndicatorMode
,并通过transitionNextState方法来更新RefreshIndicatorMode
。_CupertinoSliverRefresh
是个SingleChildRenderObjectWidget,他接受CupertinoSliverRefreshControl的参数配置,并传递给_RenderCupertinoSliverRefresh
。_RenderCupertinoSliverRefresh
是个Sliver,他本身不管理刷新状态,只负责根据目前的sliver约束条件和CupertinoSliverRefreshControl的参数配置来布局和绘制。
通过下面的伪代码,表达下大概是这样的组成方式
1 | CupertinoSliverRefreshControl (refreshIndicatorExtent){ |
CupertinoSliverRefreshControl
总结
发现CupertinoSliverRefreshControl
的逻辑并不复杂,主要通过Sliver的布局变化,触发刷新还是隐藏sliver,其中主要注意点点是:
_CupertinoSliverRefreshControlState
中使用了LayoutBuilder
,其目的是为了拿到sliver返回的Box布局信息,来判断当前的滚动状态,因为_CupertinoSliverRefresh
中并没有给到一个回调机制- 在
_CupertinoSliverRefresh
中动态修改scrollExtent,其实会引起Viewport的抖动,因为后续的sliver布局都受到自己的高度影响,所以geometry中提供了scrollOffsetCorrection
这个修正属性。
实现一个CupertinoSliverLoadMoreControl
CupertinoSliverLoadMoreControl
结构
我们的组件和CupertinoSliverRefreshControl
的结构保持一致,尽量不更改使用期望,只是增加了自动加载和预加载的功能。
用到的约束条件如下
1 | class SliverConstraints extends Constraints { |
另外用到了Viewport的偏移量,从parent
中的offset
可以读取到,和cacheExtent
,表示预加载区或者缓存区,在sliver不在Viewport区域但是在cacheExtent
时,任然会回调布局逻辑。
代码层面的主要修改集中在sliver的performLayout()
中
1 | bool invisible = constraints.precedingScrollExtent <= 0; // 如果前面没有sliver占用空间,认为当前的Viewport是空的,则不显示刷新组件 |
CupertinoSliverLoadMoreControl
总结
下拉组件和核心代码说明已经完成,其他部分代码可以查看仓库,或者试下demo(记得在手机上查看,或者打开移动端调试模式,因为桌面滚动视图不支持弹性滚动overscroll)。