State Management and Architecture Patterns

Local widget state InheritedWidget / InheritedNotifier Built-in dependency propagation

ChangeNotifier + ListenableBuilder / ValueNotifier Simple observable state

DI and state propagation over InheritedWidget Compile-safe providers, async state (AsyncValue), code generation, testable, no BuildContext needed Bloc / Cubit

Event-driven state with streams, predictable, great tooling (flutter_bloc)

GetX, MobX, Redux, signals Layered/clean architecture (presentation → domain → data), or feature-first folder structure

The repository pattern, dependency injection (get_it + injectable, Riverpod, constructor injection)

Immutable state (freezed), unidirectional data flow The Flutter team's official architecture guide recommends MVVM with repositories and services

Plain Dart backends/CLIs use the same principles: layered services, interfaces (abstract classes), DI, and pure domain logic.

// Riverpod example (with codegen: riverpod_annotation)
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'todos.g.dart';

@riverpod
class Todos extends _$Todos {
  @override
  List<String> build() => [];
  void add(String title) => state = [...state, title];
}

@riverpod
Future<List<Post>> posts(Ref ref) async => ref.watch(apiClientProvider).getPosts();
// Bloc/Cubit example
import 'package:flutter_bloc/flutter_bloc.dart';
class CounterCubit extends Cubit<int> {
  CounterCubit() : super(0);
  void increment() => emit(state + 1);
}