Search code examples
flutterfirebaseandroid-studiodartflutter-layout

How to solve Unhandled Exception: setState() called after dispose():


I was trying to implement a loading bar after I click the register button on the Registration page and then I got this error I cannot figure out a way to show loading bar after the button is clicked

error:

 [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: setState() called after dispose(): _AdminRegisterState#f3c8f(lifecycle state: defunct, not mounted)
E/flutter (29660): This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback.
E/flutter (29660): The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
E/flutter (29660): This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().
E/flutter (29660): #0      State.setState.<anonymous closure> (package:flutter/src/widgets/framework.dart:1073:9)
E/flutter (29660): #1      State.setState (package:flutter/src/widgets/framework.dart:1108:6)
E/flutter (29660): #2      _AdminRegisterState.addAndCreateUser (package:election/pages/Admin/AdminRegister.dart:94:5)
E/flutter (29660): <asynchronous suspension>
E/flutter (29660): #3      _AdminRegisterState.build.<anonymous closure> (package:election/pages/Admin/AdminRegister.dart:215:31)
E/flutter (29660): <asynchronous suspension>
E/flutter (29660):  

AdminRegister.dart:

import 'dart:math';

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:election/pages/Admin/AdminHome.dart';
import 'package:election/services/Validator.dart';
import 'package:flutter/material.dart';
import 'package:election/services/Auth.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:loader_overlay/loader_overlay.dart';

class AdminRegister extends StatefulWidget {
  const AdminRegister({Key? key}) : super(key: key);

  @override
  State<AdminRegister> createState() => _AdminRegisterState();
}

class _AdminRegisterState extends State<AdminRegister> {
  String? get _errorText {
    // at any time, we can get the text from _controller.value.text
    final text = _controllerpassword.value.text;
    //  final email = _controlleremail.value.text;
    //  final name = _controllerName.value.text;
    //  final repass = _controllerrepassword.value.text;
    //  final phone = _controllerphone.value.text;

    if (text.isEmpty) {
      return 'Can\'t be empty';
    }
    if (text.length < 8) {
      return 'atleast 8 characters requierd';
    }
    // return null if the text is valid
    return null;
  }

  String? errormessage = '';
  String? errorAddUser = '';

  late String Name;
  late String Email;
  late String Phone;
  late String Password;
  late String Admin_Key;

  bool _istrue = true;
  bool _isloading = false;

  final TextEditingController _controllerName = TextEditingController();
  final TextEditingController _controlleremail = TextEditingController();
  final TextEditingController _controllerphone = TextEditingController();
  final TextEditingController _controllerpassword = TextEditingController();
  final TextEditingController _controllerrepassword = TextEditingController();
  final TextEditingController _controllerAdminKey = TextEditingController();

//create user metheod using firebase auth
  Future<void> createUserWithEmailAndPassword() async {
    setState(() { _isloading = true; });
    try {
      await Auth().createUserwithEmailAndPassword(email: _controlleremail.text, password: _controllerpassword.text);
      if(mounted){
        Navigator.pushAndRemoveUntil(context, MaterialPageRoute(builder: (context) =>  AdminHome()),(route) => false);
      }else{return;}
    } on FirebaseAuthException catch (e) {
      setState(() {
        errormessage = e.message;
      });
    }
    setState(() { _isloading = false; });
  }

  //cloud firestore using firestore
  final CollectionReference Admins = FirebaseFirestore.instance.collection('Admins');

  Future<void>addUser()async{
    Name = _controllerName.text;
    Email=_controlleremail.text;
    Password=_controllerpassword.text;
    Phone=_controllerphone.text;
    Admin_Key=_controllerAdminKey.text;
    try{
      await Admins.doc(Email).set({
        "Name":Name,"email":Email,"password":Password,"phone":Phone,"Admin":_istrue,"private_key":Admin_Key});
      print('user added successfullyyyyyyy');
    }catch(err){

    }
  }

  Future<void>addAndCreateUser()async{
    setState(() { _isloading = true; });
    await createUserWithEmailAndPassword();
    await addUser();
    setState(() { _isloading = false; });
  }

  Widget _errorMessage() {
    return Text(errormessage == '' ? '' : 'Humm $errormessage');
  }

  @override
  Widget build(BuildContext context) {
    if(_isloading){
      return const Scaffold(
        backgroundColor: Colors.cyan,
        body: Center(
          child: CircularProgressIndicator(),
        ),
      );
    }else{
      return Scaffold(
        appBar: AppBar(
          title: const Text(
            "Register as Admin",
            style: TextStyle(
                color: Colors.cyan, fontWeight: FontWeight.bold, fontSize: 24),
          ),
        ),
        body: Container(
          margin: const EdgeInsets.all(16),
          color: Colors.cyan,
          child: Center(
            child: SingleChildScrollView(
              child: Container(
                padding: EdgeInsets.all(8),
                child: Column(
                  children: [
                    const SizedBox(
                      height: 24,
                    ),
                    Container(
                      padding: EdgeInsets.all(16),
                      child: TextField(
                          controller: _controllerName,
                          decoration: const InputDecoration(
                              hintText: 'Name',
                              border: OutlineInputBorder(
                                  borderRadius:
                                  BorderRadius.all(Radius.circular(8))))),
                    ),
                    const SizedBox(
                      height: 4,
                    ),
                    Container(
                      padding: EdgeInsets.all(16),
                      child: TextField(
                          controller: _controlleremail,
                          decoration: const InputDecoration(
                              hintText: 'email id',
                              border: OutlineInputBorder(
                                  borderRadius:
                                  BorderRadius.all(Radius.circular(8))))),
                    ),
                    const SizedBox(
                      height: 4,
                    ),
                    Container(
                      padding: EdgeInsets.all(16),
                      child: TextField(
                          controller: _controllerphone,
                          keyboardType: TextInputType.number,
                          decoration: const InputDecoration(
                              hintText: 'phone number',
                              border: OutlineInputBorder(
                                  borderRadius:
                                  BorderRadius.all(Radius.circular(8))))),
                    ),
                    const SizedBox(
                      height: 4,
                    ),
                    Container(
                      padding: EdgeInsets.all(16),
                      child: TextField(
                          controller: _controllerpassword,
                          decoration: const InputDecoration(
                              hintText: 'password',
                              border: OutlineInputBorder(
                                  borderRadius:
                                  BorderRadius.all(Radius.circular(8))))),
                    ),
                    const SizedBox(
                      height: 4,
                    ),
                    Container(
                      padding: EdgeInsets.all(16),
                      child: TextField(
                          controller: _controllerrepassword,
                          decoration: const InputDecoration(
                              hintText: 'Re enter password',
                              border: OutlineInputBorder(
                                  borderRadius:
                                  BorderRadius.all(Radius.circular(8))))),
                    ),
                    const SizedBox(
                      height: 4,
                    ),
                    Container(
                      padding: const EdgeInsets.all(16),
                      child: TextField(
                          controller: _controllerAdminKey,
                          decoration: const InputDecoration(
                              hintText: 'Metamask Private key',
                              border: OutlineInputBorder(
                                  borderRadius:
                                  BorderRadius.all(Radius.circular(8))))),
                    ),
                    const SizedBox(
                      height: 24,
                    ),
                    ElevatedButton(
                      onPressed: () async {
                        if (_controllerpassword.text.isNotEmpty && _controlleremail.text.isNotEmpty) {
                          if(_controllerAdminKey.text.isNotEmpty&&_controllerName.text.isNotEmpty){
                            if(_controllerphone.text.isNotEmpty&&_controllerpassword.text==_controllerrepassword.text){
                              await addAndCreateUser();
                            }
                          }
                        }
                      },
                      style: ElevatedButton.styleFrom(primary: Colors.white),
                      child: const Text(
                        'Register as Admin',
                        style: TextStyle(color: Colors.cyan),
                      ),
                    )
                  ],
                ),
              ),
            ),
          ),
        ),
      );

    }
  }
}

The loader is coded at the widget build method and the set state is used in the addAndCreateUser() function


Solution

  • In your code, the issue is that _isloading = false is being called even after you are redirected to AdminHome. When the account is created Navigator.pushAndRemoveUntil is called and the user is redirected to AdminHome. Because you have not set mounted check on setState when setting _isLoading = false and the screen is disposed, you are getting this exception. Your code should look like this:

    Future<void> createUserWithEmailAndPassword() async {
        ...
        ...
        ...
        if (mounted) {
          setState(() { _isloading = false; });
        }
      }
    

    P.S I would suggest setting _isLoading to false when the account is created.