
In a Flutter app, “state” is any data you want to display in the user interface. A simple example is the value of a counter that increases when you press a button. Without a proper state management solution, you’d be forced to manually pass data down through a series of widget constructors, a practice known as “prop drilling.” This makes your code hard to read, maintain, and test.
Provider solves this by allowing you to define a single source of truth for your data and then easily access it from anywhere in your widget tree without passing it down manually. When the data changes, only the widgets that depend on it will rebuild, leading to better performance and a cleaner codebase.
Core Concepts of Provider
To get started with Provider, you need to understand three main concepts: ChangeNotifier
, ChangeNotifierProvider
, and Consumer
.
ChangeNotifier
This is a class from the Flutter SDK that you extend to create your data model. It provides a simple way to notify listeners when the state has changed. You’ll put all your data and the business logic to manipulate it (like incrementing a counter or adding an item to a cart) in this class. To trigger a UI update, you simply call thenotifyListeners()
method.ChangeNotifierProvider
This widget is responsible for creating an instance of yourChangeNotifier
class and providing it to its descendants in the widget tree. You typically wrap your entire app (or a specific part of it) with aChangeNotifierProvider
so that all the widgets below it can access the state.Consumer
TheConsumer
widget is what listens for changes in yourChangeNotifier
. When the state changes (i.e.,notifyListeners()
is called), theConsumer
rebuilds its UI to reflect the new data. UsingConsumer
is a great way to optimize performance, as it ensures only the specific part of the UI that needs to be updated is rebuilt, rather than the entire widget.
A Simple Counter App Example
Let’s walk through a basic example to see how these pieces fit together. We’ll create a simple app with a counter that can be incremented.
Step 1: Add the Dependency
First, add the Provider package to your pubspec.yaml
file:
YAML
dependencies:
flutter:
sdk: flutter
provider: ^6.0.0 # Use the latest version
Then, run flutter pub get
in your terminal.
Step 2: Create the ChangeNotifier
Model
Create a new file, say counter_model.dart
, and add your state and logic.
Dart
import 'package:flutter/foundation.dart';
class CounterModel with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // Notifies all listeners that the count has changed
}
}
Step 3: Provide the Model to the App
In your main.dart
file, wrap your app with ChangeNotifierProvider
to make the CounterModel
available.
Dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'counter_model.dart';
import 'home_page.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => CounterModel(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
Step 4: Consume the State in a Widget
Now, create a HomePage
to display the counter and the button. We’ll use a Consumer
to listen to changes in the CounterModel
and rebuild the Text
widget.
Dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'counter_model.dart';
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Provider Counter App'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('You have pushed the button this many times:'),
Consumer<CounterModel>(
builder: (context, counter, child) {
return Text(
'${counter.count}',
style: Theme.of(context).textTheme.headlineMedium,
);
},
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Provider.of<CounterModel>(context, listen: false).increment();
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
Notice how we use Provider.of<CounterModel>(context, listen: false)
in the FloatingActionButton
‘s onPressed
callback. The listen: false
parameter is crucial here because we only need to access the model’s method (increment()
) and not rebuild the FloatingActionButton
itself. This is another key optimization technique.
Conclusion
Provider is an excellent state management solution for Flutter, especially for beginners and small-to-medium-sized apps. Its simplicity, performance, and strong community support make it a top choice for developers. By understanding and applying the core concepts of ChangeNotifier
, ChangeNotifierProvider
, and Consumer
, you can write cleaner, more maintainable, and highly performant Flutter applications.