You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中获取到指定日期的数据库数据时如何正确禁用表单提交按钮

问题根因
  1. 你遇到的setState() or markNeedsBuild() called during build.报错,是因为FutureBuilder的builder函数执行时机正处于组件的build流程中,此时调用setState会触发新一轮build,相当于在构建过程中要求重新构建,违反Flutter的build规范。
  2. 你把setState封装到帧后回调后出现内容闪烁,是因为你没有缓存Future实例,每次build都会重新执行getTimes(selectedDate),新的Future触发builder更新,延迟执行的setState又触发新一轮build,形成死循环,就会出现内容反复切换的问题。

解决方案

推荐使用第一种方案,逻辑更简洁,无额外状态维护成本:

方案1:复用Future状态,无需单独维护按钮状态变量

无需额外声明_isButtonDisabled变量,直接通过Future的执行结果状态同时控制输入框和按钮的状态,步骤如下:

  1. 首先在State类中缓存getTimes的Future实例,不要在FutureBuilder的future参数中直接调用方法,避免每次build重复发起请求:
class _YourPageState extends State<YourPage> {
  DateTime? selectedDate;
  Future<double?>? _getTimesFuture;
  final _timeController = TextEditingController();

  // 日期选择器的选中回调,每次选完日期更新Future
  void _onDateSelected(DateTime date) {
    setState(() {
      selectedDate = date;
      // 选新日期时更新Future实例
      _getTimesFuture = getTimes(date);
    });
  }
}
  1. 用同一个Future的快照状态同时控制输入框和按钮:
@override
Widget build(BuildContext context) {
  return Column(
    children: [
      // 你的日期选择器组件,这里简化示意
      DatePicker(onChanged: _onDateSelected),
      FutureBuilder<double?>(
        future: _getTimesFuture,
        builder: (BuildContext context, AsyncSnapshot snapshot) {
          final hasSavedData = snapshot.hasData;
          return TextFormField(
            controller: _timeController,
            textAlign: TextAlign.center,
            enabled: !hasSavedData,
            decoration: InputDecoration(
              hintText: hasSavedData ? "${snapshot.data} h" : "0 h",
              contentPadding: EdgeInsets.zero,
              filled: true,
              fillColor: Colors.white
            ),
          );
        }
      ),
      // 提交按钮直接通过快照状态判断是否禁用
      FutureBuilder<double?>(
        future: _getTimesFuture,
        builder: (context, snapshot) {
          return ElevatedButton(
            onPressed: snapshot.hasData ? null : () async {
              postTimes(await convertDate(selectedDate!), convertTime(_timeController.text));
            },
            child: const Text("Submit")
          );
        }
      )
    ],
  );
}

你也可以把整个表单都包裹在同一个FutureBuilder内,避免重复写多次Future判断。


方案2:单独维护按钮状态变量

如果你必须保留_isButtonDisabled变量,就把状态修改逻辑移到Future的回调中,不要在builder内调用setState:

void _onDateSelected(DateTime date) {
  setState(() {
    selectedDate = date;
    // 选新日期时先重置状态
    _isButtonDisabled = false;
    _getTimesFuture = getTimes(date);
  });
  // 在Future完成回调中修改按钮状态
  _getTimesFuture?.then((value) {
    if (value != null && mounted) {
      setState(() {
        _isButtonDisabled = true;
        _timeController.text = "$value h";
      });
    }
  });
}

内容的提问来源于stack exchange,提问作者JS1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 16:06:08