Flutter求助:TextEditingController释放后仍被使用错误排查
问题分析与解决方案
错误信息
A texteditingcontroller was used after being disposed. Once you have called dispose() on aTextEditingController, it can no longer be used.
首次进入页面无报错,但返回后再次进入同一屏幕时触发该错误。
核心原因
你把TextEditingController和GlobalKey<FormState>定义在了StatefulWidget类的外部,变成了全局变量。当页面被销毁(调用dispose())时,这些控制器会被释放,但再次进入页面时,新创建的State会复用这个已经被dispose的全局控制器,从而触发报错。
解决方案
将FormKey和所有TextEditingController移到对应页面的State类内部,确保每次进入页面时都会创建全新的控制器实例,避免复用已销毁的对象。
修改后的SignInScreen代码
import 'package:batch8_taskmanager_project/ui/screens/sign_up_screen.dart'; import 'package:batch8_taskmanager_project/ui/widgets/screen_background.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; class SignInScreen extends StatefulWidget { const SignInScreen({super.key}); static const String name = '/sign-in-screen'; @override State<SignInScreen> createState() => _SignInScreenState(); } class _SignInScreenState extends State<SignInScreen> { // 将FormKey和控制器移到State内部 final GlobalKey<FormState> _formKey = GlobalKey<FormState>(); final TextEditingController _emailTEController = TextEditingController(); final TextEditingController _passwordTEController = TextEditingController(); @override Widget build(BuildContext context) { final textStyle = Theme.of(context).textTheme; return Scaffold( resizeToAvoidBottomInset: false, body: ScreenBackground( child: Padding( padding: const EdgeInsets.all(16.0), child: Form( key: _formKey, child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ const SizedBox(height: 68), Text("Get Started With", style: textStyle.titleLarge), const SizedBox(height: 24), TextFormField( controller: _emailTEController, keyboardType: TextInputType.emailAddress, decoration: const InputDecoration(hintText: "Email Address"), ), const SizedBox(height: 24), TextFormField( controller: _passwordTEController, obscureText: true, decoration: const InputDecoration(hintText: "Password"), ), const SizedBox(height: 24), ElevatedButton(onPressed: () {}, child: const Icon(Icons.double_arrow)), const SizedBox(height: 48), TextButton(onPressed: () {}, child: const Text("Forgot Password?")), const SizedBox(height: 24), _buildSignIn() ], ), ), )), ); } Widget _buildSignIn() { return RichText( text: TextSpan( text: "Don't have an account? ", style: const TextStyle(color: Colors.grey), children: [ TextSpan( text: "Sign up", style: const TextStyle(color: Colors.green, fontWeight: FontWeight.bold), recognizer: TapGestureRecognizer()..onTap = () { Navigator.pushNamed(context, SignupScreen.name); } ) ], ), ); } @override void dispose() { _emailTEController.dispose(); _passwordTEController.dispose(); super.dispose(); } }
修改后的SignUpScreen代码
import 'package:batch8_taskmanager_project/ui/widgets/screen_background.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; class SignupScreen extends StatefulWidget { const SignupScreen({super.key}); static const String name = '/sign-up-screen'; @override State<SignupScreen> createState() => _SignupScreenState(); } class _SignupScreenState extends State<SignupScreen> { // 将FormKey和控制器移到State内部 final GlobalKey<FormState> _formKey = GlobalKey<FormState>(); final TextEditingController _emailTEController = TextEditingController(); final TextEditingController _firstNameTEController = TextEditingController(); final TextEditingController _lastNameTEController = TextEditingController(); final TextEditingController _mobileTEController = TextEditingController(); final TextEditingController _passwordTEController = TextEditingController(); @override Widget build(BuildContext context) { final textStyle = Theme.of(context).textTheme; return Scaffold( resizeToAvoidBottomInset: false, body: ScreenBackground( child: Padding( padding: const EdgeInsets.all(16.0), child: Form( key: _formKey, child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ const SizedBox(height: 68), Text("Join with Us", style: textStyle.titleLarge), const SizedBox(height: 24), TextFormField( controller: _emailTEController, keyboardType: TextInputType.emailAddress, decoration: const InputDecoration(hintText: "Email Address"), ), const SizedBox(height: 12), TextFormField( controller: _firstNameTEController, decoration: const InputDecoration(hintText: "First Name"), ), const SizedBox(height: 12), TextFormField( controller: _lastNameTEController, decoration: const InputDecoration(hintText: "Last Name"), ), const SizedBox(height: 12), TextFormField( controller: _mobileTEController, keyboardType: TextInputType.number, decoration: const InputDecoration(hintText: "Mobile"), ), const SizedBox(height: 12), TextFormField( controller: _passwordTEController, obscureText: true, decoration: const InputDecoration(hintText: "Password"), ), const SizedBox(height: 24), ElevatedButton(onPressed: () {}, child: const Icon(Icons.double_arrow)), const SizedBox(height: 48), _buildSignUp() ], ), ), )), ); } Widget _buildSignUp() { return RichText( text: TextSpan( text: "Have an account? ", style: const TextStyle(color: Colors.grey), children: [ TextSpan( text: "Sign in", style: const TextStyle(color: Colors.green, fontWeight: FontWeight.bold), recognizer: TapGestureRecognizer()..onTap = () { Navigator.pop(context); } ) ], ), ); } @override void dispose() { _emailTEController.dispose(); _firstNameTEController.dispose(); _lastNameTEController.dispose(); _mobileTEController.dispose(); _passwordTEController.dispose(); super.dispose(); } }
原理说明
当StatefulWidget的实例被创建时,对应的State类也会被实例化,此时内部定义的控制器会重新创建,避免复用已被dispose的全局实例。页面销毁时,dispose()方法释放当前State实例的控制器,再次进入页面时,新的State会创建全新的控制器,不会出现“使用已销毁控制器”的错误。
内容的提问来源于stack exchange,提问作者Jahangir Alam
相关产品推荐
相关产品推荐

