Flutter Daily: Understanding ValueNotifier for Lightweight State Management
Flutter Daily: Understanding ValueNotifier for Lightweight State Management
What You’ll Learn
Today we’ll explore ValueNotifier, Flutter’s built-in solution for simple, reactive state management. It’s perfect when you need to update UI without the overhead of full state management solutions like Provider or Bloc.
Why ValueNotifier?
When you have a small piece of state (like a counter, toggle, or loading flag), using setState() rebuilds the entire widget. ValueNotifier lets you rebuild only the widgets that care about that specific value—making your app more efficient.
Example
Here’s a practical example of a counter with a reset button:
import 'package:flutter/material.dart';
class CounterScreen extends StatefulWidget {
@override
State<CounterScreen> createState() => _CounterScreenState();
}
class _CounterScreenState extends State<CounterScreen> {
final ValueNotifier<int> _counter = ValueNotifier<int>(0);
@override
void dispose() {
_counter.dispose(); // Always clean up!
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('ValueNotifier Demo')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('You pressed the button:'),
// Only this ValueListenableBuilder rebuilds
ValueListenableBuilder<int>(
valueListenable: _counter,
builder: (context, count, child) {
return Text(
'$count',
style: Theme.of(context).textTheme.headlineLarge,
);
},
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () => _counter.value = 0,
child: Text('Reset'),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => _counter.value++,
child: Icon(Icons.add),
),
);
}
}
How It Works
- Create:
ValueNotifier<T>holds a value of typeT - Listen: Wrap widgets with
ValueListenableBuilderto rebuild when the value changes - Update: Change the value with
_counter.value = newValue - Clean up: Always dispose in the
dispose()method
Try It Yourself
Enhance the counter with these features:
- Add a “Decrease” button
- Change the text color to red when count is negative, green when positive
- Add a second
ValueNotifierto track total button presses (separate from the counter value)
Bonus challenge: Create a loading indicator using ValueNotifier<bool> that shows/hides a CircularProgressIndicator when fetching data.
Tip of the Day
When to use ValueNotifier vs setState:
- Use
ValueNotifierwhen only a small part of your widget tree needs to rebuild - Use
setState()for simple, local widget state where the entire widget rebuild is fine - Graduate to Provider/Riverpod when you need to share state across multiple screens
Pro tip: You can combine multiple ValueNotifiers with ValueListenableBuilder3 (from the flutter/foundation.dart package) to listen to multiple values at once without nesting builders!