52 Provider (Counter App)
I’ll show you how to implement the same counter app using Provider, which is built on top of InheritedWidget but provides a much cleaner API.
52.1 Provider Solution
First, you’ll need to add Provider to your pubspec.yaml
:
dependencies:
flutter:
sdk: flutter
provider: ^6.1.1
52.1.1 Counter Model (Business Logic)
import 'package:flutter/foundation.dart';
class CounterModel extends ChangeNotifier {
int _counter = 0;
int get counter => _counter;
void increment() {
++;
_counter// Tells Provider to rebuild dependent widgets
notifyListeners(); }
}
52.1.2 Main App Implementation
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());}
class MyApp extends StatelessWidget {
@override
{
Widget build(BuildContext context) return ChangeNotifierProvider(
: (context) => CounterModel(),
create: MaterialApp(
child: HomePage(),
home,
)
);}
}
class HomePage extends StatelessWidget {
@override
{
Widget build(BuildContext context) // Clean - no props needed!
return Scaffold(
: AppBar(title: Text('Provider Counter')),
appBar: MainContent(),
body
);}
}
class MainContent extends StatelessWidget {
@override
{
Widget build(BuildContext context) // Clean - no props needed!
return Center(
: CounterSection(),
child
);}
}
class CounterSection extends StatelessWidget {
@override
{
Widget build(BuildContext context) return Column(
: MainAxisAlignment.center,
mainAxisAlignment: [
children,
CounterDisplay(): 20),
SizedBox(height,
CounterButton(),
]
);}
}
class CounterDisplay extends StatelessWidget {
@override
{
Widget build(BuildContext context) // Option 1: Watch for changes (rebuilds when counter changes)
final counter = context.watch<CounterModel>().counter;
return Text(
'Count: $counter',
: Theme.of(context).textTheme.headlineMedium,
style
);}
}
class CounterButton extends StatelessWidget {
@override
{
Widget build(BuildContext context) return ElevatedButton(
// Option 2: Read without watching (doesn't rebuild this widget)
: () => context.read<CounterModel>().increment(),
onPressed: Text('Increment'),
child
);}
}
52.2 Widget Tree Diagram with Provider
MyApp
└── ChangeNotifierProvider<CounterModel>
├── Creates and manages: CounterModel instance
├── Provides to all descendants
└── MaterialApp
└── HomePage (clean - no props!)
└── MainContent (clean - no props!)
└── CounterSection (clean - no props!)
├── CounterDisplay
│ └── context.watch<CounterModel>() ──┐
│ (rebuilds on changes) │
└── CounterButton │
└── context.read<CounterModel>() ───┤
(calls methods, no rebuild) │
│
┌──────────────────────────────────────────────────────────┘
│
▼
CounterModel (Business Logic)
├── _counter: int
├── get counter → int
├── increment() → notifyListeners()
└── extends ChangeNotifier
52.3 Data Flow and Callback Diagram
Provider System Flow:
┌─────────────────────────────────────────┐
│ ChangeNotifierProvider │
│ ┌─────────────────────────────────┐ │
│ │ CounterModel │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ _counter: 0 │ │ │
│ │ │ increment() { │ │ │
│ │ │ _counter++; │ │ │
│ │ │ notifyListeners(); │ │ │
│ │ │ } │ │ │
│ │ └─────────────────────────┘ │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
│
│ Provides model to all descendants
▼
┌───────────────────────────────┐
│ Widget Tree │
│ │
│ HomePage │
│ └── MainContent │
│ └── CounterSection │
│ ├── CounterDisplay ◄── context.watch<CounterModel>()
│ └── CounterButton ◄── context.read<CounterModel>()
└───────────────────────────────┘
Interaction Flow:
1. User Taps Button
│
▼
2. context.read<CounterModel>().increment()
│
▼
3. CounterModel._counter++
│
▼
4. notifyListeners() called
│
▼
5. Provider notifies all listeners
│
▼
6. Only CounterDisplay rebuilds (because it uses context.watch)
│
▼
7. UI updates with new counter value
Watch vs Read:
┌─────────────────┬─────────────────┐
│ context.watch │ context.read │
├─────────────────┼─────────────────┤
│ • Rebuilds │ • No rebuild │
│ • Use for UI │ • Use for │
│ that shows │ callbacks/ │
│ data │ methods │
│ • Like │ • Like calling │
│ subscribing │ a function │
└─────────────────┴─────────────────┘
52.4 Key Provider Concepts
52.4.1 1. Separation of Concerns
// Business logic is completely separate
class CounterModel extends ChangeNotifier {
// Pure Dart class - no Flutter widgets involved
}
This is similar to how you might separate data processing logic in your Python scripts from the presentation layer.
52.4.2 2. Two Ways to Access Data
context.watch
final counter = context.watch<CounterModel>().counter;
// This widget rebuilds when CounterModel changes
context.read
.read<CounterModel>().increment();
context// Just calls the method, doesn't listen for changes
52.4.3 3. Automatic Dependency Tracking
Provider automatically tracks which widgets depend on what data. When notifyListeners()
is called, only widgets using context.watch
rebuild.
52.5 Benefits Over InheritedWidget
Cleaner API:
- No need to write
updateShouldNotify
- No need to create custom inherited widgets
- Built-in change notification system
Better Performance:
- Granular rebuilds - only widgets that watch specific data rebuild
- Can watch specific properties instead of entire objects
Type Safety:
- Generic types ensure you get the right model
- Compile-time checking instead of runtime casting
Easier Testing:
- Business logic (CounterModel) is pure Dart
- Can unit test without any Flutter dependencies
- Easy to mock in widget tests
52.6 Advanced Provider Usage
You can also use Selector for even more precise rebuilds:
class CounterDisplay extends StatelessWidget {
@override
{
Widget build(BuildContext context) return Selector<CounterModel, int>(
: (context, model) => model.counter,
selector: (context, counter, child) {
builderreturn Text('Count: $counter');
},
);}
}
This only rebuilds when the counter
specifically changes, even if other properties in CounterModel
change.
Provider essentially gives you Redux-like state management with much less boilerplate, making it perfect for Flutter apps of any size.
Would you like me to show you how to handle more complex state with multiple providers or async operations?