Flutter表单提交至Firestore时前两个TextFormField字段为空的问题求助
看起来你遇到的问题根源在于TextFormField的控制器使用方式不正确。让我帮你拆解一下:
问题原因
你在每个TextFormField的controller参数中直接实例化了TextEditingController(),而且没有保留这个控制器的引用。这种写法会导致一个关键问题:每当Widget发生重建(比如状态更新、屏幕旋转,甚至只是表单验证触发的重绘),这个控制器都会被重新创建,之前用户输入的文本会被清空。虽然你依赖onSaved来获取值,但如果控制器被重置,onSaved拿到的就是空值——这就是为什么前两个字段的数据无法正确提交到Firestore的原因。
解决方案
这里有两种可靠的修复方式,推荐第一种,因为它更简洁且符合表单的常规用法:
方案一:移除不必要的TextEditingController(推荐)
既然你已经通过onSaved回调来收集表单数据,完全不需要给每个TextFormField单独设置控制器。直接删除所有TextFormField中的controller: TextEditingController()这一行即可。
修改后的QuestionId字段示例:
TextFormField( keyboardType: TextInputType.text, decoration: const InputDecoration( labelText: 'QuestionId ? ', ), validator: (value) { if (value == null || value.isEmpty) { return 'QustionId Required'; } return null; }, onSaved: (value) { AddQuestionData['QuestionId'] = value!; }, ),
对QuestionText以及其他所有字段做同样的修改,这样表单会自动管理输入状态,onSaved就能正确获取到用户输入的真实值了。
方案二:正确管理控制器生命周期(如果需要使用控制器)
如果你确实需要使用控制器(比如要手动控制文本内容、监听输入变化等),就必须把控制器定义为State类的成员变量,并在生命周期方法中正确初始化和释放资源:
class _AddQuestionScreenState extends State<AddQuestionScreen> { DataController controller = Get.find(); final _formKey = GlobalKey<FormState>(); // 定义控制器成员变量 late TextEditingController _questionIdController; late TextEditingController _questionTextController; late TextEditingController _answerAController; late TextEditingController _answerBController; // ... 为其他所有字段添加对应的控制器变量 Map<String, dynamic> AddQuestionData = { "QuestionId": "", "QuestionText": "", "AnswerA": "", "AnswerB": "", "AnswerC": "", "AnswerD": "", "AskedIn": "", "SubjectName": "", "ChapterName": "", "CorrectAnswer": "", }; var _userImageFile; @override void initState() { super.initState(); // 初始化控制器 _questionIdController = TextEditingController(); _questionTextController = TextEditingController(); _answerAController = TextEditingController(); _answerBController = TextEditingController(); // ... 初始化其他控制器 } @override void dispose() { // 释放控制器资源,避免内存泄漏 _questionIdController.dispose(); _questionTextController.dispose(); _answerAController.dispose(); _answerBController.dispose(); // ... 释放其他控制器 super.dispose(); } // ... 其他方法保持不变 @override Widget build(BuildContext context) { return Scaffold( // ... 其他代码 TextFormField( controller: _questionIdController, // 使用成员变量控制器 keyboardType: TextInputType.text, decoration: const InputDecoration( labelText: 'QuestionId ? ', ), validator: (value) { if (value == null || value.isEmpty) { return 'QustionId Required'; } return null; }, onSaved: (value) { AddQuestionData['QuestionId'] = value!; }, ), // ... 其他TextFormField替换为对应的成员变量控制器 ); } }
为什么方案一更优?
因为你已经通过onSaved来收集表单数据,额外的控制器不仅多余,还会引入生命周期管理的复杂度。移除控制器后,代码更简洁,也能避免潜在的内存泄漏问题。
内容的提问来源于stack exchange,提问作者Ram Gupta

