Mastering Flutter State Management : A Beginner’s Guide to Riverpod, Freezed, and DDD

Jawher Mehrez
3 min readApr 24, 2024

--

Creating a seamless user experience in your Flutter app requires not only clean code but also effective state management. In the world of Flutter, Riverpod shines as a powerful tool for managing app behavior while keeping your code organized and maintainable. In this guide, we’ll delve into the depths of Riverpod, exploring its key concepts and building a practical todo list app to demonstrate its capabilities.

Understanding Riverpod

At its core, Riverpod is more than just a state management library , it’s a versatile solution that seamlessly integrates with Flutter frameworks like StateNotifier and Freezed, all while adhering to Domain-Driven Design (DDD) principles. Think of Riverpod as the evolution of Provider, tailored specifically for Flutter development. It simplifies how you manage app behavior, ensuring your code remains clean and scalable.

Key Concepts of Riverpod

1. Provider
Providers are the backbone of Riverpod, serving as the central hub for your app’s information flow. They supply the necessary data for your app’s functionality and automatically update it when changes occur. For instance, a Provider can handle user authentication states within your app.

2. Consumer
Acting as a liaison between Providers and the user interface, Consumers fetch data from Providers and display it on the screen, ensuring that the UI accurately reflects the app’s current state.

3. Family
Riverpod allows you to create multiple instances of a Provider, which is particularly useful when dealing with lists or collections of items. For example, you can have a separate Provider for each todo item in a list.

Building a Todo List App with Riverpod

Let’s put theory into practice by building a simple todo application using Riverpod, StateNotifier, Freezed, and DDD principles.

Setting Up the Project

Start by structuring your project following DDD principles, with separate folders for domain, application, and presentation layers.

Defining the Todo Model

In the domain folder, define the todo model using Freezed:

// todo.dart
import 'package:freezed_annotation/freezed_annotation.dart';
part 'todo.freezed.dart';
@freezed
abstract class Todo with _$Todo {
const factory Todo({
required String title,
required bool isCompleted,
}) = _Todo;
}

Managing Todo States

Create the application logic for managing todo states and events using StateNotifier:

// todo_state.dart
import 'package:state_notifier/state_notifier.dart';
import '../domain/todo.dart';
class TodoState {
final List<Todo> todos;
TodoState(this.todos);
}
class TodoNotifier extends StateNotifier<TodoState> {
TodoNotifier() : super(TodoState([]));
void addTodo(String title) {
final newTodo = Todo(
title: title,
isCompleted: false,
);
state = TodoState([…state.todos, newTodo]);
}
void toggleTodoCompletion(int index) {
final updatedTodos = List<Todo>.from(state.todos);
updatedTodos[index] = updatedTodos[index].copyWith(
isCompleted: !updatedTodos[index].isCompleted,
);
state = TodoState(updatedTodos);
}
}

Creating Providers

Now, create providers to manage the state and events:

// todo_provider.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../application/todo_state.dart';
final todoNotifierProvider = StateNotifierProvider((ref) => TodoNotifier());

Building the UI

In the presentation layer, create widgets to display the todos and handle user interactions:

// todo_list_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../application/todo_state.dart';
import '../providers/todo_provider.dart';
class TodoListScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Todo List'),
),
body: Consumer(
builder: (context, watch, _) {
final todos = watch(todoNotifierProvider.state).todos;
return ListView.builder(
itemCount: todos.length,
itemBuilder: (context, index) {
final todo = todos[index];
return ListTile(
title: Text(todo.title),
trailing: Checkbox(
value: todo.isCompleted,
onChanged: (_) =>
context.read(todoNotifierProvider).toggleTodoCompletion(index),
),
);
},
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
final todoTitle = 'New Todo'; // Using a fixed todo title for demonstration
context.read(todoNotifierProvider).addTodo(todoTitle);
},
child: Icon(Icons.add),
),
);
}
}

Conclusion

Riverpod, in conjunction with StateNotifier, Freezed, and DDD principles, offers a robust solution for state management in Flutter applications. By leveraging these tools, developers can create well-structured, maintainable, and scalable apps with ease. Whether you’re a beginner or an experienced developer, Riverpod provides a comprehensive solution for managing app behavior in Flutter projects.

By mastering Riverpod, you’ll not only enhance the performance and reliability of your Flutter apps but also streamline your development process. Start integrating Riverpod into your projects today and experience the difference it makes in managing state effectively.

--

--