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

  1. Create: ValueNotifier<T> holds a value of type T
  2. Listen: Wrap widgets with ValueListenableBuilder to rebuild when the value changes
  3. Update: Change the value with _counter.value = newValue
  4. Clean up: Always dispose in the dispose() method

Try It Yourself

Enhance the counter with these features:

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:

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!