Flutter中使用scrollable_positioned_list时如何重启滚动?
scrollable_positioned_list自动滚动二次触发失效问题解决
问题现象
使用scrollable_positioned_list包实现自动滚动功能时,首次从索引0滚动到最后索引一切正常,但再次从索引0触发滚动时完全失效;手动滑动列表一次后,第二次滚动又能恢复正常。
相关代码
初始化代码
// Initialization final ItemScrollController itemScrollController = ItemScrollController(); final ItemPositionsListener itemPositionsListener = ItemPositionsListener.create();
列表组件实现
// Positioned List Widget Usage Widget _detailedReportList() { return Expanded( child: ScrollablePositionedList.separated( itemScrollController: itemScrollController, itemPositionsListener: itemPositionsListener, // shrinkWrap: true, padding: const EdgeInsets.fromLTRB(16, 0, 16, 60), itemBuilder: (context, index) { return _detailedReportListItem( context, index, _userErrorFramesUri(index), _trainerErrorFramesUri(index), _errorFramesText(index), _errorTimeInSeconds(index), _errorFrameIdx(index), ); }, separatorBuilder: (context, index) { return SizedBox( height: 2, ); }, itemCount: _detailedReportListCount(), ), ); }
自动滚动逻辑
// Autoscrolling listview code if (itemScrollController.isAttached) { itemScrollController.scrollTo( index: errortimeList.indexOf(searchValue), duration: Duration(milliseconds: 300), curve: Curves.easeInOutCubic); if (!mounted) { return; } if (mounted) { // For changing the list item that is currently scrolled to setState(() { _selectedListIndex = errortimeList.indexOf(searchValue); }); } }
解决方案
这个问题本质是列表回到索引0后,ItemScrollController的内部状态未正确同步,导致后续滚动请求被忽略。以下是几种可行的解决办法:
1. 给滚动请求加微小延迟
当从索引0触发滚动时,延迟100ms左右执行,确保列表状态已经稳定:
if (itemScrollController.isAttached) { final targetIndex = errortimeList.indexOf(searchValue); Future.delayed(const Duration(milliseconds: 100), () { if (mounted && itemScrollController.isAttached) { itemScrollController.scrollTo( index: targetIndex, duration: const Duration(milliseconds: 300), curve: Curves.easeInOutCubic, ); setState(() { _selectedListIndex = targetIndex; }); } }); }
2. 触发列表状态刷新后再滚动
在调用scrollTo前先执行一次空的setState,强制更新列表状态:
if (itemScrollController.isAttached) { final targetIndex = errortimeList.indexOf(searchValue); setState(() {}); // 刷新列表状态 itemScrollController.scrollTo( index: targetIndex, duration: const Duration(milliseconds: 300), curve: Curves.easeInOutCubic, ); setState(() { _selectedListIndex = targetIndex; }); }
3. 先跳转再平滑滚动
如果scrollTo直接失效,先用jumpTo定位到目标索引,再执行平滑滚动:
if (itemScrollController.isAttached) { final targetIndex = errortimeList.indexOf(searchValue); itemScrollController.jumpTo(index: targetIndex); Future.delayed(const Duration(milliseconds: 50), () { if (mounted && itemScrollController.isAttached) { itemScrollController.scrollTo( index: targetIndex, duration: const Duration(milliseconds: 300), curve: Curves.easeInOutCubic, ); } }); setState(() { _selectedListIndex = targetIndex; }); }
4. 监听列表位置状态再触发滚动
通过itemPositionsListener监听当前列表的位置,确保滚动请求发出时列表不在过渡状态:
// 初始化时添加监听 @override void initState() { super.initState(); itemPositionsListener.itemPositions.addListener(_onListPositionChanged); } void _onListPositionChanged() { // 可以在这里记录当前列表的位置状态 } // 触发滚动时 if (itemScrollController.isAttached) { final positions = itemPositionsListener.itemPositions.value; if (positions.isNotEmpty) { final currentIndex = positions.first.index; final targetIndex = errortimeList.indexOf(searchValue); if (currentIndex == 0) { Future.delayed(const Duration(milliseconds: 100), () { itemScrollController.scrollTo( index: targetIndex, duration: const Duration(milliseconds: 300), curve: Curves.easeInOutCubic, ); setState(() { _selectedListIndex = targetIndex; }); }); } else { itemScrollController.scrollTo( index: targetIndex, duration: const Duration(milliseconds: 300), curve: Curves.easeInOutCubic, ); setState(() { _selectedListIndex = targetIndex; }); } } } // 记得在dispose时移除监听 @override void dispose() { itemPositionsListener.itemPositions.removeListener(_onListPositionChanged); super.dispose(); }
内容的提问来源于stack exchange,提问作者viki
相关产品推荐
相关产品推荐

