What is Provider?
Provider is a wrapper around InheritedWidget that makes it easier to manage and share state across your Flutter app. It is the recommended state management solution for many applications because it's simple, scalable, and integrates well with the framework. Provider allows you to expose a model (e.g., a ChangeNotifier) to the widget tree, and listen to changes in a very efficient way – only widgets that depend on the data are rebuilt when it changes.
Why Use Provider?
- Simple: Minimal boilerplate compared to other state management solutions.
- Scalable: Works well for both small apps and large projects.
- Efficient: Only rebuilds widgets that consume the data.
- Testable: Easy to mock providers for unit tests.
- Official recommendation: Provider is the go‑to solution for many Flutter developers and is backed by the Flutter team.
Adding Provider to Your Project
Add the provider package to your pubspec.yaml:
dependencies:
provider: ^6.1.0
Then run flutter pub get.
Creating a Model with ChangeNotifier
Provider works best with classes that extend ChangeNotifier. The notifier holds the state and notifies listeners when it changes using notifyListeners().
Providing the Model
Wrap your app (or part of it) with a ChangeNotifierProvider. This makes the model available to all descendant widgets.
You can also use Provider.value if you already have an instance, but create is the recommended way because it ensures the provider is disposed when no longer needed.
Consuming the Model
There are several ways to access the provided value: Consumer, Provider.of, and context.watch / context.read (with provider >= 5.0.0).
- Consumer Widget
Consumer rebuilds only the part of the widget tree that depends on the data. It's the most efficient way to listen to changes.
- Provider.of
Provider.of<T>(context) gives you access to the model. With listen: false, it doesn't rebuild when the model changes; with listen: true (default), it does rebuild. Use listen: false for actions like increment.
- context.watch / context.read (Provider 5.0+)
These extension methods are even more concise: context.watch<T>() listens to changes, context.read<T>() does not.
MultiProvider for Multiple Models
When you have several models, use MultiProvider to combine them.
ProxyProvider for Dependent Models
If one model depends on another, use ProxyProvider. It rebuilds the dependent provider when the source changes.
Best Practices
- Use
ChangeNotifierfor models: It's simple and efficient.
- Use
- Place providers high in the tree: Usually just below
MaterialAppto make them available everywhere.
- Place providers high in the tree: Usually just below
- Use
Consumerorcontext.watchonly where needed to limit rebuilds.
- Use
- For actions (like increment), use
context.readto avoid unnecessary rebuilds.
- For actions (like increment), use
- Use
MultiProviderfor multiple providers to keep the tree clean.
- Use
- Dispose providers when needed:
ChangeNotifierProviderautomatically disposes the notifier; for custom providers, usedispose.
- Dispose providers when needed:
Common Mistakes
- Calling
notifyListenersunnecessarily – only call it when state actually changes.
- Calling
- Using
Provider.ofwithlisten: truein places where you don't need to rebuild – this can cause performance issues.
- Using
- Providing the same model multiple times – ensures the same instance is used throughout the app.
- Not disposing resources – if your model has streams or controllers, dispose them in the model's own
disposemethod.
- Not disposing resources – if your model has streams or controllers, dispose them in the model's own
- Over‑complicating with
ProxyProviderwhen not needed – use simple providers first, then add complexity as needed.
- Over‑complicating with
Key Takeaways
- Provider is a simple, scalable state management solution.
- Use
ChangeNotifierandChangeNotifierProviderto expose state.
- Use
- Consume state with
Consumer,context.watch, orProvider.of.
- Consume state with
- Use
MultiProviderfor multiple models,ProxyProviderfor dependencies.
- Use
- Always call
notifyListeners()after changing state to update the UI.
- Always call