Flutter中showDialog无限重建问题求助
问题描述
点击带分页功能的订单列表项弹出Dialog后,Dialog的builder函数会无限打印"inside"(即Dialog无限重建),相关代码位于一个Stateful Widget中:
final _scrollController = ScrollController(); @override void initState() { _scrollController.addListener(() { pagination(); }); super.initState(); } void pagination() { var len = Provider.of<OrderHistoryData>(context, listen: false).allOrders.length; if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent && !Provider.of<OrderHistoryData>(context, listen: false).isLoading && (len % 20) == 0 && Provider.of<OrderHistoryData>(context, listen: false).next != null) { Provider.of<OrderHistoryData>(context, listen: false) .fetchMoreOrderHistory(); } } @override void dispose() { super.dispose(); _scrollController.dispose(); } void showFullOrderDetails(ModelOrderHistory order) { print('showDialog'); showDialog( context: context, builder: (context) { print('inside'); return Dialog( insetPadding: const EdgeInsets.all(16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(31.11)), child: const Padding( padding: EdgeInsets.only(top: 20.0, right: 20, bottom: 50, left: 26), child: Text('sdf'), ), ); }, ); }
原因分析
这不是Flutter的bug,核心是滚动监听触发的无限循环+Dialog与宿主Widget的上下文绑定:
- Dialog弹出时,页面布局发生变化,列表的滚动位置/滚动范围(
maxScrollExtent)改变,触发_scrollController的监听回调,执行pagination()。 - 若分页条件满足,
fetchMoreOrderHistory()会修改OrderHistoryData中的状态(如isLoading、allOrders),触发宿主Stateful Widget重建。 - 由于
showDialog的builder函数绑定在宿主Widget的上下文上,宿主重建时builder会被重新执行(打印"inside");同时Dialog的再次创建又会引发布局变化,再次触发滚动监听,形成无限循环。
解决方法
方法1:弹出Dialog时临时移除滚动监听
在弹出Dialog时暂停滚动监听,关闭后恢复,切断循环触发源:
void showFullOrderDetails(ModelOrderHistory order) { print('showDialog'); // 移除滚动监听 _scrollController.removeListener(pagination); showDialog( context: context, builder: (dialogContext) { print('inside'); return Dialog( insetPadding: const EdgeInsets.all(16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(31.11)), child: const Padding( padding: EdgeInsets.only(top: 20.0, right: 20, bottom: 50, left: 26), child: Text('sdf'), ), ); }, ).whenComplete(() { // Dialog关闭后恢复监听 _scrollController.addListener(pagination); }); }
方法2:强化分页逻辑的防重复触发
提前判断isLoading状态,避免在请求过程中重复触发分页:
void pagination() { final orderData = Provider.of<OrderHistoryData>(context, listen: false); // 先判断加载状态,直接拦截重复触发 if (orderData.isLoading) return; var len = orderData.allOrders.length; if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent && (len % 20) == 0 && orderData.next != null) { orderData.fetchMoreOrderHistory(); } }
方法3:确保Dialog使用独立上下文
通过builder获取Dialog自身的上下文,避免与宿主Widget的上下文强绑定(此方法需配合前两种使用,单独使用可能无法完全解决循环问题):
void showFullOrderDetails(ModelOrderHistory order) { print('showDialog'); showDialog( context: context, // 使用Dialog自身的上下文,避免依赖宿主上下文 builder: (dialogContext) { print('inside'); return Dialog( insetPadding: const EdgeInsets.all(16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(31.11)), child: const Padding( padding: EdgeInsets.only(top: 20.0, right: 20, bottom: 50, left: 26), child: Text('sdf'), ), ); }, ); }
内容的提问来源于stack exchange,提问作者ulukbek
相关产品推荐
相关产品推荐

