Flutter:移除isScrollingNotifier监听器致崩溃,需移除吗?原因何在?
问题分析与解决:ScrollController dispose时访问position导致崩溃
崩溃原因
崩溃的核心问题在于:当从新页面返回原页面时,原页面的ListView会先于State的dispose方法被销毁,此时ScrollController已经和滚动视图解绑,scrollController.position这个 getter 内部会触发断言检查_positions.isNotEmpty,而此时_positions是空列表,直接触发了断言错误。
你之前的逻辑没问题——添加监听器后确实需要在dispose时移除,但问题出在移除监听器前获取position的操作,此时控制器已经没有绑定的滚动视图了,所以这一步直接炸了。
解决方案
方案一:判断控制器是否有绑定的position再移除监听器
修改dispose方法,先检查scrollController.positions是否非空,再执行移除操作:
@override void dispose() { print("dispose"); // 先判断控制器是否还有绑定的滚动视图位置 if (scrollController.positions.isNotEmpty) { scrollController.position.isScrollingNotifier.removeListener(handleScrollStartStop); } print("disposed GODVC 2"); scrollController.removeListener(handleScrolled); print("disposed GODVC 3"); scrollController.dispose(); print("disposed GODVC 4"); super.dispose(); }
方案二:改用ScrollNotification监听滚动状态
这种方式不需要直接操作ScrollController的position,更安全:
把原页面的body部分替换为以下代码:
body: NotificationListener<ScrollNotification>( onNotification: (notification) { if (notification is ScrollStartNotification) { print('Scroll started'); } else if (notification is ScrollEndNotification) { print('Scroll stopped'); } return false; // 允许事件继续传递 }, child: ListView.builder( controller: scrollController, itemBuilder: ((context, index) { return ListTile( title: Text("$index. Something"), onTap: () { Navigator.of(context).push( MaterialPageRoute( builder: (context) => MyHomePage( viewNumber: widget.viewNumber + 1, ), ), ); }, ); }), ), ),
同时可以删掉initState中对isScrollingNotifier的监听,以及dispose中对应的移除代码。
总结
你对监听器的管理思路是正确的,只是忽略了控制器和滚动视图解绑的时机——dispose执行时,滚动视图可能已经销毁,控制器不再持有任何position。通过提前判断或改用更安全的监听方式,就能解决这个崩溃问题。
内容的提问来源于stack exchange,提问作者sudoExclamationExclamation
相关产品推荐
相关产品推荐

