← Back to list

Introduction to Flutter State Management with Providers.

If you are familiar with flutter, which you should be if you are reading this blog, you must have come across situations where the state of…

Dipankar Raj Upadhyaya · 2024-08-28 14:32 · 0 claps · 12.7 min read
#flutter #app-development #provider #flutter-state-management #flutter-provider
Open on Medium ↗
Wiki topics: BIZ · Business Strategy 📱 · Mobile Development 📚 · Books & Reading

Introduction to Flutter State Management with Providers.

If you are familiar with flutter, which you should be if you are reading this blog, you must have come across situations where the state of your UI should be updated and changed. For that there is setState() right? Yes setState()can be used but what if the state change is to be done in multiple widgets? We must know that setState() is used to update the state within a single widget.

Okay, if you have no any idea about what rubbish I am talking about with setState() and you have no idea about what actually setState() is, don’t worry. Let’s know what state management means and its use cases with a simple example. So HERE WE GO:

Let us begin with creating a simple Flutter Project. Thinking that you are reading this blog until here, you must already have flutter and dart up and running in your system, I will waste no any time to lengthen this blog guiding you in installing flutter. Let’s get straight to main.dart file which is inside the lib directory.

import 'package:basics_2/home.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Home(),
    );
  }
}

Okay, this code is self explainatory I guess. I have just created an app and named it MyAppand thus returned a home page which is defined by the class Home . Basically, all what the code does is it initializes the app and displays the Homewidget when the app starts.

Now, I would create a simple widget in home.dart file. This is how it looks


import 'package:flutter/material.dart';

class Home extends StatelessWidget {
  const Home({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: () {},
        child:  Icon(Icons.add),
      ),
      body:  Padding(
        padding: EdgeInsets.all(20),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Padding(
              padding: EdgeInsets.only(top: 60),
              child: Center(
                child: Text(
                  'Entered data is: 0',
                  style: TextStyle(
                    fontWeight: FontWeight.bold,
                    fontSize: 20,
                  ),
                ),
              ),
            ),
            SizedBox(height: 20),
          ],
        ),
      ),
    );
  }
}

The above code just gives me the boilerplate design to what my homepage looks like. Until now, no any widgets have any states to worry about and they are all simple to understand. Okay, the above code gives me the design as follows:

Make a note that in floatingActionButton , the onPressed has a callback which is yet to be defined and we will be doing it shortly after.

Well well, it seems pretty good up until now. The floating action button is placed perfectly, and the text is also there where it is intended.

Okay now let me tell you what I want to do. Whenever I press the button ‘+’ I would want to display the numbers 1 2 3 and so on. The latest number is what will be displayed on side of the text “Entered data is: ”. That seems a lot to do. So let’s start doing it.

First, let me think what all I need. Well certainly I will need a list to store the numbers to display whenever the button is pressed. And to display the list of numbers, I am using the flutter’s widget listview. So, let’s write some more code. Start by defining a list as a Home class instance as:

class Home extends StatelessWidget {
List<int> numbers = [];
...

Now, just below the SizedBox where we left earlier, we create a listview to display the contents of the numbers list. This is how it is done:

...
ListView.builder(
              itemCount: numbers.length,
              itemBuilder: (context, index) {
                return Text(numbers[index].toString());
              },
),
...

ListView takes in two parameters: itemCount where we specify the length of items in the listview and itemBuilder where we define the widget structure. In our case, we will simply display the text which is the current index of the list and that is what is done here.

Okay now, we write the main logic. All we want to do is whenever the floating action button is pressed, we want to append a number just greater than the number at last index of the list. Suppose numbers = [1,2,3] then whenever we press the floating action button, numbers = [1,2,3,4] and the text at top should be “Entered data is: 4”.

There are few things which we should consider here.

First, listView when used inside a column, we must wrap listView with Expanded. So, we make necessary changes

...
Expanded(
              child: ListView.builder(
                itemCount: numbers.length,
                itemBuilder: (context, index) {
                  return Text(numbers[index].toString());
                },
              ),
            )
...

Second, whenever we add a number to the list, our page’s state would change. The content of “Entered data is: ” will be constantly changing. So, what we do is make our HomePage a stateful widget. To do that, hover your mouse near Home and press on the bulb. You will get an option to convert into StatefulWidget

Okay now most of the things are sorted until now. Since whenever we add a new number, our whole page must refresh, and the UI should get updated accordingly. What I mean to say is, after each presses of button, the list should update and so should our UI. Thus to refresh our page, we call the function setState() inside the callback of floatingActionButton and write necessary code inside.

...
floatingActionButton: FloatingActionButton(
        onPressed: () {
          // add
          setState(() {
            final currentLastNumber = numbers.last;
            numbers.add(currentLastNumber + 1);
          });
        },
...

This code defines a FloatingActionButton that, when pressed, triggers the onPressed callback. Inside this callback, setState() is called to update the UI. The numbers list is modified by adding a new number that is one greater than the last element in the list, ensuring the UI reflects this change.

One thing which we now need to do is, the “Entered data is: ” should have the latest number inside it. I guess that was what we had discussed earlier.

So, let’s just modify the code asText('Entered data is: ${numbers.last})

Tadaaa. We achieved what we wanted. Okay now you know the basics of state management in flutter with setState. But, what if I have a button at the bottom of my application on pressing which I end up in the new page. In the new page, I want to perform the same operation which I did here with the same list of numbers. And the list of numbers should be same for both pages.

Okay, sounds challenging. But as we have already known the working of state management, this thing should not be a problem. Let us start by adding a button to go to a newer page. Also, why not give out home page a title? Let’s do it.

To give a title, just add the appBar() inside of Scaffold as:

return Scaffold(
      appBar: AppBar(
        title: Text('Home Page'),
      ),
...

Now to create a Button, which on pressing would navigate us to another page, we write the following code below where Expanded ends:

...
ElevatedButton(
              onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(builder: (context) => NewPage()),
                );
              },
              child: const Text('Next Page'),

With this, whenever we press the button, we will route to NewPage which we need to create. Okay, we will create a new file lib/new_page.dart and create a Stateful Widget there with name NewPage Also, we need to import this class in our HomePage. Don’t forget to do that.

The code here will be pretty similar to the one from the HomePage. Also, as we are using the same list numbers why don’t we pass the same from the constructor while routing to NewPage from the HomePage. So, we have the numbers list which we get here from HomePage in NewPage via constructor passing.

import 'package:flutter/material.dart';

class NewPage extends StatefulWidget {
  final List<int> numbers;
  const NewPage({
    super.key,
    required this.numbers,
  });

  @override
  State<NewPage> createState() => _NewPageState();
}

class _NewPageState extends State<NewPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        leading: IconButton(
          onPressed: () {
            Navigator.pop(context);
          },
          icon: Icon(Icons.arrow_back),
        ),
        title: const Text('New Page'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // add
          setState(() {
            final currentLastNumber = widget.numbers.last;
            widget.numbers.add(currentLastNumber + 1);
          });
        },
        child: const Icon(Icons.add),
      ),
      body: Padding(
        padding: const EdgeInsets.all(20),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Center(
              child: Text(
                'Entered data is: ${widget.numbers.last}',
                style: const TextStyle(
                  fontWeight: FontWeight.bold,
                  fontSize: 20,
                ),
              ),
            ),
            const SizedBox(height: 20),
            Expanded(
              child: ListView.builder(
                itemCount: widget.numbers.length,
                itemBuilder: (context, index) {
                  return Text(widget.numbers[index].toString());
                },
              ),
            ),
          ],
        ),
      ),
    );
    ;
  }
}

This is simple code for what we have discussed. We have a Scaffold with a leading icon which allows you to go back to HomePage and accepts the numbers list from homepage itself. All other things are same as of HomePage. One thing to note is, whenever we want to access the content passed from a constructor to a Stateful Widget, we must start with the term widget. So, to access the numbers list, we are using widget.numbers. Here is how our NewPage looks:

Yes, and the same functionalities are there in this NewPage as well. Not only that, I pressed the button at HomePage thrice making it contain the contents as below:

And on pressing the NextPage button, I go to NewPage and I have my numbers list updated and having values [0,1,2,3]. Amazing, that is what I intended to do.

And when I add more values, NewPage allows me to do and also my numbers list get updated accordingly.

But, when I press the back button and go back to my HomePage, all what I did in NewPage is lost!!!

Oh no. I had my numbers list to contain [0,1,2,3,4,5] in NewPage and when I come back, I don’t get them. That is a problem which I have now. Imagine an e-commerse application that you are developing. You added some dress into the cart and yes it shows in the cart but when you come back, you can’t see the same. Won’t that cause a problem?

This is where setState has its drawback. setState can manage the state in only one widget and not in multiple widgets. So, flutter has many state management tools which is present. Like to tackle this, we can use state management tools like GetX, Riverpod, Bloc and the most simplest of them all Providers.

Seeesh! This blog was to tell about the usage of Providers, but wow we have come a long way and have not even started on why we need providers. But yes, if you have read this much, surely you would have understood the need of providers or any other state management tools. Aren’t I right?

Now, let’s understand what Providers is and how it actually solves our problem.

Provider is one of the most popular state management solutions in Flutter, widely used for its simplicity, efficiency, and integration with Flutter’s widget tree. It leverages InheritedWidget and ChangeNotifier to efficiently propagate changes in state across the widget tree without unnecessary rebuilds.

Key Concepts of Provider:

  • ChangeNotifier is a class that provides change notifications to its listeners. It’s a core class in the Provider package and is used to notify widgets when the state changes.
  • You create a ChangeNotifier class for your state and call notifyListeners() whenever you want to trigger a UI update.

This is what ChatGPT gave. Okay no worries, let’s just go into our own app and resolve the problem which we are facing. First, let us add the dependancy of providers into our project. The simplest way to do is, open the terminal in the directory where your project is and just write flutter pub add provider

As ChatGPT said, we need to use ChangeNotifier. So, why not in a new file lib/numbers_provider.dart we create a class NumberProvider which extends ChangeNotifier. And in the class, we define our numbers list which would be common for both the pages. Also the adding functionality can be implemented in the same class inside a add function.

This is how the function looks

import 'package:flutter/material.dart';

class NumbersProvider extends ChangeNotifier {
  List<int> numbers = [0];

  void add() {
    numbers.add(numbers.last + 1);
    notifyListeners();
  }
}

We have used a function notifyListners() function which is provided by ChangeNotifier. This function notifies the widgets of the changes made.

We are using provider here. But does our application knows that? I don’t think our app has that much knowledge to understand that. So let’s make our app aware of usage of provider by wrapping our MaterialApp which is present in main.dart file with MultiProvider. In our case, we only have one provider. But imagine you developing a fully functional recipe app or a fitness tracker app, how many providers would you need? So, keeping that in mind, I am using MultiProvider and add NumbersProvider which is of type ChangeNotifierProvider in it. Update the main.dart code as below

...
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (context) => NumbersProvider())
      ],
      child: const MaterialApp(
        home: Home(),
      ),
    );
  }
}

Now, most of our work over. All we need to do is, in HomePage and NewPage, consume the contents the provider has provided us with.

All what we will do is, wrap the Scaffold with Consumer of NumbersProvider and use the method which we had written earlier in NumbersProvider.

import 'package:basics_2/new_page.dart';
import 'package:basics_2/numbers_provider.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class Home extends StatefulWidget {
  const Home({super.key});

  @override
  State<Home> createState() => _HomeState();
}

class _HomeState extends State<Home> {
  @override
  Widget build(BuildContext context) {
    return Consumer<NumbersProvider>(
      builder: (context, value, child) => Scaffold(
        appBar: AppBar(
          title: const Text('Home Page'),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            // add
            setState(() {
              value.add();
            });
          },
          child: const Icon(Icons.add),
        ),
        body: Padding(
          padding: const EdgeInsets.all(20),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              Center(
                child: Text(
                  'Entered data is: ${value.numbers.last}',
                  style: const TextStyle(
                    fontWeight: FontWeight.bold,
                    fontSize: 20,
                  ),
                ),
              ),
              const SizedBox(height: 20),
              Expanded(
                child: ListView.builder(
                  itemCount: value.numbers.length,
                  itemBuilder: (context, index) {
                    return Text(value.numbers[index].toString());
                  },
                ),
              ),
              ElevatedButton(
                onPressed: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(builder: (context) => NewPage()),
                  );
                },
                child: const Text('Next Page'),
              )
            ],
          ),
        ),
      ),
    );
  }
}

This is how our home_page.dart would look like after we use providers. There is nothing new, only we used Consumer provided by the provider package and used a builder which takes in three parameters: context, value and child which has a callback where we add the code written previously.

Because we have used the provider, there is no need to explicitly define the numbers list here. To get the numbers list, value field in the callback has access to numbers list which we defined in NumbersProvider class and we call that list using value.numbers. Same is the case with addition functionality which we have coded inside of add() method in NumbersProvider class and on pressing of floatingActionButton, call setState where we perform addition using value.add. Also make a note that since we are using providers, we need not pass the numbers as a constructor to the NewPage and this is reflected in the code already.

Okay now let’s make the similar changes to the new_page.dart as well.

We wrap with Consumer<NumbersProvider> and write the callback of builder field there. As no values are coming from the constructor, here as well we are utilizing value.number and value.add to access the properties and methods of NumbersProvider class (a simple OOPs concept).

Here is the code after making all the changes:

import 'package:basics_2/numbers_provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';

class NewPage extends StatefulWidget {
  const NewPage({super.key});

  @override
  State<NewPage> createState() => _NewPageState();
}

class _NewPageState extends State<NewPage> {
  @override
  Widget build(BuildContext context) {
    return Consumer<NumbersProvider>(
      builder: (context, value, child) => Scaffold(
        appBar: AppBar(
          leading: IconButton(
            onPressed: () {
              Navigator.pop(context);
            },
            icon: Icon(Icons.arrow_back),
          ),
          title: const Text('New Page'),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            // add
            setState(() {
              value.add();
            });
          },
          child: const Icon(Icons.add),
        ),
        body: Padding(
          padding: const EdgeInsets.all(20),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              Center(
                child: Text(
                  'Entered data is: ${value.numbers.last}',
                  style: const TextStyle(
                    fontWeight: FontWeight.bold,
                    fontSize: 20,
                  ),
                ),
              ),
              const SizedBox(height: 20),
              Expanded(
                child: ListView.builder(
                  itemCount: value.numbers.length,
                  itemBuilder: (context, index) {
                    return Text(value.numbers[index].toString());
                  },
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Shall we check if the problem that we faced is solved?

Okay here we go:

Here we begin with HomePage. Now let’s press the button 3 times:

Booyah. Okay, now shall we go the NewPage?

Yess. The numbers list is updated. But this was possible when we did it using setState as well. Our problem was after adding some values in this page and going back. Is it solved yet?

Okay let’s check. First add 2 more numbers.

Okay. Now shall we go back to HomePage? We will get to know if provider solved our problem then. Sure let’s go…..

Oh Yes. My numbers list is in updated form and is still there proper. Great. I loved it. Provider did its job.

Oh my god. This blog has been too long. My first tech blog and I don’t know how to wrap it formally. I just wrote this for fun and explained each and every part of the code maybe. Maybe I explained a lot more than I needed to. But no worries, if you my friends are unaware of flutter and dart too, and you have come this way, yes you may give this a try. And to be honest, this is very interesting because you see what you do. And also, flutter is so good to not like it. Maybe you who have come up until here must give it a try if you have not already.

This is all what I have about providers and this much is enough I guess. You can still practice and make your own creative projects as you have learnt how to use providers.

The source code for the application which we have made here is in this GitHub link

[embed]GitHub - dipankarupd/flutter-provider-eg: A basic understanding of how Provider work as a state… A basic understanding of how Provider work as a state management tool in flutter - dipankarupd/flutter-provider-eggithub.com


메타데이터
post_id
19f1da77b22c
slug
introduction-to-flutter-state-management-with-providers-19f1da77b22c
url
https://medium.com/@drupd17/introduction-to-flutter-state-management-with-providers-19f1da77b22c
canonical_url
https://medium.com/@drupd17/introduction-to-flutter-state-management-with-providers-19f1da77b22c
author_url
https://medium.com/@drupd17
status
ok
fetched_at
2026-07-23 00:27:58