如何跟踪视口中的顶部项目以进行列表颤动 [英] How to track the top Item in the viewport for a list flutter
问题描述
所以我的脑袋想不出一种方法来设计这个,我有可滚动的无限列表,列表的每个项目都包含一些信息,一个是日期.
So My head can't think of a way to engineer this, I have scrollable infinite list, every item of the list contains some information and one is date.
所以我想跟踪当前顶部索引项以更新我们所在的部分日期的另一个小部件,有点像某些消息传递应用程序,例如:像信号".
So I'd like to track the current top index Item to update another widget of the section date we are on kinda like some messaging apps do e.g: like 'Signal'.
我尝试了什么
我以为取整个滚动高度并划分项目编号,但这是错误的,因为项目是可扩展的,所以结果很愚蠢.
I thought taking the whole scroll height and dividing the items number but that was wrong as items are expandable so turned out dumb.
有人可以分享一下吗
我正在使用看起来像这样的 CustomScrollView
I am using a CustomScrollView that looks like this
CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: Container(
height: 100,
color: Colors.blue
),
),
SliverToBoxAdapter(
child: Container(
height: 100,
color: Colors.blue
),
),
SliverPersistentHeader(
delegate: PersistentHeader(
widget: Container()
),
pinned: true,
),
// I am trying to trace items in this section
SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return ListTile(title: Text('bron $index'),);
}))
],
)
推荐答案
总之可以通过在ScrollController
中添加Listener
来实现.
In short, you can achieve it by adding Listener
to ScrollController
.
在此解决方案中,您需要知道列表中的第一项偏移量和每个项的高度.
In this solution, you need to know the first item offset in the list and each item height.
final double initialOffset = 300;
final double itemHeight = 50;
那么你可以在每次滚动ScrollController时改变索引:
Then you can change the index every time ScrollController is scrolled:
...
final ScrollController _scrollController = ScrollController();
...
@override
Widget build(BuildContext context) {
final index = ValueNotifier<int>(-1);
_scrollController.addListener(() {
index.value = ((_scrollController.offset-initialOffset)/itemHeight).round()-1;
});
return CustomScrollView(
controller: _scrollController,
slivers: [
SliverToBoxAdapter(
child: Container(height: 100, color: Colors.blue),
),
SliverToBoxAdapter(
child: Container(height: 100, color: Colors.blue),
),
ValueListenableBuilder(
valueListenable: index,
builder: (_,value,__){
return SliverPersistentHeader(
pinned: true,
delegate: PersistentHeader(value),
);
},
),
SliverList(
delegate: SliverChildBuilderDelegate(
...
B.计算孩子在屏幕上的位置
这要复杂得多.
它跟踪屏幕内列表中的所有子项,并从屏幕偏移量返回索引:
It keeps track of all children in the list inside the screen and returns the index from the screen offset:
bool inTopArea(double dy) => dy <= 60 && dy >=0;
此外,它需要全局键来跟踪所有孩子并获取全局位置(来自此处)
Also, it needs Global keys to keep track of all children and gets the global location (from here)
extension GlobalKeyExtension on GlobalKey {
Rect get globalPaintBounds {
final renderObject = currentContext?.findRenderObject();
var translation = renderObject?.getTransformTo(null)?.getTranslation();
if (translation != null && renderObject.paintBounds != null) {
return renderObject.paintBounds
.shift(Offset(translation.x, translation.y));
} else {
return null;
}
}
}
final keys = <GlobalKey>[];
因为你使用builder来生成列表,所以child必须是一个有状态的widget,包含dispose
方法,代表widget不在屏幕外:p>
Because you use builder to generate the list, the child must be a stateful widget that contains dispose
method, which represents the widget is out of the screen:
...
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
final uniqueKey = GlobalKey();
keys.add(uniqueKey);
return MyChildWidget(
index: index,
dispose: (){keys.remove(uniqueKey);},
key: uniqueKey,
);
},
),
),
...
class MyChildWidget extends StatefulWidget {
const MyChildWidget({this.index,this.dispose,Key key}):super(key: key);
final int index;
final VoidCallback dispose;
@override
_MyChildWidgetState createState() => _MyChildWidgetState();
}
class _MyChildWidgetState extends State<MyChildWidget> {
@override
Widget build(BuildContext context) {
return ListTile(
title: Text('bron ${widget.index}'),
);
}
@override
void dispose() {
widget.dispose();
super.dispose();
}
}
最后,滚动时每个子dy位置的计算:
Finally, the calculation of every child dy location when scrolling:
_scrollController.addListener(() {
final tops = keys.where((aKey) {
return inTopArea(aKey.globalPaintBounds.topLeft.dy);
});
if(tops.isNotEmpty){
index.value = (tops.first.currentState as _MyChildWidgetState).widget.index;
}else{
index.value = -1;
}
});
这篇关于如何跟踪视口中的顶部项目以进行列表颤动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!