Flutter - 船新升级😱支持观察第三方构建的滚动视图💪
# 一、概述
感谢大家对 scrollview_observer (opens new window) 的支持,上一篇关于 scrollview_observer (opens new window) 的最新一篇文章是在2022年的10月份,不知不觉已经过去了8个月,它现在已经变得更加完善和强大,让我们一起来看看都做了哪些更新吧 🥳
版本信息
- 当前版本:
1.13.2
- GitHub地址: https://github.com/LinXunFeng/flutter_scrollview_observer (opens new window)
# 二、自定义触发观察的时机
未添加该功能前,滚动视图在整个滚动过程中都会被观察,计算找出第一个 item
和正在展示的所有 item
,当第一个 item
或者正在展示的 item
的对象发生变化时才会调用 [onObserve]
和 [onObserveAll]
回调。
但往往在一些场景下,我们想要指定在某个滚动状态下才进行观察,以及观察回调的触发时机,那该怎么办呢?
# 1、autoTriggerObserveTypes
参数
现在你可以通过该参数设置自动触发观察的时机,定义如下:
final List<ObserverAutoTriggerObserveType>? autoTriggerObserveTypes;
enum ObserverAutoTriggerObserveType {
scrollStart,
scrollUpdate,
scrollEnd,
}
其默认值为 [.scrollStart, .scrollUpdate, .scrollEnd]
枚举值说明:
枚举值 | 描述 |
---|---|
scrollStart | 开始滚动 |
scrollUpdate | 滚动中 |
scrollEnd | 结束滚动 |
如在视频列表的场景中,我们可能只需要在列表结束滚动时再去做观察哪个 item
是第一个,然后进行自动播放,这个时候设置 autoTriggerObserveTypes
为 .scrollEnd
即可,同时也避免了大量不必要的计算。
# 2、triggerOnObserveType
参数
用于配置触发 [onObserve]
和 [onObserveAll]
回调的前提,定义如下:
final ObserverTriggerOnObserveType triggerOnObserveType;
enum ObserverTriggerOnObserveType {
directly,
displayingItemsChange,
}
其默认值为 .displayingItemsChange
枚举值说明:
枚举值 | 描述 |
---|---|
directly | 观察到数据后直接将数据返出 |
displayingItemsChange | 当列表子部件进出或数量发生变化时将观察到的数据返出 |
一般是在实时获取 item
的 leadingMarginToViewport
(item
顶部与视窗顶部的距离) 和 trailingMarginToViewport
(item
底部与视窗底部的距离)的时候比较常用。
# 三、保持IM会话位置功能增强
之前限制只支持插入一条消息时保持会话位置,现在放开支持多条,使用方式不变,效果如下
注:该功能依赖被插入消息前的最新消息视图做为参照去计算偏移量,所以如果一次性插入的消息数太多,导致该参照消息视图无法得到渲染,则该功能会失效,需要你自己去对
ScrollView
的cacheExtent
设置合理的值来尽量避免这个问题!
# 四、支持观察瀑布流
原先仅针对 GridView
这种常规网格布局,只处理顶部偏移量一致的 item
,现在调整了内部处理逻辑,已支持瀑布流这种模式
如上图红线所示,位于第一的 item
是 grid item2
和 grid item3
。
# 五、支持观察viewport
大家多多少少会遇到这种情况,在 CustomScrollView
中放置了好多 sliver
,需要对它们进行观察,判断哪些 sliver
正在展示,而不仅仅是观察 SliverList
和 SliverGrid
中的 item
。
在这里可以使用 onObserveViewport
回调得到你想要的结果,代码如下
SliverViewObserver(
child: _buildScrollView(),
sliverContexts: () {
return [
if (grid1Context != null) grid1Context!,
if (swipeContext != null) swipeContext!,
if (grid2Context != null) grid2Context!,
];
},
onObserveViewport: (result) {
firstChildCtxInViewport = result.firstChild.sliverContext;
if (firstChildCtxInViewport == grid1Context) {
debugPrint('current first sliver in viewport - gridView1');
} else if (firstChildCtxInViewport == swipeContext) {
debugPrint('current first sliver in viewport - swipeView');
} else if (firstChildCtxInViewport == grid2Context) {
debugPrint('current first sliver in viewport - gridView2');
}
},
)
- 在
sliverContexts
回调中返回需要观察的sliver
的BuildContext
。 - 在
onObserveViewport
回调中拿到观察结果,结果对应的类为SliverViewportObserveModel
class SliverViewportObserveModel {
/// 当前 CustomScrollView 所对应的 viewport 实例.
final RenderViewportBase viewport;
/// 根据 sliverContexts,返回当前观察到的第一个 sliver
final SliverViewportObserveDisplayingChildModel firstChild;
/// 根据 sliverContexts,返回当前观察到的正在展示的所有 sliver
final List<SliverViewportObserveDisplayingChildModel>
displayingChildModelList;
...
}
这里需要注意的是,观察的对象不应为嵌套的 sliver
,如下代码所示:
SliverPadding(
sliver: SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return ListTile(title: Text('index - $index'));
},
),
),
padding: const EdgeInsets.all(8),
);
你需要观察的是 SliverPadding
,而不是 SliverList
,在给 sliverContexts
传递数据时需要留意,否则观察无效。
那一块的 Sliver
的 BuildContext
要怎么获取?
你可以使用 GlobalKey
,但这里更建议使用 SliverLayoutBuilder
SliverLayoutBuilder(
builder: (context, _) {
// 将 context 记录起来
if (sliverCtx != context) sliverCtx = context;
SliverPadding(
sliver: SliverList(
delegate: SliverChildBuilderDelegate(
...
),
),
padding: const EdgeInsets.all(8),
);
},
);
# 六、支持自定义观察对象和观察逻辑
# 1、customTargetRenderSliverType
回调
仅支持
ListViewObserver
和GridViewObserver
在保持原来的观察逻辑上,告诉 scrollview_observer
要处理的 RenderSliver
类型,目的是为了支持对第三方库构建的滚动视图进行观察。
customTargetRenderSliverType: (renderObj) {
// 告诉该库它需要观察什么类型的 RenderObject
// 这里以观察 loading_more_list 的 ListView 为例
return renderObj is ExtendedRenderSliverList;
},
这里罗列几个在 loading_more_list
中常用的 RenderObject
供大家对照使用。
类型 | RenderObject |
---|---|
ListView | ExtendedRenderSliverList |
GridView | ExtendedRenderSliverGrid |
WaterfallFlow | RenderSliverWaterfallFlow |
# 2、customHandleObserve
回调
该回调用于自定义观察逻辑,当自带的处理逻辑不符合你的需求时使用。
customHandleObserve: (context) {
// 可以完全自定义你的观察逻辑
final _obj = context.findRenderObject();
if (_obj is RenderSliverList) {
// 可以使用该库提供的默认处理方式
ObserverCore.handleListObserve(context: context);
}
if (_obj is RenderSliverGrid || _obj is RenderSliverWaterfallFlow) {
// 可以使用该库提供的默认处理方式
return ObserverCore.handleGridObserve(context: context);
}
// 处理一些由第三方构建的 Sliver
...
// 其它类型不做处理
return null;
},
现已将原处理 RenderSliverList
和 RenderSliverGride
的逻辑抽离至 ObserverCore
中供大家自由组合使用。
# 3、extendedHandleObserve
回调
仅支持
SliverViewObserver
该回调用于对原来的观察逻辑进行补充,原来只处理 RenderSliverList
、RenderSliverFixedExtentList
和 RenderSliverGrid
。
extendedHandleObserve: (context) {
// 在对原来的观察逻辑进行拓展
final _obj = context.findRenderObject();
if (_obj is RenderSliverWaterfallFlow) {
return ObserverCore.handleGridObserve(context: context);
}
return null;
},
结合 ObserverCore
和以上的几个功能点,实现了可自由观察由第三方构建的滚动视图的功能,不再局限于官方的 SliverList
和 SliverGrid
。
这是一个很有用的功能,它让你的滚动视图的构建变得更加自由,比如观察使用 fluttercandies/waterfall_flow (opens new window) 构建的瀑布流。
# 七、定位Tab的下标计算支持网格类型
对 ObserverUtils.calcAnchorTabIndex
进行增强,现支持处理列表和网格的模块下标计算,这里直接看效果图
GitHub: https://github.com/LinXunFeng/flutter_scrollview_observer (opens new window)
- 01
- Flutter - 危!3.24版本苹果审核被拒!11-13
- 02
- Flutter - 轻松搞定炫酷视差(Parallax)效果09-21
- 03
- Flutter - 轻松实现PageView卡片偏移效果09-08