0
0
FlutterHow-ToBeginner · 4 min read

How to Use Provider in Flutter: Simple State Management

To use Provider in Flutter, wrap your app or widget tree with a ChangeNotifierProvider that holds your state class. Access and listen to the state inside widgets using Provider.of<YourModel>(context, listen: true) or Consumer<YourModel> to rebuild UI when data changes.
📐

Syntax

The basic syntax involves creating a state class that extends ChangeNotifier, then wrapping your widget tree with ChangeNotifierProvider to provide that state. Inside widgets, use Provider.of<YourModel>(context, listen: true) to read or listen to the state, or use Consumer<YourModel> to rebuild only parts of the UI when the state changes.

  • ChangeNotifier: A class that holds your data and notifies listeners on changes.
  • ChangeNotifierProvider: Provides the instance of your state to the widget tree.
  • Consumer: Widget that listens to changes and rebuilds UI accordingly.
dart
class Counter extends ChangeNotifier {
  int value = 0;
  void increment() {
    value++;
    notifyListeners();
  }
}

ChangeNotifierProvider(
  create: (_) => Counter(),
  child: MyApp(),
);
💻

Example

This example shows a simple counter app using Provider. The Counter class holds the count and notifies listeners when incremented. The UI updates automatically when the button is pressed.

dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class Counter extends ChangeNotifier {
  int value = 0;
  void increment() {
    value++;
    notifyListeners();
  }
}

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (_) => Counter(),
      child: const MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Provider Example')),
        body: Center(
          child: Consumer<Counter>(
            builder: (context, counter, child) {
              return Text('Count: ${counter.value}', style: const TextStyle(fontSize: 30));
            },
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () => context.read<Counter>().increment(),
          child: const Icon(Icons.add),
        ),
      ),
    );
  }
}
Output
A screen with an app bar titled 'Provider Example', a large text showing 'Count: 0' in the center, and a floating '+' button. Pressing the button increments the count and updates the text.
⚠️

Common Pitfalls

  • Not wrapping your widget tree with ChangeNotifierProvider causes errors when accessing the provider.
  • Using Provider.of<YourModel>(context) without listen: true will not rebuild UI on changes.
  • Calling notifyListeners() is necessary to update listeners; forgetting it means UI won't refresh.
  • Using context.read<YourModel>() inside build() can cause stale data; prefer Consumer or Selector for rebuilds.
dart
/// Wrong: Missing ChangeNotifierProvider
Text('Count: ${Provider.of<Counter>(context, listen: true).value}'),

/// Right: Wrap with provider
ChangeNotifierProvider(
  create: (_) => Counter(),
  child: MyApp(),
);
📊

Quick Reference

Here is a quick summary of key Provider usage:

ConceptDescription
ChangeNotifierClass that holds state and calls notifyListeners() on changes.
ChangeNotifierProviderWraps widget tree to provide state instance.
ConsumerWidget that rebuilds UI when state changes.
context.read()Access state without listening for changes.
context.watch()Access state and rebuild when it changes.

Key Takeaways

Wrap your app with ChangeNotifierProvider to provide state to widgets.
Use Consumer or context.watch to listen and rebuild UI on state changes.
Always call notifyListeners() inside your ChangeNotifier to update UI.
Avoid accessing provider without wrapping or without proper listening.
Use context.read for one-time actions without rebuilding UI.