Flutter:ScrollablePositionedList在build中调用scrollTo报错求助
问题解决:ScrollablePositionedList在build阶段调用scrollTo报错
问题描述
使用ScrollablePositionedList实现索引列表时,按钮点击事件中调用jumpTo/scrollTo方法可正常运行,但在build方法return语句前调用该方法,会抛出断言错误:'_scrollableListState != null'不成立。
错误日志
E/flutter ( 3018): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: 'package:scrollable_positioned_list/src/scrollable_positioned_list.dart': Failed assertion: line 236 pos 12: '_scrollableListState != null': is not true.
代码示例
@override Widget build(BuildContext context) { // ... ItemScrollController idxCtrl = ItemScrollController(); listenable.listen( (p0) { idxCtrl.scrollTo(index: p0, duration: Duration(seconds: 1));//--> 抛出错误 }); // ... return Container( // ... IconButton( onPressed: () { idxCtrl.scrollTo(index: 0, duration: Duration(seconds: 1));//--> 正常运行 }, icon: Icon(Icons.tab)), // ... ScrollablePositionedList.builder( itemScrollController: idxCtrl, // ... // ... ) // ... }
错误原因
核心问题是时机不匹配:在build方法return前调用scrollTo时,ItemScrollController还未与ScrollablePositionedList完成绑定,此时内部的_scrollableListState为null,触发断言失败。而按钮点击事件是在Widget完全渲染完成后触发,控制器已和列表完成绑定,因此能正常执行。
解决方案
方案1:使用WidgetsBinding.instance.addPostFrameCallback
将滚动操作延迟到当前帧渲染完成后执行,确保控制器已与列表绑定:
@override Widget build(BuildContext context) { ItemScrollController idxCtrl = ItemScrollController(); listenable.listen((p0) { // 等待当前Widget渲染完成后执行滚动 WidgetsBinding.instance.addPostFrameCallback((_) { idxCtrl.scrollTo(index: p0, duration: const Duration(seconds: 1)); }); }); return Container( // ... ScrollablePositionedList.builder( itemScrollController: idxCtrl, // ... ), // ... ); }
方案2:将控制器提升为State成员变量
不在build方法内创建ItemScrollController,而是在State初始化阶段创建,保证控制器生命周期与Widget一致,同时结合帧回调确保执行时机:
class _YourWidgetState extends State<YourWidget> { late final ItemScrollController idxCtrl; late final Listenable yourListenable; @override void initState() { super.initState(); idxCtrl = ItemScrollController(); yourListenable = ...; // 初始化你的监听对象 yourListenable.listen((p0) { WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) { idxCtrl.scrollTo(index: p0, duration: const Duration(seconds: 1)); } }); }); } @override Widget build(BuildContext context) { return Container( // ... IconButton( onPressed: () { idxCtrl.scrollTo(index: 0, duration: const Duration(seconds: 1)); }, icon: const Icon(Icons.tab)), // ... ScrollablePositionedList.builder( itemScrollController: idxCtrl, // ... ), // ... ); } }
方案3:通过ItemPositionsListener确认列表状态
监听列表的位置变化,确认列表已初始化完成后再执行滚动操作:
class _YourWidgetState extends State<YourWidget> { late final ItemScrollController idxCtrl; late final ItemPositionsListener itemPositionsListener; late final Listenable yourListenable; bool _isListReady = false; @override void initState() { super.initState(); idxCtrl = ItemScrollController(); itemPositionsListener = ItemPositionsListener.create(); yourListenable = ...; // 监听列表位置,标记列表已准备就绪 itemPositionsListener.itemPositions.addListener(() { if (!_isListReady && itemPositionsListener.itemPositions.value.isNotEmpty) { setState(() { _isListReady = true; }); } }); yourListenable.listen((p0) { if (_isListReady && mounted) { idxCtrl.scrollTo(index: p0, duration: const Duration(seconds: 1)); } }); } @override Widget build(BuildContext context) { return Container( // ... ScrollablePositionedList.builder( itemScrollController: idxCtrl, itemPositionsListener: itemPositionsListener, // ... ), // ... ); } }
内容的提问来源于stack exchange,提问作者Judicael Ramanampison
相关产品推荐
相关产品推荐

