In this tutorial, we will see how to implement a very simple app state management solution using Provider.

This is an example of a shopping app, which has a wishlist, a cart, and a user login state, three things that change often and the change needs to be reflected in a few places.

Before we begin, add this dependency in pubspec,yaml

provider: ^4.0.1

We will follow 4 simple steps

  1. Create the model class extending ChangeNotifier
  2. Make changes in data = Access the class members via Provider
  3. Create auto-refreshing views according to the data changes.
  4. Initialize and make the Provider accessible throughout the app

Create the model class

First, we need to create a model class for each of the models which need change management. We extend these models from ChangeNotifier class. This ChangeNotifier enables your class to use a method notifyListeners() which will emit a notification of change to all the Consumers of this class written by you.Let us create a simple user model that will hold the user login state.

class UserModel extends ChangeNotifier {
  
  //variable to hold the login state
  bool _isLoggedIn = false;
 
  //function to retrieve the above value
  bool isLoggedIn() => _isLoggedIn; 
  
  //constructor. check the login status on initialization
  UserModel() {
    checkLogin();
  }

  //function to check login
  checkLogin() async {
    
    //the below statement is calling a network service in other file of my code
    //that returns the boolean value indicating user login status. store the value in 
    //our variable.
    _isLoggedIn = await ApiService.loggedIn();

    //this line will send the change notification to all the places where we consume it.
    notifyListeners();
  }

  logout() async {
    
    //logout somehow
    await ApiService.logout();

    //change the variable value and notify listeners
    _isLoggedIn = false;
    notifyListeners();
  }

  login(String email, String pass) async {
    //login somehow
    await ApiService.login(email, pass);
    //change the variable value and notify listeners 
    _isLoggedIn = false;
    notifyListeners();
  }

}

Access the class variables and functions

To show an example of how to access the methods of the model class, Let’s create a login screen that will log the user in, using the login method in the model above. I am only showing the relevant code here, you will need to create a form with a submit button in the build method.

class LoginScreen extends StatelessWidget {

  _doLogin(String email, String pass, BuildContext context) async {

    //access the UserModel Provider
    var usermodel = Provider.of<UserModel>(context,listen: false);

    //call the login function
    usermodel.login(email, pass).then(() {
      //close the screen when done
      Navigator.pop(context);
    });
  }

  @override
  Widget build(BuildContext context) {
    //return your view here
  }
}

Create a view that will auto-refresh on model data change

Now let us access the data in a screen, and display a view according to the state.

We will use a Consumer Widget, which will allow us to use an instance of the model class.

Remember that this widget will be refreshed each time the data changes, which is each time the notifyListeners() method is called, so we should use this widget as deep in the tree as possible so that only the relevant portion of our screen is refreshed and not more than necessary.

class UserScreen extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: new Column(
        children: <Widget> [
          Consumer<UserModel> (
            builder: (context,model,widget) {
              if (model.isLoggedIn())
                return Text("You are logged in!");
              
              return Column(
                children: <Widget> [
                  Text("You are NOT logged in."),
                  FlatButton(
                    child: Text("LOGIN"),
                    onPressed: () {
                      //goToLoginPage
                    }
                  ),
                ],
              );
            }
          ),
        ],
      ),
    );
  }
}

This will cause this screen to refresh the login status of the user automatically when it changes.

Initialize and make the model available across the entire app.

Now, we need these providers to be accessible throughout our app. So we declare them in our main App class.

Make the App class a StatefulWidget and return a MultiProvider with all the classes that you need to be available globally.

class App extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return AppState();
  }
}

class AppState extends State<App> {

  @override
  Widget build(BuildContext context) {

    return MultiProvider(
      providers: [
        
        ChangeNotifierProvider(create: (context) => UserModel(),),
        //similar to above statement, declare other providers here if applicable
      ],

      child: MaterialApp(
        title: "My Shopping App",
        home: SplashScreen(),
      ),
    );
  }
}

Modify your main() method, to include the following line.

void main() async {
  Provider.debugCheckInvalidValueType = null;
  runApp(App());
}

That’s all for now! Although there’s a lot more to providers in flutter, this was a very basic tutorial for those who are beginners in this. I tried to make this simple and easily understandable for anybody, as I had struggled to figure this out for a while! Hope you enjoyed it.