Provider: Pass Data Down Your Widget Tree
Think of Provider as a delivery service for your app's data, passing it down the widget tree without sending it through every widget's constructor. It's a lightweight way to manage state. The footgun: never use `.value` to create a new object.
WHY IT EXISTS: Flutter's widget tree structure can make it cumbersome to pass data from a high-level widget to a low-level one. Without a solution, you'd have to pass the data through the constructor of every intermediate widget, a tedious and error-prone process. Provider was created to solve this data-passing problem elegantly.
THE MENTAL MODEL: Think of Provider as a network of invisible pipes running alongside your widget tree. You can "provide" a piece of data (a state object, a value) at a high point in the tree, and any widget further down can "read" from that pipe without needing to know about the widgets in between. It decouples your UI from your data's physical location in the tree.
HOW IT WORKS: Provider is a wrapper around Flutter's built-in InheritedWidget, but with simplified resource management. You wrap a widget (like MaterialApp or a specific screen) with a Provider widget, specifying what data to provide. For example, ChangeNotifierProvider is used for objects that can notify listeners of changes. Widgets below can then access this data using methods like context.watch<MyData>() to listen for changes and rebuild, or context.read<MyData>() to just get the data once without subscribing.
WHEN TO USE IT: Use Provider for simple to moderately complex state management. It's an officially recommended starting point for state in Flutter. It's excellent for sharing application-wide state (like user authentication or theme settings) or for providing state scoped to a specific feature. Many other libraries, like flutter_bloc, use Provider under the hood for dependency injection.
WHEN NOT TO USE IT: For extremely complex state with intricate side effects and dependencies, you might find more structured solutions like riverpod (by the same author) offer more guardrails. Also, avoid using it to pass simple data between two closely related widgets where a direct constructor argument would be clearer and less magical.
ONE CANONICAL EXAMPLE: The most common footgun is misusing the constructors. To provide a NEWLY CREATED object that the provider will manage and dispose of, use the create factory: ChangeNotifierProvider(create: (context) => MyModel()). To provide an EXISTING object that you are managing elsewhere, use the .value constructor: ChangeNotifierProvider.value(value: myExistingModel). Getting this wrong leads to state not updating or objects being disposed of prematurely.
Read the original → pub.dev
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.