I'm experiencing error A texteditingcontroller was used after being disposed. Once you have called dispose() on aTextEditingController, it can no longer be used.
I'm not experiencing this error when I'm Navigating the page first time but I'm experiencing this error when I'm returning and trying to go to same screen 2nd time.
Here is my 2 screen code including named rount code. I'll include full code git link as well at the end.
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();
}
GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final TextEditingController _emailTEController = TextEditingController();
final TextEditingController _passwordTEController = TextEditingController();
class _SignInScreenState extends State<SignInScreen> {
@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();
}
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();
class _SignupScreenState extends State<SignupScreen> {
@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();
}
}
Named Route Code
initialRoute: '/',
onGenerateRoute: (RouteSettings settings){
late Widget widget;
if(settings.name == SplashScreen.name){
widget = const SplashScreen();
}else if(settings.name == SignInScreen.name){
widget = const SignInScreen();
}else if(settings.name == SignupScreen.name){
widget = const SignupScreen();
}
return MaterialPageRoute(builder: (_) => widget);
},
https://github.com/jahangirsim/batch8_taskmanager_project.git
The error happens because you're disposing of the TextEditingController
inside the dispose
method, but the controller was initialized globally.
Consequently entering the page for the first time won't throw an error, but for the following times.
To fix this error you have to move the initialization inside the StatefulWidget.
class _SignInScreenState extends State<SignInScreen> {
GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final TextEditingController _emailTEController = TextEditingController();
final TextEditingController _passwordTEController = TextEditingController();
...
}
class _SignupScreenState extends State<SignupScreen> {
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();
...
}
With this change, each time the Widget is created, the TextEditingController
is initialized and disposed within the widget lifecycle.
You should also put the GlobalKey
inside the widget, if it is only used within the widget.