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

Flutter中onFocusChange无法捕获TextField最新输入值问题求助

Flutter TextField失焦时无法获取最新输入值问题

背景

我正在开发一个基础任务管理应用,addTaskFormView里包含任务标题和描述两个TextField组件,每个TextField都单独封装在独立文件中,通过父组件addTaskFormView传入的回调函数来更新父组件中TextEditingController的状态。

问题

我希望用户点击屏幕其他区域离开TextField时能获取输入值,所以用Focus组件包裹了TextField,在onFocusChange回调里调用父组件传过来的方法。但现在点击提交按钮时,虽然调用了FocusScope.of(context).unfocus(),理论上应该触发onFocusChange,但实际却拿不到最新的输入值——第一次点击提交没输出,第二次点击才会输出上一次的内容,比如输入"first"点提交没反应,输入"second"点提交才输出"first"。

代码

addTaskFormView.dart

class addTaskFormView extends StatefulWidget {

  var taskIdentifier;

  final TextEditingController titleController = TextEditingController();
  final TextEditingController descController = TextEditingController();


  addTaskFormView({Key? key, this.taskIdentifier});

  @override
  State<addTaskFormView> createState() => _addTaskFormViewState();
}

class _addTaskFormViewState extends State<addTaskFormView> {

  String taskTitle = '';
  String description ='';

  var viewModelAddTask;

  @override
  void initState() {
    super.initState();

    viewModelAddTask = Provider.of<addTaskFormViewModel>(context, listen: false);
  }

  @override
  Widget build(BuildContext context) {

    setTitle(String? value) {
      print("CALL BACK HAS BEEN CALLED");
      setState(() {
        this.taskTitle = value!;
        widget.titleController.text = value;
      });
    }

    setDescription(String? value) {
      setState(() {
        this.description = value!;
        widget.descController.text = value;
      });
    }

    return GestureDetector(
      onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
      child: Scaffold(

        appBar: AppBar(
          title: const Text('Add a new task'),
        ),

        body: Column(
            children:[
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Column(
                    children: [
                      buildTaskTitle(
                          callBackFunction: setTitle,
                          titleController: widget.titleController,
                      ),
                      buildTaskDesc(
                          callBackFunction: setDescription,
                          descController: widget.descController
                      ),
                      ElevatedButton(onPressed: (){
                        //submit data to hive
                        FocusScope.of(context).unfocus();
                        print("task title: $taskTitle");
                        print("task title: $description");

                      }, child:  const Text('Submit'))
                    ]
                ),

              ),
            ]
          ),
        ),
    );
  }

}

buildTaskDesc.dart

class buildTaskDesc extends StatefulWidget {
  const buildTaskDesc({Key? key, required this.callBackFunction, required this.descController});

  final Function callBackFunction;
  final TextEditingController descController;

  @override
  State<buildTaskDesc> createState() => _buildTaskDescState();
}

class _buildTaskDescState extends State<buildTaskDesc> {

  String description = '';

  @override
  Widget build(BuildContext context) {
    return Focus(
      canRequestFocus: false,
      onFocusChange: (hasFocus) {
        hasFocus ? print('title has focus') : widget.callBackFunction(widget.descController.text);
      },

      child: TextField(

        key: const Key('task_description'),

        controller: widget.descController,

        decoration: InputDecoration(
          labelText: 'Description',
          hintText: 'Enter task description',
          border: const OutlineInputBorder(),

          suffixIcon: widget.descController.text.isEmpty
              ? Container(width: 0)
              : IconButton(
            icon: const Icon(Icons.close),
            onPressed: () => widget.descController.clear(),

          ),
        ),

        style: const TextStyle(fontSize: 14),
        maxLines: 15,
        minLines: 7,

        keyboardType: TextInputType.text,
        textInputAction: TextInputAction.done,
      ),
    );



  }
}

额外尝试

buildTaskTitle的逻辑和buildTaskDesc完全一致,这里就不贴代码了。我试过用GestureDetector包裹Scaffold,通过onTap调用unfocus;也试过在ElevatedButton里嵌套GestureDetector调用unfocus,但都没解决问题。

解决方案

问题根源

调用FocusScope.of(context).unfocus()后,onFocusChange的回调执行时机早于TextField将输入内容同步到TextEditingController的时机,所以此时拿到的还是旧值。

可行解决方式

  • 实时同步输入内容:在TextField中添加onChanged回调,每次输入变化时直接调用父组件的回调更新状态。修改buildTaskDesc中的TextField:

    TextField(
      // 原有代码不变
      onChanged: (value) {
        widget.callBackFunction(value);
      },
    )
    

    这样父组件能实时获取最新输入值,无需依赖失焦事件。

  • 延迟获取控制器值:如果必须依赖失焦事件,在onFocusChange中延迟执行回调,确保TextField完成内容同步:

    onFocusChange: (hasFocus) {
      if (!hasFocus) {
        Future.delayed(const Duration(milliseconds: 100), () {
          widget.callBackFunction(widget.descController.text);
        });
      }
    },
    
  • 监听控制器变化:在父组件的initState中给TextEditingController添加监听,直接同步最新值:

    @override
    void initState() {
      super.initState();
      viewModelAddTask = Provider.of<addTaskFormViewModel>(context, listen: false);
      
      widget.titleController.addListener(() {
        setState(() {
          taskTitle = widget.titleController.text;
        });
      });
      
      widget.descController.addListener(() {
        setState(() {
          description = widget.descController.text;
        });
      });
    }
    

    这种方式最可靠,提交时无需先失焦,直接打印控制器的text属性即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 05:55:06