Search code examples
flutter

A texteditingcontroller was used after being disposed. Once you have called dispose() on aTextEditingController, it can no longer be used


enter image description hereI'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


Solution

  • 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.