Flutter FutureBuilder调用setState报错修复后TextFormField无法点击问题
问题根因与修复方案
存在的4个核心问题
- 按钮状态逻辑写反:需求是「请求有返回数据时禁用按钮」,你的代码在
snapshot.hasData时将_isButtonDisabled设为false,逻辑完全颠倒,这是按钮始终启用的直接原因。 - 触发无限重绘死循环:
FutureBuilder的builder方法会在组件每次重绘时执行,你在builder内每次都通过addPostFrameCallback插入setState操作,setState又会触发组件重绘,往复循环导致UI卡顿、交互无响应,这是TextFormField无法点击的核心原因。 - 数据展示逻辑错误:有返回数据时你清空了
_timeController,仅用hintText展示接口返回值,hintText是无输入内容时的提示文本,不符合正式数据展示的场景。 - 接口重复请求:你直接将
getTimes(selectedDate)作为future参数传入FutureBuilder,每次组件重绘都会重新调用接口,即使用户选中的日期没有变化也会重复请求。
修复方案
第一步:缓存接口请求Future
在你的State类中新增一个变量缓存请求,仅当选中的日期变化时才重新发起请求:
// 类属性定义 Future<double?>? _timesFuture; // DateTimePicker选中日期的回调方法中更新请求 void onSelectDate(DateTime pickedDate) { setState(() { selectedDate = pickedDate; // 仅日期变化时重新生成请求Future _timesFuture = getTimes(pickedDate); }); }
第二步:移除builder内的状态更新逻辑
不需要单独维护_isButtonDisabled变量,直接通过snapshot的状态同步控制按钮和输入框状态即可,避免在build过程中修改状态:
child: FutureBuilder<double?>( future: _timesFuture, builder: (BuildContext context, AsyncSnapshot<double> snapshot) { // 提前判断是否有有效返回数据 final hasValidData = snapshot.hasData && snapshot.data != null; // 有数据时直接给controller赋值,不要用hintText存业务数据 if (hasValidData) { _timeController.text = '${snapshot.data} h'; } else if(!snapshot.hasData && _timeController.text.endsWith(' h')) { // 无数据时清空之前的接口返回值 _timeController.clear(); } return TextFormField( controller: _timeController, textAlign: TextAlign.center, // 有数据时禁用输入框 enabled: !hasValidData, decoration: InputDecoration( hintText: "0 h", contentPadding: EdgeInsets.zero, filled: true, // 有数据时用浅色背景标识禁用状态 fillColor: hasValidData ? Colors.white70 : Colors.white, ), ); } )
第三步:按钮状态直接同步请求状态
按钮的onPressed参数直接根据snapshot状态控制,不需要额外维护布尔变量:
// 你的按钮控件写法示例 ElevatedButton( // 有返回数据时禁用按钮,无数据时启用,完全匹配需求 onPressed: snapshot.hasData && snapshot.data != null ? null : () { // 按钮点击逻辑 }, child: Text('提交'), )
如果你一定要保留_isButtonDisabled变量,只需要在onSelectDate发起请求、以及请求结束的回调里修改状态即可,不要在builder中操作。
内容的提问来源于stack exchange,提问作者JS1
相关产品推荐
相关产品推荐

