Skip to main content

ChangeNotifierProvider

caution

The content of this page may be outdated.
It will be updated in the future, but for now you may want to refer to the content in the top of the sidebar instead (introduction/essentials/case-studies/...)

ChangeNotifierProvider (flutter_riverpod/hooks_riverpod only) is a provider that is used to listen to and expose a ChangeNotifier from Flutter itself.

Using ChangeNotifierProvider is discouraged by Riverpod and exists primarily for:

  • an easy transition from package:provider when using its ChangeNotifierProvider
  • supporting mutable state, even though immutable state is preferred
info

Prefer using NotifierProvider instead.
Consider using ChangeNotifierProvider only if you are absolutely certain that you want mutable state.

Using mutable state instead of immutable state can sometimes be more efficient. The downside is, it can be harder to maintain and may break various features.
For example, using provider.select to optimize rebuilds of your widgets may not work if your state is mutable, as select will think that the value hasn't changed.
As such, using immutable data structures can sometimes be faster. Therefore it is important to make benchmarks specific to your use-case, to make sure that you are truly gaining performance by using ChangeNotifierProvider.

As a usage example, we could use ChangeNotifierProvider to implement a todo-list. Doing so would allow us to expose methods such as addTodo to let the UI modify the list of todos on user interactions:


class Todo {
Todo({
required this.id,
required this.description,
required this.completed,
});

String id;
String description;
bool completed;
}

class TodosNotifier extends ChangeNotifier {
final todos = <Todo>[];

// Let's allow the UI to add todos.
void addTodo(Todo todo) {
todos.add(todo);
notifyListeners();
}

// Let's allow removing todos
void removeTodo(String todoId) {
todos.remove(todos.firstWhere((element) => element.id == todoId));
notifyListeners();
}

// Let's mark a todo as completed
void toggle(String todoId) {
final todo = todos.firstWhere((todo) => todo.id == todoId);
todo.completed = !todo.completed;
notifyListeners();
}
}

// Finally, we are using ChangeNotifierProvider to allow the UI to interact with
// our TodosNotifier class.
final todosProvider = ChangeNotifierProvider<TodosNotifier>((ref) {
return TodosNotifier();
});

Now that we have defined a ChangeNotifierProvider, we can use it to interact with the list of todos in our UI:


class TodoListView extends ConsumerWidget {
const TodoListView({super.key});


Widget build(BuildContext context, WidgetRef ref) {
// rebuild the widget when the todo list changes
List<Todo> todos = ref.watch(todosProvider).todos;

// Let's render the todos in a scrollable list view
return ListView(
children: [
for (final todo in todos)
CheckboxListTile(
value: todo.completed,
// When tapping on the todo, change its completed status
onChanged: (value) =>
ref.read(todosProvider.notifier).toggle(todo.id),
title: Text(todo.description),
),
],
);
}
}