Flutter 生态中有多种成熟的第三方状态管理框架,各有侧重。本节介绍 GetX、Riverpod 和 Bloc/Cubit 三种主流方案。
一、GetX
GetX 是功能最全面的一体化框架,集状态管理、路由、依赖注入于一身,以极简代码著称。
安装
yaml
dependencies:
get: ^4.6.6
1.1 依赖注入:Get.put / Get.find
dart
// Get.put:立即注册并创建实例
void main() {
Get.put(ApiService());
Get.put(UserController());
runApp(const MyApp());
}
// Get.lazyPut:懒加载,第一次使用时才创建
Get.lazyPut<ProductController>(() => ProductController());
// Get.find:在任意地方获取已注册的实例
final userController = Get.find<UserController>();
// 永久保留(不随页面关闭销毁)
Get.put(AppConfigController(), permanent: true);
// 标签区分多个同类实例
Get.put(CartController(), tag: 'main_cart');
final cart = Get.find<CartController>(tag: 'main_cart');
1.2 GetxController:控制器封装
dart
class UserController extends GetxController {
// 响应式变量(.obs)
final RxString name = ''.obs;
final RxBool isLoggedIn = false.obs;
final Rx<User?> currentUser = Rx<User?>(null);
final RxList<Order> orders = <Order>[].obs;
@override
void onInit() {
super.onInit();
// 初始化时执行
loadUserData();
}
@override
void onClose() {
// 控制器销毁时清理
super.onClose();
}
Future<void> loadUserData() async {
try {
final user = await ApiService.fetchUser();
currentUser.value = user;
name.value = user.name;
isLoggedIn.value = true;
} catch (e) {
print('Load user error: $e');
}
}
Future<void> logout() async {
await ApiService.logout();
currentUser.value = null;
isLoggedIn.value = false;
orders.clear();
Get.offAllNamed('/login'); // 清空路由栈跳转登录
}
}
1.3 Obx 响应式监听
dart
class UserProfilePage extends StatelessWidget {
final controller = Get.find<UserController>();
@override
Widget build(BuildContext context) {
return Scaffold(
body: Obx(() {
// 只有响应式变量(.obs)变化时,Obx 内部才重建
if (!controller.isLoggedIn.value) {
return const LoginPrompt();
}
return Column(
children: [
Text('Welcome, ${controller.name.value}'),
// 列表响应式:增删改都会触发重建
Obx(() => ListView.builder(
itemCount: controller.orders.length,
itemBuilder: (_, i) => OrderTile(order: controller.orders[i]),
)),
],
);
}),
floatingActionButton: FloatingActionButton(
onPressed: controller.logout,
child: const Icon(Icons.logout),
),
);
}
}
1.4 GetBuilder(非响应式,手动更新)
dart
class CounterController extends GetxController {
int count = 0; // 普通变量(非 .obs)
void increment() {
count++;
update(); // 手动触发 GetBuilder 重建
}
}
GetBuilder<CounterController>(
builder: (controller) => Text('${controller.count}'),
)
二、Riverpod(推荐)
Riverpod 是 Provider 的进化版,编译期安全、可测试性极强,是当前 Flutter 社区最推荐的状态管理方案。
安装
yaml
dependencies:
flutter_riverpod: ^2.5.1
riverpod_annotation: ^2.3.4
dev_dependencies:
riverpod_generator: ^2.3.10
build_runner: ^2.4.0
2.1 ProviderScope
dart
void main() {
runApp(
const ProviderScope( // ⚠️ 必须包裹根 Widget
child: MyApp(),
),
);
}
2.2 使用 @riverpod 注解(推荐代码生成)
dart
// user_provider.dart
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'user_provider.g.dart'; // 代码生成文件
// 同步 Provider
@riverpod
String greeting(GreetingRef ref) => 'Hello, Flutter!';
// 异步 Provider
@riverpod
Future<User> currentUser(CurrentUserRef ref) async {
return await ApiService.fetchCurrentUser();
}
// 带参数的 Provider(family)
@riverpod
Future<Product> productDetail(ProductDetailRef ref, int productId) async {
return await ApiService.fetchProduct(productId);
}
// 运行代码生成
// dart run build_runner build
2.3 StateNotifier / AsyncNotifier
dart
// 定义状态类
@immutable
class CartState {
final List<CartItem> items;
final bool isLoading;
const CartState({this.items = const [], this.isLoading = false});
CartState copyWith({List<CartItem>? items, bool? isLoading}) =>
CartState(items: items ?? this.items, isLoading: isLoading ?? this.isLoading);
double get totalPrice =>
items.fold(0, (sum, item) => sum + item.price * item.quantity);
}
// AsyncNotifier:适合有异步初始化的状态
@riverpod
class CartNotifier extends _$CartNotifier {
@override
Future<CartState> build() async {
// build() 是初始化逻辑
final savedItems = await CartRepository.loadSavedCart();
return CartState(items: savedItems);
}
Future<void> addItem(Product product) async {
final current = await future; // 等待当前状态
state = AsyncData(current.copyWith(
items: [...current.items, CartItem.fromProduct(product)],
));
await CartRepository.saveCart(state.value!.items);
}
Future<void> removeItem(String id) async {
state = state.when(
data: (cart) => AsyncData(
cart.copyWith(items: cart.items.where((i) => i.id != id).toList()),
),
loading: () => const AsyncLoading(),
error: (e, s) => AsyncError(e, s),
);
}
}
2.4 在 Widget 中消费 Provider
dart
// 使用 ConsumerWidget 替代 StatelessWidget
class CartPage extends ConsumerWidget {
const CartPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
// 监听异步状态
final cartAsync = ref.watch(cartNotifierProvider);
return cartAsync.when(
loading: () => const CircularProgressIndicator(),
error: (e, _) => Text('Error: $e'),
data: (cart) => Column(
children: [
CartItemList(items: cart.items),
Text('总计:¥${cart.totalPrice.toStringAsFixed(2)}'),
ElevatedButton(
onPressed: () => ref.read(cartNotifierProvider.notifier).clear(),
child: const Text('清空购物车'),
),
],
),
);
}
}
// 使用 ConsumerStatefulWidget 替代 StatefulWidget
class SearchPage extends ConsumerStatefulWidget {
@override
ConsumerState<SearchPage> createState() => _SearchPageState();
}
class _SearchPageState extends ConsumerState<SearchPage> {
late TextEditingController _searchController;
@override
void initState() {
super.initState();
_searchController = TextEditingController();
}
@override
Widget build(BuildContext context) {
// 使用 ref.watch 监听
final query = ref.watch(searchQueryProvider);
final results = ref.watch(searchResultsProvider(query));
return Column(
children: [
TextField(
controller: _searchController,
onChanged: (q) => ref.read(searchQueryProvider.notifier).state = q,
),
results.when(
data: (list) => SearchResults(results: list),
loading: () => const CircularProgressIndicator(),
error: (e, _) => const SizedBox.shrink(),
),
],
);
}
}
三、Bloc / Cubit
Bloc 是事件驱动的状态管理框架,强调明确的数据流,适合大型复杂业务。
安装
yaml
dependencies:
flutter_bloc: ^8.1.5
3.1 Cubit(简化版 Bloc)
dart
// Cubit 直接暴露方法,无需事件类
class CounterCubit extends Cubit<int> {
CounterCubit() : super(0); // 初始状态
void increment() => emit(state + 1);
void decrement() => emit(state > 0 ? state - 1 : 0);
void reset() => emit(0);
}
// 使用
BlocProvider(
create: (_) => CounterCubit(),
child: BlocBuilder<CounterCubit, int>(
builder: (context, count) {
return Text('$count');
},
),
)
3.2 Bloc(完整事件驱动)
dart
// 1. 定义事件
abstract class AuthEvent {}
class LoginRequested extends AuthEvent {
final String email;
final String password;
LoginRequested({required this.email, required this.password});
}
class LogoutRequested extends AuthEvent {}
class TokenRefreshRequested extends AuthEvent {}
// 2. 定义状态
abstract class AuthState {}
class AuthInitial extends AuthState {}
class AuthLoading extends AuthState {}
class AuthAuthenticated extends AuthState {
final User user;
AuthAuthenticated(this.user);
}
class AuthUnauthenticated extends AuthState {}
class AuthError extends AuthState {
final String message;
AuthError(this.message);
}
// 3. 实现 Bloc
class AuthBloc extends Bloc<AuthEvent, AuthState> {
final AuthRepository _authRepository;
AuthBloc(this._authRepository) : super(AuthInitial()) {
on<LoginRequested>(_onLoginRequested);
on<LogoutRequested>(_onLogoutRequested);
on<TokenRefreshRequested>(_onTokenRefresh);
}
Future<void> _onLoginRequested(
LoginRequested event,
Emitter<AuthState> emit,
) async {
emit(AuthLoading());
try {
final user = await _authRepository.login(
email: event.email,
password: event.password,
);
emit(AuthAuthenticated(user));
} on AuthException catch (e) {
emit(AuthError(e.message));
} catch (e) {
emit(AuthError('Network error'));
}
}
Future<void> _onLogoutRequested(
LogoutRequested event,
Emitter<AuthState> emit,
) async {
await _authRepository.logout();
emit(AuthUnauthenticated());
}
Future<void> _onTokenRefresh(
TokenRefreshRequested event,
Emitter<AuthState> emit,
) async {
// 静默刷新 token
try {
await _authRepository.refreshToken();
} catch (_) {
emit(AuthUnauthenticated());
}
}
}
3.3 BlocProvider / BlocBuilder / BlocListener
dart
// 提供 Bloc
BlocProvider(
create: (context) => AuthBloc(context.read<AuthRepository>()),
child: const LoginPage(),
)
// BlocBuilder:根据状态构建 UI
BlocBuilder<AuthBloc, AuthState>(
// buildWhen:减少不必要的重建
buildWhen: (previous, current) => previous.runtimeType != current.runtimeType,
builder: (context, state) {
return switch (state) {
AuthLoading() => const CircularProgressIndicator(),
AuthAuthenticated(:final user) => UserDashboard(user: user),
AuthUnauthenticated() => const LoginPage(),
AuthError(:final message) => ErrorWidget(message: message),
_ => const SizedBox.shrink(),
};
},
)
// BlocListener:监听状态变化执行副作用(导航、弹窗等)
BlocListener<AuthBloc, AuthState>(
listener: (context, state) {
if (state is AuthAuthenticated) {
Navigator.pushReplacementNamed(context, '/home');
} else if (state is AuthError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(state.message)),
);
}
},
child: const LoginForm(),
)
// BlocConsumer = BlocBuilder + BlocListener
BlocConsumer<AuthBloc, AuthState>(
listener: (context, state) {
// 副作用
},
builder: (context, state) {
return const LoginForm(); // UI
},
)
// 触发事件
context.read<AuthBloc>().add(LoginRequested(
email: _email,
password: _password,
));
四、方案选型建议
| 场景 | 推荐方案 |
|---|---|
| 快速原型 / 小项目 | GetX |
| 中大型项目 / 现代化架构 | Riverpod |
| 企业级 / 严格分层 / 多人协作 | Bloc |
| Flutter 官方风格 | Provider |
👉 下一节:3.4 状态同步与生命周期管理
3.3 第三方框架
Flutter 生态中有多种成熟的第三方状态管理框架,各有侧重。本节介绍 GetX、Riverpod 和 Bloc/Cubit 三种主流方案。
一、GetX
GetX 是功能最全面的一体化框架,集状态管理、路由、依赖注入于一身,以极简代码著称。
安装
yaml
dependencies:
get: ^4.6.6
1.1 依赖注入:Get.put / Get.find
dart
// Get.put:立即注册并创建实例
void main() {
Get.put(ApiService());
Get.put(UserController());
runApp(const MyApp());
}
// Get.lazyPut:懒加载,第一次使用时才创建
Get.lazyPut<ProductController>(() => ProductController());
// Get.find:在任意地方获取已注册的实例
final userController = Get.find<UserController>();
// 永久保留(不随页面关闭销毁)
Get.put(AppConfigController(), permanent: true);
// 标签区分多个同类实例
Get.put(CartController(), tag: 'main_cart');
final cart = Get.find<CartController>(tag: 'main_cart');
1.2 GetxController:控制器封装
dart
class UserController extends GetxController {
// 响应式变量(.obs)
final RxString name = ''.obs;
final RxBool isLoggedIn = false.obs;
final Rx<User?> currentUser = Rx<User?>(null);
final RxList<Order> orders = <Order>[].obs;
@override
void onInit() {
super.onInit();
// 初始化时执行
loadUserData();
}
@override
void onClose() {
// 控制器销毁时清理
super.onClose();
}
Future<void> loadUserData() async {
try {
final user = await ApiService.fetchUser();
currentUser.value = user;
name.value = user.name;
isLoggedIn.value = true;
} catch (e) {
print('Load user error: $e');
}
}
Future<void> logout() async {
await ApiService.logout();
currentUser.value = null;
isLoggedIn.value = false;
orders.clear();
Get.offAllNamed('/login'); // 清空路由栈跳转登录
}
}
1.3 Obx 响应式监听
dart
class UserProfilePage extends StatelessWidget {
final controller = Get.find<UserController>();
@override
Widget build(BuildContext context) {
return Scaffold(
body: Obx(() {
// 只有响应式变量(.obs)变化时,Obx 内部才重建
if (!controller.isLoggedIn.value) {
return const LoginPrompt();
}
return Column(
children: [
Text('Welcome, ${controller.name.value}'),
// 列表响应式:增删改都会触发重建
Obx(() => ListView.builder(
itemCount: controller.orders.length,
itemBuilder: (_, i) => OrderTile(order: controller.orders[i]),
)),
],
);
}),
floatingActionButton: FloatingActionButton(
onPressed: controller.logout,
child: const Icon(Icons.logout),
),
);
}
}
1.4 GetBuilder(非响应式,手动更新)
dart
class CounterController extends GetxController {
int count = 0; // 普通变量(非 .obs)
void increment() {
count++;
update(); // 手动触发 GetBuilder 重建
}
}
GetBuilder<CounterController>(
builder: (controller) => Text('${controller.count}'),
)
二、Riverpod(推荐)
Riverpod 是 Provider 的进化版,编译期安全、可测试性极强,是当前 Flutter 社区最推荐的状态管理方案。
安装
yaml
dependencies:
flutter_riverpod: ^2.5.1
riverpod_annotation: ^2.3.4
dev_dependencies:
riverpod_generator: ^2.3.10
build_runner: ^2.4.0
2.1 ProviderScope
dart
void main() {
runApp(
const ProviderScope( // ⚠️ 必须包裹根 Widget
child: MyApp(),
),
);
}
2.2 使用 @riverpod 注解(推荐代码生成)
dart
// user_provider.dart
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'user_provider.g.dart'; // 代码生成文件
// 同步 Provider
@riverpod
String greeting(GreetingRef ref) => 'Hello, Flutter!';
// 异步 Provider
@riverpod
Future<User> currentUser(CurrentUserRef ref) async {
return await ApiService.fetchCurrentUser();
}
// 带参数的 Provider(family)
@riverpod
Future<Product> productDetail(ProductDetailRef ref, int productId) async {
return await ApiService.fetchProduct(productId);
}
// 运行代码生成
// dart run build_runner build
2.3 StateNotifier / AsyncNotifier
dart
// 定义状态类
@immutable
class CartState {
final List<CartItem> items;
final bool isLoading;
const CartState({this.items = const [], this.isLoading = false});
CartState copyWith({List<CartItem>? items, bool? isLoading}) =>
CartState(items: items ?? this.items, isLoading: isLoading ?? this.isLoading);
double get totalPrice =>
items.fold(0, (sum, item) => sum + item.price * item.quantity);
}
// AsyncNotifier:适合有异步初始化的状态
@riverpod
class CartNotifier extends _$CartNotifier {
@override
Future<CartState> build() async {
// build() 是初始化逻辑
final savedItems = await CartRepository.loadSavedCart();
return CartState(items: savedItems);
}
Future<void> addItem(Product product) async {
final current = await future; // 等待当前状态
state = AsyncData(current.copyWith(
items: [...current.items, CartItem.fromProduct(product)],
));
await CartRepository.saveCart(state.value!.items);
}
Future<void> removeItem(String id) async {
state = state.when(
data: (cart) => AsyncData(
cart.copyWith(items: cart.items.where((i) => i.id != id).toList()),
),
loading: () => const AsyncLoading(),
error: (e, s) => AsyncError(e, s),
);
}
}
2.4 在 Widget 中消费 Provider
dart
// 使用 ConsumerWidget 替代 StatelessWidget
class CartPage extends ConsumerWidget {
const CartPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
// 监听异步状态
final cartAsync = ref.watch(cartNotifierProvider);
return cartAsync.when(
loading: () => const CircularProgressIndicator(),
error: (e, _) => Text('Error: $e'),
data: (cart) => Column(
children: [
CartItemList(items: cart.items),
Text('总计:¥${cart.totalPrice.toStringAsFixed(2)}'),
ElevatedButton(
onPressed: () => ref.read(cartNotifierProvider.notifier).clear(),
child: const Text('清空购物车'),
),
],
),
);
}
}
// 使用 ConsumerStatefulWidget 替代 StatefulWidget
class SearchPage extends ConsumerStatefulWidget {
@override
ConsumerState<SearchPage> createState() => _SearchPageState();
}
class _SearchPageState extends ConsumerState<SearchPage> {
late TextEditingController _searchController;
@override
void initState() {
super.initState();
_searchController = TextEditingController();
}
@override
Widget build(BuildContext context) {
// 使用 ref.watch 监听
final query = ref.watch(searchQueryProvider);
final results = ref.watch(searchResultsProvider(query));
return Column(
children: [
TextField(
controller: _searchController,
onChanged: (q) => ref.read(searchQueryProvider.notifier).state = q,
),
results.when(
data: (list) => SearchResults(results: list),
loading: () => const CircularProgressIndicator(),
error: (e, _) => const SizedBox.shrink(),
),
],
);
}
}
三、Bloc / Cubit
Bloc 是事件驱动的状态管理框架,强调明确的数据流,适合大型复杂业务。
安装
yaml
dependencies:
flutter_bloc: ^8.1.5
3.1 Cubit(简化版 Bloc)
dart
// Cubit 直接暴露方法,无需事件类
class CounterCubit extends Cubit<int> {
CounterCubit() : super(0); // 初始状态
void increment() => emit(state + 1);
void decrement() => emit(state > 0 ? state - 1 : 0);
void reset() => emit(0);
}
// 使用
BlocProvider(
create: (_) => CounterCubit(),
child: BlocBuilder<CounterCubit, int>(
builder: (context, count) {
return Text('$count');
},
),
)
3.2 Bloc(完整事件驱动)
dart
// 1. 定义事件
abstract class AuthEvent {}
class LoginRequested extends AuthEvent {
final String email;
final String password;
LoginRequested({required this.email, required this.password});
}
class LogoutRequested extends AuthEvent {}
class TokenRefreshRequested extends AuthEvent {}
// 2. 定义状态
abstract class AuthState {}
class AuthInitial extends AuthState {}
class AuthLoading extends AuthState {}
class AuthAuthenticated extends AuthState {
final User user;
AuthAuthenticated(this.user);
}
class AuthUnauthenticated extends AuthState {}
class AuthError extends AuthState {
final String message;
AuthError(this.message);
}
// 3. 实现 Bloc
class AuthBloc extends Bloc<AuthEvent, AuthState> {
final AuthRepository _authRepository;
AuthBloc(this._authRepository) : super(AuthInitial()) {
on<LoginRequested>(_onLoginRequested);
on<LogoutRequested>(_onLogoutRequested);
on<TokenRefreshRequested>(_onTokenRefresh);
}
Future<void> _onLoginRequested(
LoginRequested event,
Emitter<AuthState> emit,
) async {
emit(AuthLoading());
try {
final user = await _authRepository.login(
email: event.email,
password: event.password,
);
emit(AuthAuthenticated(user));
} on AuthException catch (e) {
emit(AuthError(e.message));
} catch (e) {
emit(AuthError('Network error'));
}
}
Future<void> _onLogoutRequested(
LogoutRequested event,
Emitter<AuthState> emit,
) async {
await _authRepository.logout();
emit(AuthUnauthenticated());
}
Future<void> _onTokenRefresh(
TokenRefreshRequested event,
Emitter<AuthState> emit,
) async {
// 静默刷新 token
try {
await _authRepository.refreshToken();
} catch (_) {
emit(AuthUnauthenticated());
}
}
}
3.3 BlocProvider / BlocBuilder / BlocListener
dart
// 提供 Bloc
BlocProvider(
create: (context) => AuthBloc(context.read<AuthRepository>()),
child: const LoginPage(),
)
// BlocBuilder:根据状态构建 UI
BlocBuilder<AuthBloc, AuthState>(
// buildWhen:减少不必要的重建
buildWhen: (previous, current) => previous.runtimeType != current.runtimeType,
builder: (context, state) {
return switch (state) {
AuthLoading() => const CircularProgressIndicator(),
AuthAuthenticated(:final user) => UserDashboard(user: user),
AuthUnauthenticated() => const LoginPage(),
AuthError(:final message) => ErrorWidget(message: message),
_ => const SizedBox.shrink(),
};
},
)
// BlocListener:监听状态变化执行副作用(导航、弹窗等)
BlocListener<AuthBloc, AuthState>(
listener: (context, state) {
if (state is AuthAuthenticated) {
Navigator.pushReplacementNamed(context, '/home');
} else if (state is AuthError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(state.message)),
);
}
},
child: const LoginForm(),
)
// BlocConsumer = BlocBuilder + BlocListener
BlocConsumer<AuthBloc, AuthState>(
listener: (context, state) {
// 副作用
},
builder: (context, state) {
return const LoginForm(); // UI
},
)
// 触发事件
context.read<AuthBloc>().add(LoginRequested(
email: _email,
password: _password,
));
四、方案选型建议
| 场景 | 推荐方案 |
|---|---|
| 快速原型 / 小项目 | GetX |
| 中大型项目 / 现代化架构 | Riverpod |
| 企业级 / 严格分层 / 多人协作 | Bloc |
| Flutter 官方风格 | Provider |
👉 下一节:3.4 状态同步与生命周期管理