Skip to content

Intermediate · State management · topic 28 of 50 · 35 min

Provider and ChangeNotifier

Explanation

Provider is a widely used, simple package for sharing state. Put your state in a class that extends ChangeNotifier and calls notifyListeners() after changes. Place a ChangeNotifierProvider above the widgets that need it (MultiProvider for several). Read it with context.watch<T>() in build (rebuilds on change) or context.read<T>() in callbacks (no rebuild); Consumer and Selector limit rebuilds to small parts.

Why Provider?

Provider wraps InheritedWidget in an easy API. It is used in many production apps, has been recommended in the Flutter documentation's simple state management guide, and is a good first state management tool to learn. Add it with flutter pub add provider.

The idea has three parts. A model class holds the shared data and the methods that change it. A provider widget makes one instance of that model available to everything below it in the tree. Widgets that need the data read it from context, and widgets that display it are rebuilt automatically when the model announces a change.

ChangeNotifier is a small class from Flutter's foundation library. It keeps a list of listeners; calling notifyListeners() tells all of them "something changed". Provider registers the widgets that watch the model as listeners, so you never call setState yourself for shared data.

Step 1: a ChangeNotifier model

dart
import 'package:flutter/foundation.dart';

class CartItem {
  const CartItem(this.name, this.price);
  final String name;
  final int price;
}

class CartModel extends ChangeNotifier {
  final List<CartItem> _items = [];

  List<CartItem> get items => List.unmodifiable(_items);    // read-only view
  int get count => _items.length;
  int get total => _items.fold(0, (sum, i) => sum + i.price);

  void add(CartItem item) {
    _items.add(item);
    notifyListeners();                                       // tell listeners to rebuild
  }

  void clear() {
    _items.clear();
    notifyListeners();
  }
}
Keep the list private; expose read-only getters and methods that call notifyListeners.

Step 2: provide it above the widgets

dart
void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => CartModel(),
      child: const ShopApp(),
    ),
  );
}

// Several models:
// MultiProvider(providers: [
//   ChangeNotifierProvider(create: (_) => CartModel()),
//   ChangeNotifierProvider(create: (_) => AuthModel()),
// ], child: const ShopApp())
Place providers as low as possible but above every widget that needs them.

Step 3: read it

dart
class CartBadge extends StatelessWidget {
  const CartBadge({super.key});
  @override
  Widget build(BuildContext context) {
    final count = context.watch<CartModel>().count;        // rebuilds when the cart changes
    return Badge(label: Text('$count'), child: const Icon(Icons.shopping_cart));
  }
}

class AddButton extends StatelessWidget {
  const AddButton({super.key, required this.item});
  final CartItem item;
  @override
  Widget build(BuildContext context) {
    return FilledButton(
      onPressed: () => context.read<CartModel>().add(item),   // read in callbacks: no rebuild needed
      child: const Text('Add to cart'),
    );
  }
}
watch in build to listen; read in event handlers to call methods.
APIUse inRebuilds on change?
context.watch<T>()buildYes
context.read<T>()Callbacks like onPressedNo
context.select<T, R>((t) => t.part)buildOnly when that part changes
Consumer<T>(builder: ...)Inside a larger buildOnly the Consumer's subtree

Consumer and select limit rebuilds: if only the cart badge depends on the count, wrap just the badge, not the whole screen. This matters for performance in big screens.

Keep the business logic, like calculating totals and checking stock, inside the model, not in widgets. Then the same rules apply on every screen, and you can unit test the model without any UI. ChangeNotifierProvider also disposes the model automatically when the provider is removed from the tree.

Think of it like this: ChangeNotifier is like a WhatsApp group admin for the cart. Widgets that watch the cart are group members: when the admin posts an update (notifyListeners), every member sees it. context.read is like messaging the admin privately to ask for a change, without joining the group.

Common mistakes

  • Forgetting notifyListeners(): data changes but the UI does not.
  • Using context.watch inside onPressed: an error or warning; use context.read there.
  • ProviderNotFoundException: the provider is not above the widget that reads it (for example, provided inside a different route). Move it higher.
  • Exposing the mutable list publicly so screens change it without notifying.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Provider and ChangeNotifier · 1/6

flutter pub add provider

Provider is a simple, popular package for sharing state, built on InheritedWidget.

Prefer a video by a teacher? Find free videos on “flutter provider changenotifier tutorial watch read consumer” on YouTube ↗

In real life: A shared cart across screens

A sweet shop's ordering app shows the cart count badge on every screen's AppBar, an "Add" button on each product, and a checkout screen with the total. One CartModel provided at the top of the app powers all of them. Adding a laddu on the product screen instantly updates the badge everywhere, and the checkout reads the same total.

dart
class CheckoutTotal extends StatelessWidget {
  const CheckoutTotal({super.key});
  @override
  Widget build(BuildContext context) {
    final total = context.select<CartModel, int>((c) => c.total);   // rebuild only when total changes
    return Text('To pay: Rs $total', style: Theme.of(context).textTheme.headlineSmall);
  }
}
select keeps rebuilds small.

Practice (0/3 done)

Task 1 · Question

You change data in a ChangeNotifier, but the screen does not update. What is likely missing?

Task 2 · Question

Which should an onPressed callback use to add an item to the cart?

Task 3 · Practice

Build a two-screen shop with Provider: a product list where each item has an Add button (context.read), an AppBar badge with the count (context.watch or select) on both screens, and a cart screen listing items with a total and a Clear button. Make sure the cart is shared between screens.

Quick quiz

Question 1

1.Which class do Provider models usually extend?

Question 2

2.What causes ProviderNotFoundException?

Question 3

3.Which API rebuilds a widget only when one value of the model changes?

Question 4

4.Where should you place a provider?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

Which class do Provider models usually extend?

Answer: ChangeNotifier

ChangeNotifier has addListener and notifyListeners.

What causes ProviderNotFoundException?

Answer: Reading a provider that is not above the widget in the tree

Move the provider higher.

Which API rebuilds a widget only when one value of the model changes?

Answer: context.select

select listens to a part.

Where should you place a provider?

Answer: Above all widgets that need it, but no higher than needed

Scope it sensibly.

Which property holds a list of widgets in a Column?Related

Answer: children

Column has children.

From Everything is a widget

Which object holds a StatefulWidget's State between rebuilds?Related

Answer: The element

Elements are long-lived.

From Interview questions: Flutter

What is app state?Related

Answer: State shared by many parts of the app, like a cart

It usually survives navigation.

From What state is, and setState

Which method runs once when a State is created?Related

Answer: initState

Use it for one-time setup.

From Stateless and stateful widgets

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 2 more topics in “State management” before its exam: Riverpod overview, Bloc, Cubit and choosing an approach.
  2. Module 7 exam: State management · 12 questions · 25 min, without help. 4 topics of this module still to finish first.
  3. Then the next module, Networking, storage and Firebase. 50 topics left in the course before the final exam.

← Previous: What state is, and setState