How to use Provider, Consumer, and ConsumerWidget


Up until now we've been creating two instances of the HiveDataStore: one in the main.dart file and one inside the HomePage.

HiveDataStore is a dependency for the HomePage, and we should only create it once. We can do this by using Riverpod as a dependency injection solution.

Step 1 is to add a parent ProviderScope in our main() function:

// inside main()
  child: MyApp(),

Step 2 is to create a Provider<HiveDataStore>:

// hive_data_store.dart
final dataStoreProvider = Provider<HiveDataStore>((ref) {
  return HiveDataStore();

Step 3 is to use it in the HomePage:

// 1. extend from ConsumerWidget
class HomePage extends ConsumerWidget {
  // 2. Add an additional WidgetReference argument
  Widget build(BuildContext context, WidgetReference ref) {
    // 3. get the dataStore with
    final dataStore =;
    return ValueListenableBuilder(
      valueListenable: dataStore.tasksListenable(),
      builder: (_, Box<Task> box, __) => TasksGridPage(
        tasks: box.values.toList(),

As an alternative to ConsumerWidget, we can return a Consumer inside the build() method, but this results in more boilerplate code.

BuildContext vs WidgetReference

There are some similarities between the two:

  • with Flutter we can use the BuildContext to access ancestor widgets such as Navigator and MediaQuery.
  • with Riverpod we can use this WidgetReference to access all the providers that we have created.

And some differences:

  • BuildContext works inside the widget tree and we can only use it to access ancestor widgets.
  • Riverpod providers are defined in the global scope and live outside the widget tree. We can use WidgetReference to access any provider by reference.


Also note that calling inside a widget will cause it to rebuild every time the Provider's value changes.

This is useful when we providers such as StateNotifierProviderFutureProvider or StreamProvider. See my Essential Guide for more info.

Complete and Continue