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