Flutter Provider

Google's recommended state management solution

📦 What is Provider?

Provider is a wrapper around InheritedWidget that makes state management simple and scalable. It's officially recommended by Google for Flutter apps, offering easy dependency injection and reactive state updates with minimal boilerplate.


// Access provider data
final counter = Provider.of<Counter>(context);
                                    

Key Concepts

🎯

Simple API

Easy to learn and use

Provider.of<T>(context)
context.watch<T>()
🔄

ChangeNotifier

Observable state objects

class Model extends 
  ChangeNotifier

Efficient

Only rebuilds listeners

Consumer<Model>(
  builder: (context, model, child)
🏆

Recommended

Official Google solution

// Add to pubspec.yaml
provider: ^6.0.0

🔹 Installation

Add Provider to your pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
  provider: ^6.0.0

Then run:

flutter pub get

🔹 Basic Counter Example

Create a counter using Provider:

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

// 1. Create a ChangeNotifier model
class Counter extends ChangeNotifier {
  int _count = 0;
  
  int get count => _count;
  
  void increment() {
    _count++;
    notifyListeners(); // Notify widgets to rebuild
  }
}

// 2. Provide the model
void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => Counter(),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: CounterPage(),
    );
  }
}

Output:

A counter model that notifies listeners when the count changes.

🔹 Consuming Provider Data

Three ways to access Provider data:

class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Provider Counter')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // Method 1: Provider.of
            Text(
              '${Provider.of<Counter>(context).count}',
              style: TextStyle(fontSize: 48),
            ),
            
            // Method 2: context.watch (shorter syntax)
            Text(
              '${context.watch<Counter>().count}',
              style: TextStyle(fontSize: 48),
            ),
            
            // Method 3: Consumer (more control)
            Consumer<Counter>(
              builder: (context, counter, child) {
                return Text(
                  '${counter.count}',
                  style: TextStyle(fontSize: 48),
                );
              },
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // Don't listen to changes here
          context.read<Counter>().increment();
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

Output:

A counter page showing three different ways to access and display the counter value.

🔹 Multiple Providers

Use MultiProvider for multiple state objects:

class UserModel extends ChangeNotifier {
  String _name = 'Guest';
  String get name => _name;
  
  void setName(String newName) {
    _name = newName;
    notifyListeners();
  }
}

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => Counter()),
        ChangeNotifierProvider(create: (_) => UserModel()),
      ],
      child: MyApp(),
    ),
  );
}

// Access both providers
class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counter = context.watch<Counter>();
    final user = context.watch<UserModel>();
    
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Hello, ${user.name}!'),
            Text('Count: ${counter.count}'),
          ],
        ),
      ),
    );
  }
}

Output:

An app with multiple providers managing different pieces of state.

🔹 Shopping Cart Example

A practical example with Provider:

class CartModel extends ChangeNotifier {
  final List<String> _items = [];
  
  List<String> get items => _items;
  int get itemCount => _items.length;
  
  void addItem(String item) {
    _items.add(item);
    notifyListeners();
  }
  
  void removeItem(String item) {
    _items.remove(item);
    notifyListeners();
  }
  
  void clear() {
    _items.clear();
    notifyListeners();
  }
}

class ShoppingPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Shopping Cart'),
        actions: [
          Consumer<CartModel>(
            builder: (context, cart, child) {
              return Padding(
                padding: EdgeInsets.all(16),
                child: Text('Items: ${cart.itemCount}'),
              );
            },
          ),
        ],
      ),
      body: Column(
        children: [
          ElevatedButton(
            onPressed: () {
              context.read<CartModel>().addItem('Apple');
            },
            child: Text('Add Apple'),
          ),
          Expanded(
            child: Consumer<CartModel>(
              builder: (context, cart, child) {
                return ListView.builder(
                  itemCount: cart.itemCount,
                  itemBuilder: (context, index) {
                    return ListTile(
                      title: Text(cart.items[index]),
                      trailing: IconButton(
                        icon: Icon(Icons.delete),
                        onPressed: () {
                          cart.removeItem(cart.items[index]);
                        },
                      ),
                    );
                  },
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

Output:

A shopping cart app that displays items and allows adding/removing them.

🔹 When to Use Provider

✅ Good For:

  • Most Flutter applications
  • Sharing state across widgets
  • Dependency injection
  • Medium to large apps
  • Learning state management

❌ Consider Alternatives For:

  • Very simple apps (use setState)
  • Complex async operations (try Riverpod or Bloc)
  • Apps needing time-travel debugging (use Redux)
  • When you prefer code generation (use Riverpod)

🔹 Provider Best Practices

  • Use context.watch<T>(): For listening to changes
  • Use context.read<T>(): For one-time access without listening
  • Use Consumer: For partial rebuilds
  • Keep models simple: One responsibility per model
  • Dispose properly: Provider handles disposal automatically

🧠 Test Your Knowledge

Which method notifies listeners in a ChangeNotifier?