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)。