Flutter中多TextField用不同控制器却输入同步问题求助
问题解决:Flutter注册页面多个TextField内容同步显示
问题描述
开发Flutter注册页面时,已为email、password、name、confirmPassword四个TextField分配独立的TextEditingController,但输入email、password或confirmPassword时,这三个输入框内容会同步显示,仅name输入框正常工作。重新编写控制器代码无效,需解决该问题。
控制器代码
class SignUpController extends GetxController { static SignUpController get instance => Get.find(); final email = TextEditingController(); final password = TextEditingController(); final name = TextEditingController(); final confirmPassword = TextEditingController(); void registerUser(String email, String password, String name) { AuthenticationRepository.instance.createUser(email, password, name); } @override void onClose() { email.dispose(); password.dispose(); name.dispose(); confirmPassword.dispose(); super.onClose(); } }
页面State代码
class _AskIfCompanyState extends State<AskIfCompany> { final FocusNode _focusNode = FocusNode(); final _formKey = GlobalKey<FormState>(); bool isJobSeeker = false; bool isCompany = false; @override void initState() { super.initState(); } @override void dispose() { _focusNode.dispose(); super.dispose(); } void _handleCheckBoxChange(bool? selected, String type) { setState(() { if (type == 'jobSeeker') { isJobSeeker = selected ?? false; if (isJobSeeker) { isCompany = false; } } else if (type == 'company') { isCompany = selected ?? false; if (isCompany) { isJobSeeker = false; } } }); } void _requestFocus() { FocusScope.of(context).requestFocus(_focusNode); } @override Widget build(BuildContext context) { final controller = Get.put(SignUpController()); var screenWidth = MediaQuery.of(context).size.width; var screenHeight = MediaQuery.of(context).size.height; return Scaffold( body: SafeArea( child: SingleChildScrollView( child: Column( children: [ Row( children: <Widget>[ buildLogo(screenWidth, screenHeight), ], ), const SizedBox(height: 25), Row( children: [ SizedBox(width: screenWidth * 0.15), const Text( 'Nom entier:', style: TextStyle( fontWeight: FontWeight.bold, fontSize: 20, ), ), ], ), const SizedBox(height: 5), SizedBox( child: buildTextField( context: context, controller: controller.name, hinttext: 'Entrez votre nom et prénom', ), ), const SizedBox(height: 5), Row( children: [ SizedBox(width: screenWidth * 0.15), const Text( 'Email:', style: TextStyle( fontWeight: FontWeight.bold, fontSize: 20, ), ), ], ), const SizedBox(height: 5), SizedBox( child: buildTextField( context: context, controller: controller.email, hinttext: 'Entrez votre mail', ), ), const SizedBox(height: 5), Row( children: [ SizedBox(width: screenWidth * 0.15), const Text( 'Mot de passe:', style: TextStyle( fontWeight: FontWeight.bold, fontSize: 20, ), ), ], ), const SizedBox(height: 5), SizedBox( child: buildTextFieldPassword( context: context, controller: controller.password, hinttext: 'Entrez votre mot de passe', ), ), const SizedBox(height: 5), Row( children: [ SizedBox(width: screenWidth * 0.15), const Text( 'Confirmez votre mot de passe:', style: TextStyle( fontWeight: FontWeight.bold, fontSize: 20, ), ), ], ), const SizedBox(height: 5), SizedBox( child: buildTextFieldPassword( controller: controller.confirmPassword, context: context, hinttext: 'Confirmez votre mot de passe', ), ), const SizedBox(height: 15), Row( children: [ SizedBox(width: screenWidth * 0.15), buildRoundCheckBox( isChecked: isJobSeeker, onChanged: (selected) => _handleCheckBoxChange(selected, 'jobSeeker'), borderColor: const Color(0xffffd5c2), checkedColor: const Color(0xffffd5c2), uncheckedColor: Colors.grey[200] ?? Colors.grey, checkedWidget: const Icon(Icons.check, color: Colors.black), size: 24, ), const SizedBox(width: 10), const Text( 'Je suis un candidat', style: TextStyle(fontSize: 15), ), ], ), const SizedBox(height: 20), Row( children: [ SizedBox(width: screenWidth * 0.15), buildRoundCheckBox( isChecked: isCompany, onChanged: (selected) => _handleCheckBoxChange(selected, 'company'), borderColor: const Color(0xffffd5c2), checkedColor: const Color(0xffffd5c2), uncheckedColor: Colors.grey[200] ?? Colors.grey, checkedWidget: const Icon(Icons.check, color: Colors.black), size: 24, ), const SizedBox(width: 10), const Text( 'Je suis une entreprise (HR)', style: TextStyle(fontSize: 15), ), ], ), const SizedBox(height: 20), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ buildElevatedButton( buttonText: 'S\"inscrire', onPressed: () { if (_formKey.currentState!.validate()) { SignUpController.instance.registerUser( controller.email.text.trim(), controller.password.text.trim(), controller.name.text.trim(), ); } }, ), ], ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ GestureDetector( onTap: () { Navigator.push( context, MaterialPageRoute(builder: (context) => const LogIn()), ); }, child: const Text( 'Déja un compte? Se connecter', style: TextStyle( color: Colors.black, fontSize: 12, fontWeight: FontWeight.w400, decoration: TextDecoration.underline, height: 0, ), ), ), ], ) ], ), ), ), ); } }
问题原因
控制器定义本身无问题,核心问题出在自定义的buildTextField和buildTextFieldPassword方法实现:
- 方法未正确使用传入的
controller参数,硬编码复用了同一个TextEditingController实例; - 或方法内部逻辑错误,导致多个TextField绑定到同一控制器。
另外,在build方法中调用Get.put(SignUpController())虽不会重复创建实例(GetX维护单例),但移至initState更规范,避免不必要调用。
解决方案
1. 修复自定义TextField构建方法
确保方法正确接收并使用传入的controller参数,示例实现如下:
Widget buildTextField({ required BuildContext context, required TextEditingController controller, required String hinttext, }) { return Padding( padding: EdgeInsets.symmetric(horizontal: MediaQuery.of(context).size.width * 0.15), child: TextField( controller: controller, // 必须使用传入的控制器 decoration: InputDecoration( hintText: hinttext, border: OutlineInputBorder(), ), ), ); } Widget buildTextFieldPassword({ required BuildContext context, required TextEditingController controller, required String hinttext, }) { return Padding( padding: EdgeInsets.symmetric(horizontal: MediaQuery.of(context).size.width * 0.15), child: TextField( controller: controller, // 必须使用传入的控制器 obscureText: true, decoration: InputDecoration( hintText: hinttext, border: OutlineInputBorder(), ), ), ); }
2. 规范控制器初始化位置
将Get.put(SignUpController())从build方法移至initState:
class _AskIfCompanyState extends State<AskIfCompany> { final FocusNode _focusNode = FocusNode(); final _formKey = GlobalKey<FormState>(); late SignUpController controller; bool isJobSeeker = false; bool isCompany = false; @override void initState() { super.initState(); controller = Get.put(SignUpController()); // 在此处初始化控制器 } // 其余代码不变 }
3. 验证控制器唯一性
可在控制器构造函数添加日志,确认是否重复创建:
class SignUpController extends GetxController { static SignUpController get instance => Get.find(); final email = TextEditingController(); final password = TextEditingController(); final name = TextEditingController(); final confirmPassword = TextEditingController(); SignUpController() { print("SignUpController created"); // 添加日志 } // 其余代码不变 }
若日志多次打印,需检查是否在其他位置也调用了Get.put(SignUpController())。
总结
多个TextField内容同步的核心原因是绑定了同一控制器实例,重点排查自定义构建方法的参数使用,同时规范控制器初始化位置即可解决问题。
内容的提问来源于stack exchange,提问作者Marlier Thibaud
相关产品推荐
相关产品推荐

