Flutter: State Management with Provider – A Step-by-Step Guide for Beginners Tutorial

State management is one of the most critical aspects of Flutter development, especially when building complex apps that require updating UI in response to user interactions or data changes. Provider is one of the most widely-used packages for handling state in Flutter, thanks to its simplicity and scalability. In this tutorial, we’ll walk you through what Provider is, how it works, and how to implement state management using it step-by-step.


What is State Management?

In Flutter, state refers to data that can change during the app’s lifecycle (e.g., user input, API data). Managing this state efficiently is key to building dynamic applications. State management tools help in keeping the UI in sync with the state changes.

Why Use Provider?

  • Provider is a wrapper around InheritedWidget, allowing efficient management of state throughout your app.
  • It helps separate business logic from UI, improving the app’s scalability and maintainability.
  • It works well with Flutter’s reactive architecture, updating UI elements automatically when the state changes.

Step 1: Set Up Your Flutter Project

First, you need to create a new Flutter project. If you haven’t set up Flutter yet, refer to the Flutter installation guide here.

  1. Open your terminal/command prompt and run:bashCopy codeflutter create provider_example
  2. Navigate to the project directory:bashCopy codecd provider_example

Step 2: Adding the Provider Package

You need to add the provider package to your project to get started with state management.

  1. Open the pubspec.yaml file and add the provider dependency:yamlCopy codedependencies: flutter: sdk: flutter provider: ^6.0.0
  2. Save the file, and run:bashCopy codeflutter pub get

This will download and install the Provider package into your project.


Step 3: Understanding Provider – Core Concepts

Before we dive into the implementation, let’s break down some key concepts you’ll encounter when working with Provider:

  • ChangeNotifier: A simple class provided by Flutter to hold state and notify listeners when that state changes.
  • ChangeNotifierProvider: The widget that provides an instance of a ChangeNotifier to the widget tree. It allows other widgets to listen for changes.
  • Consumer: A widget that listens for state changes in the provided ChangeNotifier and rebuilds itself accordingly.

Step 4: Creating a Simple App with Provider

Let’s build a simple counter app using Provider to manage the state.

Step 4.1: Define a Counter Model with ChangeNotifier

The model represents the business logic of the app, managing the state and notifying widgets of any state changes.

  1. Create a new file lib/models/counter_model.dart:dart

import ‘package:flutter/material.dart’;

class CounterModel with ChangeNotifier {
int _counter = 0;

int get counter => _counter;

void increment() {
_counter++;
notifyListeners(); // Notifies all listeners (widgets) that state has changed.
}
}

In this file:

  • We’ve defined a CounterModel class that extends ChangeNotifier.
  • The increment function increases the counter and calls notifyListeners() to update the UI.

Step 4.2: Provide the CounterModel to the App

Next, we need to wrap our app with ChangeNotifierProvider to make CounterModel available throughout the widget tree.

  1. Modify lib/main.dart:dart

import ‘package:flutter/material.dart’;
import ‘package:provider/provider.dart’;
import ‘models/counter_model.dart’;
import ‘counter_screen.dart’;

void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => CounterModel()),
],
child: MyApp(),
),
);
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: ‘Flutter Demo’,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: CounterScreen(),
);
}
}

  • Here, we’ve wrapped our app with ChangeNotifierProvider, which provides an instance of CounterModel to all the widgets within the app.
  • The MultiProvider allows you to manage multiple providers if needed.

Step 4.3: Building the Counter UI

Now, let’s create a screen to display and interact with the counter.

  1. Create a new file lib/counter_screen.dart:dart

import ‘package:flutter/material.dart’;
import ‘package:provider/provider.dart’;
import ‘models/counter_model.dart’;

class CounterScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(‘Provider Counter Example’),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
‘You have pushed the button this many times:’,
),
Consumer(
builder: (context, counter, child) {
return Text(
‘${counter.counter}’, // Displaying the counter value.
style: Theme.of(context).textTheme.headline4,
);
},
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Provider.of(context, listen: false).increment();
},
tooltip: ‘Increment’,
child: Icon(Icons.add),
),
);
}
}

  • The Consumer widget listens to the CounterModel. When the notifyListeners() method is called in CounterModel, the Consumer automatically rebuilds to show the updated counter value.
  • The FloatingActionButton triggers the increment method of CounterModel, which updates the state and refreshes the UI.

Step 4.4: Running the App

  1. Run the app:bashCopy codeflutter run

You should see a simple app with a floating action button. Every time you press the button, the counter value will increase, demonstrating Provider’s ability to manage state and update the UI.


Step 5: Exploring the Benefits of Provider

Now that you’ve created a basic app using Provider, let’s discuss why it’s useful for more advanced use cases.

  • Separation of Concerns: Provider helps you separate business logic (state management) from UI components.
  • Scalability: As your app grows, managing state across multiple widgets becomes easier with Provider, as the logic remains centralized.
  • Efficient Rebuilds: Only widgets listening to the specific state changes are rebuilt, which boosts performance.
  • Composability: Multiple state objects can be combined and managed using MultiProvider.

Step 6: Advanced Use Cases

Once you are comfortable with the basic usage, you can move on to more complex scenarios with Provider:

Step 6.1: Using Provider.of for More Control

If you need more direct control, you can use the Provider.of method to interact with the provider outside of the build method.

Example:

dartCopy codefinal counter = Provider.of<CounterModel>(context);
counter.increment();

Step 6.2: Working with Multiple Providers

You can manage multiple state objects with MultiProvider:

dartCopy codeMultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => CounterModel()),
ChangeNotifierProvider(create: (_) => AnotherModel()),
],
child: MyApp(),
)

Step 6.3: Combining Providers with FutureBuilder or StreamBuilder

For asynchronous data handling (e.g., fetching data from an API), you can combine Provider with FutureBuilder or StreamBuilder to manage state efficiently.


Conclusion

By following this step-by-step guide, you’ve learned how to manage state in a Flutter app using Provider. You built a simple counter app, explored the key concepts of ChangeNotifier and Consumer, and understood how Provider simplifies state management in Flutter apps.

You can now apply Provider to more complex use cases such as handling user authentication, managing form inputs, or fetching data from APIs. Provider’s flexibility and ease of use make it an essential tool for any Flutter developer looking to create scalable, maintainable apps.


Next Steps:

  • Explore Riverpod, an alternative to Provider with a more declarative API.
  • Implement state management for multiple features like user login, settings, and data fetching.

Happy coding with Flutter and Provider!

1 comment

comments user
zoritoler imol

I truly appreciate this post. I have been looking all over for this! Thank goodness I found it on Bing. You have made my day! Thanks again

Post Comment