Search code examples
flutterflutter-providerflutter-state

Flutter provider and widget lifecycle


I have a simple flow:

  • a UserModel implementing ChangeProvider which wraps the state of the user (if it is logged in and utilities to log him in/out). In particular logout looks like:

    void logout() {
       user = null;
       notifyListeners();
    }
    
  • a UserPage widget with (among others):

    class UserPage extends StatelessWidget {
         @override
         Widget build(BuildContext context) {
             // ... adding only relevant code
    
             // a text with the user first letter of the email
             Text(context.watch<UserModel>().user.email[0])
    
             // a logout button with the following onPressed method
             TextButton( \\ ...
                 onPressed: () { 
                   context.read<UserModel>().logout();  
                   Navigator.pop(context); 
                 }
             )
         }
    }
    

I was expecting that pressing logout and popping the UserPage widget will not let flutter rebuild it. However it is not the case and the notifyListeners() in logout method makes flutter rebuild the widget and trigger a NullPointerException (since the user is null and the email can't be accessed).

I could deal with it (checking if the user object is != null but I would like to understand why this happens).

Is it correct to assume pop destroys the widget? If not, how should I handle this case? When a user is logged out I don't want to have in memory this widget nor deal with its existence. I would expect to create a UserPage when a user logs in and destroy it after its logout


Solution

  • When you call logout , watch in this line Text(context.watch<UserModel>().user.email[0]) will cause the widget it's in to rebuild.

    You need to call logout in the call back of push the one you called to push this page. pop can also send values to inform about the conditions like success or failure.

    So it would be something like this:

    Navigator.pushNamed(context, some_route).then(value) {
    context.read<UserModel>().logout();  
    }
    

    value in the call back can be returned from pop like so Navigator.of(context).pop(true);

    This will ensure that logout is only called after the widget has been popped.

    If you don't like callbacks you can use async await pattern.