Skip to main content

Flutter State Management 101

Compare setState, Provider, and Riverpod with practical guidance on when to use each.

App DevelopmentIntermediate 14 min read 5 lessons
FlutterDartState

What You'll Learn

  • Know when to use setState vs a state management library
  • Wire up Provider for app-wide shared state
  • Migrate to Riverpod for type-safe, testable state
  • Avoid common pitfalls like rebuilding too many widgets
  • Structure a Flutter app around clean state boundaries

Prerequisites

  • Basic Dart syntax
  • Familiarity with Flutter widgets and StatefulWidget

Step-by-Step Guide

  1. Step 1

    1. setState — Widget-Local State

    setState is Flutter's built-in way to update a StatefulWidget. It triggers a rebuild of that widget's subtree. Use it only for state that is truly local to a single widget (e.g., whether a dropdown is open, a tab index, a text field value). If two widgets need the same state, lift it up or reach for a proper state solution.

  2. Step 2

    2. InheritedWidget — The Foundation

    Provider and Riverpod both build on InheritedWidget, Flutter's mechanism for passing data down the tree without manually threading it through constructors. Understanding InheritedWidget helps you debug why context.watch() triggers rebuilds. You rarely write InheritedWidget directly, but knowing it exists explains everything.

  3. Step 3

    3. Provider — Simple App-Wide State

    Wrap your MaterialApp with MultiProvider. Create a ChangeNotifier class for each domain of state. Use context.watch<T>() to rebuild on changes, context.read<T>() to call methods without subscribing. Provider is great for small to medium apps. Add provider: ^6.x to pubspec.yaml.

  4. Step 4

    4. Riverpod — Type-Safe & Testable

    Riverpod removes the BuildContext dependency for reading providers, making it fully testable in plain Dart. Define providers as top-level constants. Use ConsumerWidget instead of StatelessWidget. ref.watch() subscribes, ref.read() doesn't. StateNotifierProvider + StateNotifier replaces ChangeNotifier for more explicit state transitions.

  5. Step 5

    5. Choosing the Right Tool

    Single screen with isolated state → setState. Small app with a few shared values → Provider. Large app, teams, or needing testability → Riverpod. Avoid over-engineering: start simple and refactor when friction appears. Don't introduce BLoC or Redux unless the team is already familiar and the complexity justifies it.

Further Reading & Resources

Ready to put this into practice?

Apply for a real App Development internship and ship production projects with mentorship.

Related Tutorials