Flutter交互回调中使用FutureBuilder时future方法被两次调用如何解决
问题原因
- 你在
LoadMoreWidget的build方法中重复调用了widget.loadMoreViewBuilder(context):一次是在水平/垂直拖动结束的回调中,一次是在Stack的子组件渲染时。每次调用该方法都会构造新的FutureBuilder,而你给future属性传的是updateData()(直接执行方法返回Future),所以每调用一次就会触发一次updateData执行,自然会调用两次。 - 你没有对
FutureBuilder的future做缓存,只要组件重绘就会生成新的Future实例,也会导致updateData重复执行。 - 核心逻辑设计错误:不应该通过强制转换Widget拿到Future执行加载逻辑,加载逻辑应该和UI组件解耦。
修复方案
步骤1:给主页状态加加载锁和Future缓存
修改_MyHomePageState代码,新增加载状态判断和Future缓存变量,避免重复触发加载:
class _MyHomePageState extends State<MyHomePage> { late List<SampleData> chartData; bool _isLoading = false; // 加载状态锁,避免重复触发 Future<List<SampleData>>? _updateFuture; // 缓存加载用的Future实例 @override void initState() { getChartData(); super.initState(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: LoadMoreWidget( data: chartData, loadMoreViewBuilder: onLoadMoreWidgetCalled, onLoadMore: triggerLoadMore // 传入加载触发方法 ), )); } Widget onLoadMoreWidgetCalled(BuildContext context) { return FutureBuilder( future: _updateFuture, // 绑定缓存的Future,不直接执行方法 builder: (context, snapshot) { if ((snapshot.hasData && !snapshot.hasError) || !_isLoading) { return const SizedBox.shrink(); } else { return getProgressIndicator(); } }); } // 抽离公共加载触发方法,统一控制加载状态 Future<void> triggerLoadMore() async { if (_isLoading) return; setState(() { _isLoading = true; _updateFuture = updateData(); }); await _updateFuture; setState(() { _isLoading = false; }); } // 其余原有方法(getChartData、getProgressIndicator、updateData)保持不变 }
步骤2:修改自定义Widget的逻辑,解耦加载逻辑和UI
给LoadMoreWidget新增加载触发回调参数,不要在拖动回调中强行操作Widget实例拿Future:
class LoadMoreWidget extends StatefulWidget { const LoadMoreWidget({ Key? key, required this.data, required this.loadMoreViewBuilder, required this.onLoadMore, // 新增加载触发回调参数 }) : super(key: key); final List<dynamic> data; final LoadMoreViewBuilderCallback loadMoreViewBuilder; final Future<void> Function() onLoadMore; // 回调类型定义 @override State<LoadMoreWidget> createState() => _LoadMoreWidgetState(); } class _LoadMoreWidgetState extends State<LoadMoreWidget> { @override Widget build(BuildContext context) { List<dynamic> data = widget.data; return LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { return GestureDetector( onHorizontalDragEnd: (DragEndDetails details) async { await widget.onLoadMore(); // 直接调用加载回调 setState(() {}); }, onVerticalDragEnd: (DragEndDetails details) async { await widget.onLoadMore(); // 直接调用加载回调 setState(() {}); }, child: Stack(children: [ ListView.builder( itemBuilder: (context, index) { return Card( child: Row( children: [ Text('x:${data[index].x}'), Text('y:${data[index].y}'), ], )); }, itemCount: widget.data.length, ), // 仅在这里调用一次构造加载UI,不再重复调用 widget.loadMoreViewBuilder(context) ])); }); } }
修改完成后,updateData方法仅会在拖动结束时触发一次,不会重复执行。
内容的提问来源于stack exchange,提问作者Kanagambigai Murugan
相关产品推荐
相关产品推荐

