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);
}