Provider: Unhandled Exception: A class was used after being disposed - Flutter

Created on 19 Aug 2020  路  6Comments  路  Source: rrousselGit/provider

I'm getting this error

E/flutter ( 9610): [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: A CustRegViewModel was used after being disposed.
E/flutter ( 9610): Once you have called dispose() on a CustRegViewModel, it can no longer be used.

I have a View named CustRegView where I take a phone number form the user and send it to ViewModel named CustRegViewModel to authenticate which is supposed to return true or false base on its authentication. I am not disposing CustRegViewModel anywhere.

class CustRegView extends StatefulWidget {
  @override
  _CustRegViewState createState() => _CustRegViewState();
}

class CustRegView extends StatelessWidget{
  final TextEditingController _controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    final deviceSize = MediaQuery.of(context).size;
    return BaseView<CustRegViewModel>( 
          builder: (context, model, child) => Scaffold(

    ...<some code>

    FlatButton (
        onPressed: () async {
            var registerSuccess = await model.register( _controller.text, context);   
              //    ^^^^^^^^^^^ HERE I AM GETTING AN ERROR IN ABOVE LINE ^^^^^^^^^^^ 
            if (registerSuccess) {
                Navigator.pushNamed(context, 'newScreen');
            } else {
                UIHelper().showErrorButtomSheet(context, model.errorMessage);
            }
    )
}

CustRegViewModel looks like this

class CustRegViewModel extends BaseViewModel {

    final AuthService _authService = locator<AuthService>();
    final DialogService _dialogService = locator<DialogService>();
    dynamic newUserResult; 
    dynamic verifyResult;

    Future<bool> register(String phoneNo, BuildContext context) async {
        await verifyPhone;
        return    verifyResult ? true :  false;  // From here it returns true
    }

    Future<void> verifyPhone(phoneNo) async {

            await FirebaseAuth.instance.verifyPhoneNumber(
                    phoneNumber: updatedPhoneNo,
                    timeout: Duration(seconds: 50),
                    verificationCompleted: (AuthCredential authCred) async {...... <some code>
                    verificationFailed: (AuthException authException) {...... <some code>
                    codeSent: (String verID, [int forceCodeResend]) async {...... <some code>
                    codeAutoRetrievalTimeout: (String verID) {...
             ).catchError((error) {...... <some code>
    }
}

BaseViewlooks like this

class BaseView<T extends BaseViewModel> extends StatefulWidget {
  final Widget Function(BuildContext context, T model, Widget child) builder;
  final Function(T) onModelReady;
  BaseView({this.builder, this.onModelReady});
  @override
  _BaseViewState<T> createState() => _BaseViewState<T>();
}

class _BaseViewState<T extends BaseViewModel> extends State<BaseView<T>> {
  T model = locator<T>();

  @override
  void initState() {
    if (widget.onModelReady != null) {
      widget.onModelReady(model);
    }
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<T>(
      create: (context) => model,
      child: Consumer<T>(builder: widget.builder),
    );
  }
}

BaseViewModellooks like this

class BaseViewModel extends ChangeNotifier {
  ViewState _state = ViewState.Idle;


  ViewState get state => _state;

  void setState(ViewState viewState) {
    _state = viewState;
    notifyListeners();
  }
}
bug

All 6 comments

As the documentation of ChangeNotifier provider says, do not use the default constructor if you are reusing an existing ChangeNotifier

See https://pub.dev/documentation/provider/latest/provider/ChangeNotifierProvider-class.html

As the documentation of ChangeNotifier provider says, do not use the default constructor if you are reusing an existing ChangeNotifier

See https://pub.dev/documentation/provider/latest/provider/ChangeNotifierProvider-class.html

Thanks for the response. Where I'm using the default constructor? I read that document and still don't know what I am doing wrong in my code.

ChangeNotifierProvider should be ChangeNotifierProvider.value

ChangeNotifierProvider should be ChangeNotifierProvider.value

After changing this to .value, I'm getting below error

E/flutter (16830): [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: Looking up a deactivated widget's ancestor is unsafe.
E/flutter (16830): At this point the state of the widget's element tree is no longer stable.
E/flutter (16830): To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method. 

On mentioned part here, in the above code

    FlatButton (
        onPressed: () async {
            var registerSuccess = await model.register( _controller.text, context);   

            if (registerSuccess) {
                Navigator.pushNamed(context, 'newScreen');   // <------ E R R O R    H E R E 
            } else {
                UIHelper().showErrorButtomSheet(context, model.errorMessage);
            }
    )

Why is that?

It sounds like a completely different issue.

My guess is that you are calling context.read inside dispose

It sounds like a completely different issue.

My guess is that you are calling context.read inside dispose

Yes, It was indeed different. That was my childish mistake. I was trying to push from a context which is popped before the push() method call. Thanks for your response

Was this page helpful?
0 / 5 - 0 ratings