Provider: ProxyProvider, Lifecycle & Testing
ProxyProvider: Dependent Providers
// CartModel needs AuthService to know which user's cart to load --
// lets one provider be built using another provider's value
ChangeNotifierProxyProvider<AuthService, CartModel>(
create: (_) => CartModel(),
update: (_, auth, cart) => cart!..updateUser(auth.currentUser),
)FutureProvider: Async Values
// Provides null while the future is pending, then the resolved
// User once it completes -- dependent widgets rebuild on that transition
FutureProvider<User?>(
create: (_) => fetchCurrentUser(),
initialData: null,
)Automatic Disposal
ChangeNotifierProvider automatically calls dispose() on the ChangeNotifier it created when the provider is removed from the tree (e.g. navigating away from a screen) -- no manual lifecycle tracking needed in most cases.
Provider.value(): Existing vs. New Objects
// CORRECT -- reuses an object created/owned elsewhere
Provider.value(value: existingCartModel)
// WRONG -- constructs a brand new CartModel on EVERY rebuild of the
// surrounding widget, since there's no create-callback caching it
Provider.value(value: CartModel())Testing ChangeNotifier Directly
// Plain Dart class -- no widget rendering needed to test its logic
test('addItem increases itemCount', () {
final cart = CartModel();
cart.addItem(testItem);
expect(cart.itemCount, 1);
});Provider vs. BLoC vs. Riverpod
Provider directly exposes a mutable ChangeNotifier -- simpler, less boilerplate, but less structural enforcement of how state changes than BLoC's explicit event classes.
ProviderNotFoundException is thrown if a widget calls watch<T>()/read<T>() for a type with no matching provider higher in the ANCESTOR tree.
Riverpod (same author) evolved Provider to move more safety checks to compile time -- Provider remains a solid, simpler choice for many existing/smaller apps.
Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free