Flutter中setState刷新后TextEditingController数据丢失的解决方法
解决TextField在setState后丢失输入内容的问题
你的问题根源是在build方法中直接给TextEditingController的text属性赋值,每次调用setState触发Widget重建时,都会用userData的原始值覆盖用户输入的内容,导致输入丢失。
解决步骤:
- 将控制器的初始值设置逻辑移到
initState方法中,只在Widget初始化时执行一次 - 如果需要响应
userData的外部更新,可以通过didChangeDependencies方法处理,避免每次build都重置输入内容
修改后的完整代码:
import 'package:attend/constants.dart/enums.dart'; import 'package:attend/providers/user_data_provider.dart'; import 'package:attend/screens/faculty_screens/faculty_home_screen.dart'; import 'package:attend/screens/student_screens/screens/student_screen.dart'; import 'package:attend/widgets.dart/profile_image.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:flutter/cupertino.dart'; class ProfileDetailScreen extends StatefulWidget { static const routeName = '/profile-detail-screen'; const ProfileDetailScreen({super.key}); @override State<ProfileDetailScreen> createState() => _ProfileDetailScreenState(); } class _ProfileDetailScreenState extends State<ProfileDetailScreen> { final _formKey = GlobalKey<FormState>(); var selectedGender = 'Male'; final _nameCon = TextEditingController(); final _surnameCon = TextEditingController(); final _enrollmentCon = TextEditingController(); var isValid = false; @override void initState() { super.initState(); // 在初始化时设置控制器初始值,只执行一次 WidgetsBinding.instance.addPostFrameCallback((_) { final userData = Provider.of<UserDataProvider>(context, listen: false); _nameCon.text = userData.name ?? ''; _surnameCon.text = userData.surname ?? ''; if (userData.role == Roles.student) { _enrollmentCon.text = userData.enrollmentNo ?? ''; // 若有学号字段可补充此逻辑 } }); } @override void dispose() { _nameCon.dispose(); _surnameCon.dispose(); _enrollmentCon.dispose(); super.dispose(); } void saveData() async { isValid = _formKey.currentState!.validate(); if (!isValid) { return; } var gender = Genders.Other; if (selectedGender == Genders.Male.name) { gender = Genders.Male; } else if (selectedGender == Genders.Female.name) { gender = Genders.Female; } // 此处可添加保存数据到userData的逻辑 } @override Widget build(BuildContext context) { final userData = Provider.of<UserDataProvider>(context, listen: false); return Scaffold( appBar: AppBar( title: const Text('Profile details'), actions: [ IconButton( onPressed: saveData, icon: const Icon(Icons.save)) ], ), body: Padding( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), child: Form( key: _formKey, child: Column( children: [ ProfileImage(selectedGender: selectedGender), const SizedBox(height: 20), //! name textfield TextFormField( textCapitalization: TextCapitalization.sentences, decoration: InputDecoration( contentPadding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10), focusedBorder: OutlineInputBorder( borderSide: const BorderSide(color: Colors.grey), borderRadius: BorderRadius.circular(15), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(15), borderSide: const BorderSide(color: Colors.red), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(15), borderSide: const BorderSide(color: Colors.grey), ), filled: true, fillColor: Colors.grey.shade100, labelText: 'Enter your name', labelStyle: const TextStyle(color: Colors.black, fontSize: 15), ), validator: (value) { if (value!.isEmpty) { return 'Please enter name!'; } else if (value.length <= 2) { return 'Name at least 3 character long!'; } return null; }, controller: _nameCon, textInputAction: TextInputAction.next, ), const SizedBox(height: 20), //! surname textfield TextFormField( textCapitalization: TextCapitalization.sentences, decoration: InputDecoration( contentPadding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10), focusedBorder: OutlineInputBorder( borderSide: const BorderSide(color: Colors.grey), borderRadius: BorderRadius.circular(15), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(15), borderSide: const BorderSide(color: Colors.red), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(15), borderSide: const BorderSide(color: Colors.grey), ), filled: true, fillColor: Colors.grey.shade100, labelText: 'Enter your surname', labelStyle: const TextStyle(color: Colors.black, fontSize: 15), ), validator: (value) { if (value!.isEmpty) { return 'Please enter surname!'; } else if (value.length <= 2) { return 'Surname at least 3 character long!'; } return null; }, controller: _surnameCon, textInputAction: userData.role == Roles.student ? TextInputAction.next : TextInputAction.done, ), const SizedBox(height: 20), //! if student then he has to add enrollment no if (userData.role == Roles.student) TextFormField( textCapitalization: TextCapitalization.sentences, decoration: InputDecoration( contentPadding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10), focusedBorder: OutlineInputBorder( borderSide: const BorderSide(color: Colors.grey), borderRadius: BorderRadius.circular(15), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(15), borderSide: const BorderSide(color: Colors.red), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(15), borderSide: const BorderSide(color: Colors.grey), ), filled: true, fillColor: Colors.grey.shade100, labelText: 'Enter your enrollment no', labelStyle: const TextStyle(color: Colors.black, fontSize: 15), ), validator: (value) { if (value!.isEmpty) { return 'Please enter enrollment no!'; } else if (value.length <= 2) { return 'Please enter valid enrollment no!'; } return null; }, controller: _enrollmentCon, textInputAction: TextInputAction.done, ), const SizedBox(height: 20), //! selected gender Row( children: [ const Text( 'Gender ', style: TextStyle(fontSize: 17), ), const SizedBox(width: 40), DropdownButton<String>( dropdownColor: Colors.purple.shade50, value: selectedGender, onChanged: (newValue) { setState(() { selectedGender = newValue!; }); }, items: <String>['Male', 'Female', 'Others'] .map<DropdownMenuItem<String>>((String value) { return DropdownMenuItem<String>( value: value, child: Text( value, style: const TextStyle( fontWeight: FontWeight.normal, fontSize: 15), ), ); }).toList(), ), ], ), ], ), ), ), ); } }
额外说明:
- 使用
WidgetsBinding.instance.addPostFrameCallback是因为initState阶段无法直接获取Provider数据,需等待Widget挂载完成后再执行 - 若后续
userData有外部更新需要同步到输入框,可重写didChangeDependencies方法,添加判断逻辑避免重复覆盖用户输入
内容的提问来源于stack exchange,提问作者Smit Prajapati
相关产品推荐
相关产品推荐

