-
-
Notifications
You must be signed in to change notification settings - Fork 46
2、滚动到指定下标位置
建议搭配滚动视图的 cacheExtent
属性使用,将其赋予适当的值可避免不必要的翻页,分为以下几种情况:
- 如果子部件是固定高度则使用
isFixedHeight
参数即可,不用设置cacheExtent
- 如果是详细页这类滚动视图,建议将
cacheExtent
设置为double.maxFinite
- 如果为子部件不等高的滚动视图,建议根据自身情况将
cacheExtent
设置为比较大且合理的值
正常创建和使用 ScrollController
实例
ScrollController scrollController = ScrollController();
ListView _buildListView() {
return ListView.separated(
controller: scrollController,
...
);
}
创建 ListObserverController
实例并将其传递给 ListViewObserver
ListObserverController observerController = ListObserverController(controller: scrollController);
ListViewObserver(
controller: observerController,
child: _buildListView(),
...
)
现在即可滚动到指定下标位置了
// 无动画滚动至下标位置
observerController.jumpTo(index: 1)
// 动画滚动至下标位置
observerController.animateTo(
index: 1,
duration: const Duration(milliseconds: 250),
curve: Curves.ease,
);
如果你的视图是 CustomScrollView
,其 slivers
中包含了 SliverList
和 SliverGrid
,这种情况也是支持的,只不过需要使用 SliverViewObserver
,并在调用滚动方法时传入对应的 BuildContext
以区分对应的 Sliver
。
SliverViewObserver(
controller: observerController,
child: CustomScrollView(
controller: scrollController,
slivers: [
_buildSliverListView1(),
_buildSliverListView2(),
],
),
sliverListContexts: () {
return [
if (_sliverViewCtx1 != null) _sliverViewCtx1!,
if (_sliverViewCtx2 != null) _sliverViewCtx2!,
];
},
...
)
observerController.animateTo(
sliverContext: _sliverViewCtx2, // _sliverViewCtx1
index: 10,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
如果你的 ListView
或 GridView
有用到其 padding
参数时,也需要同步给该值!如:
ListView.separated(padding: _padding, ...);
GridView.builder(padding: _padding, ...);
observerController.jumpTo(index: 1, padding: _padding);
如果列表子部件的高度是固定的,则建议使用 isFixedHeight
参数提升性能
该功能自从
1.17.0
版本后,开始支持ListView/SliverList
、GridView/SliverGrid
以及其它由第三方构建的ScrollView
,之前的版本只支持ListView/SliverList
// 无动画滚动至下标位置
observerController.jumpTo(index: 150, isFixedHeight: true)
// 动画滚动至下标位置
observerController.animateTo(
index: 150,
isFixedHeight: true
duration: const Duration(milliseconds: 250),
curve: Curves.ease,
);
需要注意的是,如果你的滚动视图是由第三方库构建的,则可能需要使用 renderSliverType
参数去指明处理的方式,如果是 SliverList
和 SliverGrid
,则可以不指明。
renderSliverType
的类型:
enum ObserverRenderSliverType {
list,
grid,
}
如面对由 waterfall_flow
构建的瀑布流视图,在调用 jumpTo
方法时就需要指明 renderSliverType
为 ObserverRenderSliverType.grid
observerController.jumpTo(
index: 13,
isFixedHeight: true,
renderSliverType: ObserverRenderSliverType.grid,
);
用于在滚动到指定下标位置时,设置整体的偏移量。
如在有 SliverAppBar
的场景下,其高度会随着 ScrollView
的滚动而变化,到达一定的偏移量后会固定高度悬浮于顶部,这时就需要使用到 offset
参数了。
SliverAppBar(
key: appBarKey,
pinned: true,
expandedHeight: 200,
flexibleSpace: FlexibleSpaceBar(
title: const Text('AppBar'),
background: Container(color: Colors.orange),
),
);
observerController.animateTo(
...
offset: (offset) {
// 根据目标偏移量 offset,计算出 SliverAppBar 的高度并返回
// observerController 内部会根据该返回值做适当的偏移调整
return ObserverUtils.calcPersistentHeaderExtent(
key: appBarKey,
offset: offset,
);
},
);
alignment
参数用于指定你期望定位到子部件的对齐位置,该值需要在 [0.0, 1.0]
这个范围之间。默认为 0
,比如:
-
alignment: 0
: 滚动到子部件的顶部位置 -
alignment: 0.5
: 滚动到子部件的中间位置 -
alignment: 1
: 滚动到子部件的尾部位置
为了性能考虑,在默认情况下,列表在滚动到指定位置时,ScrollController
会对子部件的信息进行缓存,便于下次直接使用。
但是对于子部件高度一直都是动态改变的场景下,这反而会造成不必要的麻烦,所以这时可以通过对 cacheJumpIndexOffset
属性设置为 false
来关闭这一缓存功能。
如果你想保留滚动的缓存功能,并且只想在特定情况下去清除缓存,则可以使用 clearIndexOffsetCache
方法。
/// Clear the offset cache that jumping to a specified index location.
clearIndexOffsetCache(BuildContext? sliverContext) {
...
}
其形参 sliverContext
只有在你自行管理 ScrollView
的 BuildContext
时才需要传递。
- 方式一:
initialIndex
最简单的使用方式,直接指定下标即可
observerController = ListObserverController(controller: scrollController)
..initialIndex = 10;
- 方式二:
initialIndexModel
定制初始下标位置的配置,各参数说明请看该节的最后部分
observerController = ListObserverController(controller: scrollController)
..initialIndexModel = ObserverIndexPositionModel(
index: 10,
isFixedHeight = true,
alignment = 0.5,
);
- 方式三:
initialIndexModelBlock
回调内返回 ObserverIndexPositionModel
对象, 适用于在一些参数无法一开始就可以确定的场景下使用,如 sliverContext
observerController = SliverObserverController(controller: scrollController)
..initialIndexModelBlock = () {
return ObserverIndexPositionModel(
index: 6,
sliverContext: _sliverListCtx,
offset: calcPersistentHeaderExtent,
);
};
ObserverIndexPositionModel
的定义:
ObserverIndexPositionModel({
required this.index,
this.sliverContext,
this.isFixedHeight = false,
this.alignment = 0,
this.offset,
this.padding = EdgeInsets.zero,
});
属性 | 类型 | 描述 |
---|---|---|
index |
int |
初始下标 |
sliverContext |
BuildContext |
滚动视图的 BuildContext
|
isFixedHeight |
bool |
如果列表子部件的高度是固定的,则建议使用 isFixedHeight 参数提升性能,默认为 false
|
alignment |
double |
指定你期望定位到子部件的对齐位置,该值需要在 [0.0, 1.0] 这个范围之间。默认为 0
|
offset |
double Function(double targetOffset) |
用于在滚动到指定下标位置时,设置整体的偏移量 |
padding |
EdgeInsets |
当你的 ListView 或 GridView 有用到 padding 参数时,也需要同步给该值,其实情况则不需要 |
从 1.18.0
版本开始,新增了一些滚动状态,方便监听,注意,只有在调用了 ObserverController
的 jumpTo
或 animateTo
方法后才会发出。
通知 | 描述 |
---|---|
ObserverScrollStartNotification |
开始执行滚动任务,当前还未找到目标 item
|
ObserverScrollInterruptionNotification |
滚动任务被中断,如:传入了不存在的下标,没有 ScrollController 等 |
ObserverScrollDecisionNotification |
滚动任务决策完毕,当前找到了目标 item
|
ObserverScrollEndNotification |
滚动任务正常执行完毕 |
以上的通知均继承自 ObserverScrollNotification
,可以做为 NotificationListener
所需的泛型。
通知顺序:
ObserverScrollStartNotification
-> ObserverScrollDecisionNotification
-> ObserverScrollEndNotification
.
ObserverScrollInterruptionNotification
不参与上述顺序,在滚动条件不满足时便会发出,发出后滚动任务结束,不会再发出其它通知!
NotificationListener<ObserverScrollNotification>(
child: widget,
onNotification: (notification) {
if (notification is ObserverScrollStartNotification) {
} else if (notification is ObserverScrollInterruptionNotification) {
} else if (notification is ObserverScrollDecisionNotification) {
} else if (notification is ObserverScrollEndNotification) {
}
return true;
},
);
jumpTo
和 animateTo
方法也是在 1.18.0
版本才正式支持 await
,await
后的结果有两种可能:
- 中断(同
ObserverScrollInterruptionNotification
) - 正常结束(同
ObserverScrollEndNotification
)