Mobile Development 17 min read

Choosing the Best Flutter State Management in 2021: InheritedWidget, Provider, and Riverpod

This article explains when to use a state‑management solution in Flutter, compares Ephemeral and App state, and provides detailed overviews, usage steps, and pros‑cons of InheritedWidget, Provider, and the newer Riverpod library.

QQ Music Frontend Team
QQ Music Frontend Team
QQ Music Frontend Team
Choosing the Best Flutter State Management in 2021: InheritedWidget, Provider, and Riverpod
Unlike Redux’s dominance in React, Flutter offers many data‑flow solutions; this article introduces the most worthwhile options for 2021, including the familiar InheritedWidget and provider, as well as the eagerly anticipated Riverpod.

1. When does a state require a data‑flow management solution?

For declarative UI, UI = f(state); the build method is f, so a solution should let consumers obtain data, be notified on updates, and avoid unnecessary rebuilds.

Only the part of the state that triggers UI updates matters. State can be divided into Ephemeral State (local to a single widget, e.g., animation progress) and App State (shared across the app, e.g., login status).

Ephemeral State is used by a single widget, such as the progress of a complex animation.

App State is retained globally and shared by many components, like a user’s login status.

Generally, the type of a state can be inferred from which components use it.

Ephemeral State can be managed with a StatefulWidget.

For App State, consider the following approaches:

InheritedWidget – a built‑in component for sharing data with descendants.

Event Bus – a global singleton (not covered here).

Data‑flow frameworks – community solutions such as provider or Riverpod.

2. InheritedWidget

InheritedWidget is a crucial Flutter component that propagates ancestor state to descendants without passing parameters manually . Its properties are final , meaning that updating them triggers a UI rebuild.

2.1 How to use it

Typical steps:

Create a widget that extends InheritedWidget and holds the state and methods to modify it.

Place this widget at the top of the subtree that needs the state.

In descendant widgets, call the static

of(context)

method to retrieve the state.

Now see how InheritedWidget works internally.

2.2 Establishing dependency between nodes

When a child calls

of

, Flutter invokes

BuildContext.getElementForInheritedWidgetOfExactType

to create a dependency.

Each

Element

maintains two maps:

_inheritedWidgets

: a

Map<Type, InheritedElement>

of all ancestor inherited widgets.

_dependencies

: records the specific ancestors this element depends on.

InheritedElement also keeps a list of its dependents. When a matching ancestor is found, it registers the dependent and calls

updateInheritance

on the ancestor.

2.3 Why use BuildContext to obtain data?

Children retrieve data via

XXDataWidget.of(context).data

. The

context

represents the widget’s element, which provides access to the nearest matching inherited widget.

<code>@override
InheritedElement getElementForInheritedWidgetOfExactType&lt;T extends InheritedWidget&gt;() {
    assert(_debugCheckStateIsActiveForAncestorLookup());
    final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
    return ancestor;
}
</code>

Because

_inheritedWidgets

is a

Map

, it can only locate the nearest ancestor of the same type, which is a limitation.

2.4 Update mechanism

When an update is needed,

InheritedWidget.updateShouldNotify

(usually overridden by the user) decides whether to notify dependents; if so, it iterates over

_dependents

and calls

Element.didChangeDependencies

.

3. provider

Although InheritedWidget works, it requires a lot of boilerplate. Provider, now at version 4.3.3, is the officially recommended solution and reduces repetitive code.

Provider builds on InheritedWidget but adds many optimizations such as lazy loading and selective rebuilds.

3.1 How to use it

Provider usage is extensive; detailed guides are available online.

Flutter | State Management Guide – Provider https://juejin.cn/post/6844903864852807694

3.2 Simple implementation

Provider is essentially a thin wrapper around InheritedWidget. Readers are encouraged to implement a minimal

mini_provider

to deepen understanding.

Cross‑component state sharing (Provider) https://book.flutterchina.club/chapter7/provider.html

3.3 Provider and MVVM

Common pitfalls when first using provider include excessive rebuilds if Selector/Consumer are not used properly, and difficulty handling dependent data across pages.

Even with lazy loading, missing Selector or Consumer can cause unnecessary UI refreshes.

When pages have data dependencies, refreshing becomes cumbersome.

Flutter does not enforce a specific architecture; you can adopt MVC, MVVM, or any pattern you prefer.

Key MVVM concepts:

View – the UI and user interactions.

Model – holds raw data.

ViewModel – processes data for the view and contains helper functions.

Repository – fetches data from databases or APIs.

Bean – data entity definitions.

Separating ViewModel from Model prevents unnecessary rebuilds of the Model when the ViewModel updates.

3.4 Encapsulating a generic page container

Most pages need to handle loading, empty, error, and success states. A reusable container can abstract this logic, favoring composition over inheritance for UI components.

Typical classes involved:

ChangeNotifier – Flutter’s listener‑subscriber base class.

NormalPageState – enumeration of page states.

NormalPageController – extends ChangeNotifier to manage state changes; mixed into a ViewModel.

ContainerStateManager – displays content based on

NormalPageState

and subscribes via provider.

3.5 Drawbacks

Provider’s limitations:

It couples dependency injection with UI code.

Because it relies on InheritedWidget, it can only locate the nearest same‑type ancestor.

State availability is discovered at runtime.

The author of provider, Remi Rousselet, created Riverpod to address these three issues while preserving provider’s ergonomics.

4. Riverpod

Riverpod’s slogan is “provider but different”. It decouples from Flutter, solves provider’s shortcomings, and is a promising 2021 solution.

4.1 Usage

4.1.1 Where is the state stored?

Wrap the root widget with

ProviderScope

; multiple scopes can override upper‑level state.

<code>void main() {
  runApp(
    ProviderScope(
      child: MyApp(),
    ),
  );
}
</code>

4.1.2 Listening to provider changes and rebuilding UI

Method 1: Use

ConsumerWidget

, which provides a

ScopedReader

to read providers and trigger rebuilds.

<code>typedef ScopedReader = T Function&lt;T&gt;(ProviderBase&lt;Object, T&gt; provider);
</code>
<code>final helloProvider = Provider((_) => 'Hello World');

class HomePage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, ScopedReader watch) {
    final String value = watch(helloProvider);
    return Scaffold(
      appBar: AppBar(title: Text('Example')),
      body: Center(child: Text(value)),
    );
  }
}
</code>

Method 2: Use a regular

Consumer

widget inside a

StatelessWidget

.

<code>class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Example')),
      body: Center(
        child: Consumer(
          builder: (context, watch, child) {
            final String value = watch(helloProvider);
            return Text(value);
          },
        ),
      ),
    );
  }
}
</code>

4.1.3 Accessing a provider outside of build

Use

context.read

to obtain a provider instance.

<code>class IncrementNotifier extends ChangeNotifier {
  int _value = 0;
  int get value => _value;
  void increment() {
    _value += 1;
    notifyListeners();
  }
}

final incrementProvider = ChangeNotifierProvider((ref) => IncrementNotifier());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Riverpod Tutorial',
      home: Scaffold(
        body: Center(
          child: Consumer(
            builder: (context, watch, child) {
              final incrementNotifier = watch(incrementProvider);
              return Text(incrementNotifier.value.toString());
            },
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            context.read(incrementProvider).increment();
          },
          child: Icon(Icons.add),
        ),
      ),
    );
  }
}
</code>

4.2 Advantages

Riverpod can retrieve the same type of data from ancestors, not just the nearest one.

<code>final firstStringProvider = Provider((ref) => 'First String Data');
final secondStringProvider = Provider((ref) => 'Second String Data');

// Inside a ConsumerWidget
final first = watch(firstStringProvider);
final second = watch(secondStringProvider);
</code>

Dependencies become simpler.

<code>class FakeHttpClient {
  Future<String> get(String url) async {
    await Future.delayed(const Duration(seconds: 1));
    return 'Response from $url';
  }
}

final fakeHttpClientProvider = Provider((ref) => FakeHttpClient());
final responseProvider = FutureProvider<String>((ref) async {
  final httpClient = ref.read(fakeHttpClientProvider);
  return httpClient.get('https://baidu.com');
});
</code>

5. Summary

A quick comparison of the three data‑flow solutions:

Solution

Pros

Cons

InheritedWidget

Built‑in Flutter solution.

Excessive boilerplate; can only find the nearest same‑type state.

Provider

Comprehensive, many optimizations, large community, stable.

Couples DI with UI; limited to nearest same‑type state; runtime discovery only.

Riverpod

Created by provider’s author, fixes provider’s three main drawbacks; not coupled to Flutter; supports flutter_hooks.

Still in beta.

Riverpod is essentially a new version of provider with added benefits and is the most promising state‑management option for new projects in 2021; flutter_hooks is also worth exploring as a replacement for StatefulWidget.

FlutterState ManagementProviderRiverpodInheritedWidget
QQ Music Frontend Team
Written by

QQ Music Frontend Team

QQ Music Web Frontend Team

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.