Back to Rules
Flutter92% popularity

Flutter State Management with Riverpod

Use Riverpod for reactive state management with compile-time safety and testable provider architecture.

Remi RousseletUpdated Mar 10, 2024

Overview

Riverpod represents the evolution of state management in Flutter, addressing many shortcomings of previous solutions like Provider, Bloc, and GetX. Its core innovation is the concept of providers as first-class values, enabling compile-time safety and excellent IDE support through code generation or the provider package's built-in syntax. The architecture promotes unidirectional data flow where state flows down from providers to widgets, and events flow up through callbacks. This separation creates a clear mental model that scales from simple local state to complex global application state. Providers are inherently composable, allowing complex state logic to be built from simpler building blocks through provider composition. Unlike Provider which relies on InheritedWidget, Riverpod manages its own widget-style registry that works across the entire widget tree. This approach solves the "provider not found" issues that plague Provider implementations and enables features like testing providers in complete isolation. The ref.invalidate() method allows manual state refresh without creating new provider instances, while auto-dispose handles memory management for providers that should not persist when no longer needed. Code generation with riverpod_generator brings additional benefits including automatic annotation processing, optimized provider code generation, and integration with freezed for immutable state classes. The generated code maintains full type safety while reducing boilerplate significantly. For production applications, this combination of Riverpod with code generation represents the current best practice for Flutter state management.

Code Example

.flutterrules
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import '../models/todo.dart';
import '../repositories/todo_repository.dart';

part 'todo_provider.freezed.dart';

@freezed
class TodoState with _$TodoState {
  const factory TodoState({
    @Default([]) List<Todo> todos,
    @Default(AsyncValue.loading()) AsyncValue<void> loadingState,
    String? errorMessage,
  }) = _TodoState;
}

class TodoNotifier extends StateNotifier<TodoState> {
  final TodoRepository _repository;

  TodoNotifier(this._repository) : super(const TodoState()) {
    loadTodos();
  }

  Future<void> loadTodos() async {
    state = state.copyWith(loadingState: const AsyncValue.loading());
    try {
      final todos = await _repository.getTodos();
      state = state.copyWith(todos: todos, loadingState: const AsyncValue.data(null));
    } catch (e, st) {
      state = state.copyWith(
        loadingState: AsyncValue.error(e, st),
        errorMessage: e.toString(),
      );
    }
  }

  Future<void> addTodo(Todo todo) async {
    await _repository.addTodo(todo);
    await loadTodos();
  }
}

final todoRepositoryProvider = Provider<TodoRepository>((ref) {
  return TodoRepository();
});

final todoProvider = StateNotifierProvider<TodoNotifier, TodoState>((ref) {
  return TodoNotifier(ref.watch(todoRepositoryProvider));
});

More Flutter Rules

FLUTTER
90%

Flutter Clean Architecture Layers

Structure Flutter applications with domain, data, and presentation layers for maintainability.

flutterclean-architecturedomain-driven
// Domain Layer - Entities
class Article {
  final String id;
  final String title;
  final String content;
  final String authorId;
  final DateTime ...
Jan 20, 2024by Reso Coder
View Rule
FLUTTER
88%

Flutter BLoC Pattern Implementation

Implement BLoC for predictable state management with clear event-state separation.

flutterblocstate-management
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:equatable/equatable.dart...
Feb 18, 2024by Felix Angelov
View Rule
FLUTTER
87%

Flutter Navigation with GoRouter

Implement declarative routing with GoRouter for deep linking and nested navigation.

fluttergorouternavigation
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import '....
Feb 22, 2024by Chris Cox
View Rule