Flutter: Change Notifier

Carlos Costa

In brief, ChangeNotifier is a simple class that provides change notification to its listeners.

In this note we will create a simple counter to demonstrate how to use the ChangeNotifier class, we also will combine a change notifier with a mixin to make it easier to use.

Let’s create some files:

  • counter_state: Class that represent the state of our counter.
  • counter_mixin: Mixin that register the listeners and notify them when the state changes.
  • decrement_button: Widget that decrement the counter.
  • increment_button: Widget that increment the counter.

Counter State


import 'package:flutter/material.dart';

class _Counter extends ChangeNotifier {
  int value = 0;

  void increment() {
    value++;
    notifyListeners();
  }

  void decrement() {
    value--;
    notifyListeners();
  }
}

final counter = _Counter();
  • ChangeNotifier: is a class that provides change notification to its listeners.

The _Counter class is private because we don’t want to expose it, we only want to expose the counter variable.

The increment and decrement methods are responsible for changing the state and notify the listeners.

Counter Mixin


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

mixin CounterMixin<T extends StatefulWidget> on State<T> {
  @override
  void initState() {
    super.initState();
    counter.addListener(action);
  }

  @override
  void dispose() {
    super.dispose();
    counter.removeListener(action);
  }

  action() {
    setState(() {});
  }
}

This mixin extends the State class and register the listeners in the initState method. Every widget that use this mixin will be considered a listener.

Increment and Decrement buttons


import 'package:flutter/material.dart';
import 'counter/counter_mixin.dart';
import 'counter/counter_state.dart';

class DecrementButton extends StatefulWidget {
  const DecrementButton({super.key});

  @override
  State<DecrementButton> createState() => _DecrementButtonState();
}

class _DecrementButtonState extends State<DecrementButton> with CounterMixin {
  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: counter.decrement,
      child: Text('Decrement ${counter.value}'),
    );
  }
}
import 'package:flutter/material.dart';
import 'counter/counter_mixin.dart';
import 'counter/counter_state.dart';

class IncrementButton extends StatefulWidget {
  const IncrementButton({super.key});

  @override
  State<IncrementButton> createState() => _IncrementButtonState();
}

class _IncrementButtonState extends State<IncrementButton> with CounterMixin {
  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: counter.increment,
      child: Text('Increment ${counter.value}'),
    );
  }
}

To finish, we create the increment and decrement buttons, they are stateful widgets because they need to register the listeners by using the mixin.

References