Flutter嵌套FutureBuilder引发无限循环:原因解析与修复方案
Flutter页面无限循环的原因与修复方案
问题原因
- FutureBuilder的future参数在build中动态创建:你在内部
FutureBuilder的future字段里直接执行了() { ... return APIService.Get_Old_Model_No(id); }(),每次页面build(比如计时器触发setState、输入框onChanged触发setState)时,都会重新创建并执行这个Future,发起新的API请求。 - 计时器持续触发setState:启动计时器后,
Timer.periodic每秒调用setState更新时间,导致页面频繁重建,进而反复触发内部API请求,形成无限循环。
修复步骤
1. 缓存内部API请求的Future,避免每次build重复执行
在State类中添加一个Map来缓存每个id对应的Future,避免重复发起请求:
class _ScreenState extends State<Screen> { // ... 原有变量保留 Map<int, Future<Get_Old_Model_No>> _oldModelFutures = {}; // 用于缓存Future的Map // 初始化指定id对应的API请求Future void _initOldModelFuture(int id) { if (!_oldModelFutures.containsKey(id)) { _oldModelFutures[id] = APIService.Get_Old_Model_No(id); } } }
2. 修改内部FutureBuilder的future参数
不再在build方法中动态创建Future,而是使用提前缓存的Future实例:
// 替换内部的FutureBuilder代码 Expanded( child: FutureBuilder<Get_Old_Model_No>( future: _oldModelFutures[id], // 使用缓存的Future builder: (context, snapshot) { print("Snapshot data:${snapshot.data}"); print("Snapshot error:${snapshot.error}"); if (snapshot.hasData) { return Column( children: [ // Text(snapshot.data?.endDate), ], ); } else if (snapshot.hasError) { return Text("${snapshot.error}"); } return CircularProgressIndicator(); }, ), )
同时在外部FutureBuilder获取到数据后,提前初始化对应id的Future:
// 在外部FutureBuilder的snapshot.hasData分支中添加初始化逻辑 return Column( children: List.generate(snapshot.data!.length, (int index) { final model = snapshot.data![index]; // 替换为你实际获取id的逻辑(从model或路由参数中) final int id = ModalRoute.of(context)!.settings.arguments as Map<String,dynamic>['id'] ?? 0; _initOldModelFuture(id); // 提前缓存该id的API请求Future String Some_VariableName = model.name; return Column( children: [ Text(model.name), // ... 后续原有代码 ], ); }), );
3. 完善计时器的停止逻辑
确保点击停止按钮时能正确取消Timer,避免不必要的setState触发:
void _stopStopwatch() { _stopwatch.stop(); _timer.cancel(); setState(() { isStartButtonDisabled = false; isStopButtonDisabled = true; }); }
4. 优化输入框的状态更新逻辑
当前输入框onChanged每次输入都会触发setState,可以改用TextEditingController减少不必要的页面重建:
class _ScreenState extends State<Screen> { // ... 原有变量保留 final TextEditingController _numberController = TextEditingController(); @override void initState() { super.initState(); // 通过监听控制器更新变量 _numberController.addListener(() { final int? parsedValue = int.tryParse(_numberController.text); if (parsedValue != null) { setState(() { variable1 = parsedValue; }); } }); } @override void dispose() { _numberController.dispose(); // 销毁控制器 super.dispose(); } }
然后替换原有的TextFormField:
TextFormField( controller: _numberController, keyboardType: TextInputType.number, ),
内容的提问来源于stack exchange,提问作者A_K
相关产品推荐
相关产品推荐

