Flutter状态管理
之前项目用过provider,但是最近看到riverpod和GetX也很火,所以想对比一下这三个状态管理库。
下面我们用「计数器」这样一个简单的例子,分别展示 Provider、Riverpod 和 GetX 在 Flutter 中如何做状态管理,并在最后进行对比总结。这些示例都力求最小可运行,方便你快速上手。
一、Provider
原理:基于 Flutter 内置的 InheritedWidget + ChangeNotifier
,由 Remi Rousselet 开发,是 Flutter 社区使用最广泛的状态管理方案之一。
1. 添加依赖
在 pubspec.yaml
中:
dependencies:
flutter:
sdk: flutter
provider: ^6.0.0
2. 定义一个 ChangeNotifier
(计数器示例)
import 'package:flutter/material.dart';
class Counter extends ChangeNotifier {
int _value = 0;
int get value => _value;
void increment() {
_value++;
notifyListeners(); // 通知UI刷新
}
}
3. 在根部注入 Provider
在 main.dart
:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'counter.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => Counter(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: ProviderDemoPage(),
);
}
}
class ProviderDemoPage extends StatelessWidget {
Widget build(BuildContext context) {
final counter = Provider.of<Counter>(context);
return Scaffold(
appBar: AppBar(title: Text('Provider Demo')),
body: Center(
child: Text('Value: ${counter.value}', style: TextStyle(fontSize: 24)),
),
floatingActionButton: FloatingActionButton(
onPressed: () => counter.increment(),
child: Icon(Icons.add),
),
);
}
}
- 运行后,每点击 FAB,
counter.increment()
->notifyListeners()
-> 重建Text(...)
。
二、Riverpod
原理:由 Provider 作者推出的下一代方案,不依赖 BuildContext,提供更灵活的 Provider 体系和更好的测试体验。
1. 添加依赖
dependencies:
flutter:
sdk: flutter
flutter_riverpod: ^2.3.6
2. 定义一个状态或 Provider
最简单用 StateProvider<int>
:
import 'package:flutter_riverpod/flutter_riverpod.dart';
// 一个最简单的StateProvider,初始为0
final counterProvider = StateProvider<int>((ref) => 0);
3. 在根部包裹 ProviderScope
在 main.dart
:
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'counter_provider.dart'; // 上面定义的
void main() {
runApp(
ProviderScope(child: MyApp()), // 必须有ProviderScope
);
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: RiverpodDemoPage(),
);
}
}
4. 在页面中读写状态
class RiverpodDemoPage extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider); // 读出当前值
return Scaffold(
appBar: AppBar(title: Text('Riverpod Demo')),
body: Center(
child: Text('Value: $count', style: TextStyle(fontSize: 24)),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// 改值
final notifier = ref.read(counterProvider.notifier);
notifier.state++;
},
child: Icon(Icons.add),
),
);
}
}
三、GetX
原理:一个集成路由管理、依赖注入和状态管理的多功能库。状态管理部分可以通过 GetBuilder
、GetxController
、Obx
(Rx变量) 等多种方式来实现。
1. 添加依赖
dependencies:
flutter:
sdk: flutter
get: ^4.6.5
2. 定义一个控制器 (计数器)
import 'package:get/get.dart';
class CounterController extends GetxController {
// 用一个 RxInt 管理状态
var count = 0.obs;
void increment() {
count.value++;
}
}
3. 在根部或页面中初始化控制器
GetX 常见做法:直接 Get.put()
,或者放到Bindings里,这里用最简写法:
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'counter_controller.dart';
void main() {
runApp(GetMaterialApp(
home: GetXDemoPage(),
));
}
class GetXDemoPage extends StatelessWidget {
// 通过 Get.put() 获取controller实例
final CounterController controller = Get.put(CounterController());
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('GetX Demo')),
body: Center(
// 使用 Obx 来监听变化
child: Obx(() => Text(
'Value: ${controller.count.value}',
style: TextStyle(fontSize: 24),
)),
),
floatingActionButton: FloatingActionButton(
onPressed: controller.increment,
child: Icon(Icons.add),
),
);
}
}
- 当
controller.increment()
修改了count.value
,所有使用了Obx(() => ...)
并监听count
的界面会自动刷新。
四、三者对比总结
Provider | Riverpod | GetX | |
---|---|---|---|
原理 | 基于 InheritedWidget + ChangeNotifier | 独立的 Provider 体系;无 BuildContext 依赖 | 自带路由管理、依赖注入、状态管理等一体化解决方案 |
使用方式 | 在根部用 Provider(s),子 Widget 里用 Provider.of 或 Consumer |
在根部用 ProviderScope ;组件中 ref.watch / ref.read |
通过 GetMaterialApp 启动,并用 Get.put 、Obx 、GetBuilder 、Rx 等模式 |
学习曲线 | 上手较快,文档多;适合初中小项目 | 概念稍多但更灵活;更易测试、无上下文限制 | 功能非常多(路由、DI、状态管理),满足“全家桶”需求;入门简单,API 较多需谨慎使用 |
测试性 | 需要构建 Widget 树或 Mock Context | 更好测试,可用 ProviderContainer 覆盖或模拟状态 |
可以直接测试控制器 (如 CounterController );mock 相对简单,但需注意路由/依赖注入部分 |
依赖注入 | 需手动声明 Provider ,层层嵌套 |
Provider + ref.notifier 等,声明式注入 | 自带 Get.put() 、Get.lazyPut() 等依赖注入,或用 Bindings |
社区生态 | 很多项目和教程;Flutter 官方文档也常引用 | 新一代状态管理,逐渐流行;作者同一人(Remi) | 国外、国内社区都很火;Star 很多,但也有对“大而全”包不够精细化的争议 |
典型场景 | 小中型项目,喜欢 ChangeNotifier 简单模式 |
中大型项目,对可测试性、灵活依赖注入要求高 | 需要路由管理 + 状态管理 + 依赖注入一次搞定,或者喜欢 “命令式”的编程风格 |
混合使用
是的,在 Flutter 开发中,Provider
、Riverpod
和 GetX
可以根据项目需求混合使用。每个框架都有其独特的优势,适用于不同的场景。通过合理组合它们,可以发挥各自的长处,提升开发效率和代码可维护性。
混合使用的建议:
-
职责分离:在项目中混合使用多个状态管理库时,要清晰地划分每个库的职责,避免冗余。建议使用
GetX
处理局部或 UI 层的状态,而让Riverpod
处理数据层的复杂逻辑。 -
避免冗余的状态管理:在项目中混合使用多个状态管理库时,要清晰地划分每个库的职责,避免冗余。建议使用
GetX
处理局部或 UI 层的状态,而让Riverpod
处理数据层的复杂逻辑。 -
性能考虑:两者的性能都很优异,但在状态较为复杂或频繁变动时,可能需要根据实际场景选择更适合的框架。比如,
GetX
更擅长处理频繁的 UI 状态更新,Riverpod
则适合异步数据处理和全局状态的维护。 -
团队协作:在混合使用这些框架时,确保团队成员都熟悉各自的工作原理和最佳实践,以保持代码的一致性和可维护性。
示例:
以下是一个将 GetX
和 Riverpod
混合使用的简单示例:
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
// 定义一个 Riverpod 的 StateProvider 来管理计数状态
final counterProvider = StateProvider((ref) => 0);
void main() {
runApp(
ProviderScope(
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'GetX and Riverpod Demo',
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Home Page')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 使用 Riverpod 管理的状态来更新界面
Consumer(
builder: (context, ref, child) {
final count = ref.watch(counterProvider);
return Text('Riverpod Count: $count');
},
),
SizedBox(height: 20),
// 使用 GetX 来处理路由导航
ElevatedButton(
onPressed: () {
Get.to(SecondPage());
},
child: Text('Go to Second Page'),
),
],
),
),
floatingActionButton: Consumer(
builder: (context, ref, child) {
return FloatingActionButton(
onPressed: () {
// 使用 Riverpod 更新计数状态
ref.read(counterProvider.notifier).state++;
},
child: Icon(Icons.add),
);
},
),
);
}
}
class SecondPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Second Page')),
body: Center(
child: ElevatedButton(
onPressed: () {
Get.back(); // 使用 GetX 处理返回操作
},
child: Text('Back to Home'),
),
),
);
}
}
在这个示例中,Riverpod
被用于状态管理,而 GetX
被用于路由管理。这种组合利用了 Riverpod
的类型安全和灵活性,以及 GetX
的简洁路由管理功能。
注意事项:
-
学习成本:混合使用多个框架可能增加学习成本。确保团队成员熟悉所使用框架的概念和用法,以避免开发过程中的混乱。
-
文档和社区支持:在选择和混合使用框架时,参考官方文档和社区资源,以获取最佳实践和支持。
总的来说,Provider
、Riverpod
和 GetX
可以根据项目需求进行混合使用。关键在于明确每个框架的职责范围,合理分工,充分发挥各自优势,以实现高效的状态管理和路由控制。
什么时候用哪个?
- Provider:如果你偏好在 Flutter 官方文档中能看到大量 Provider 示例,或已有 Provider 的使用基础,并且项目规模不算很大,Provider 就够用了。
- Riverpod:想要更清晰的无上下文状态管理、更强的可测试性、复杂依赖注入(如网络、数据库、repo 等层层 Provider)时,推荐 Riverpod。
- GetX:除了状态管理,还想统一管理路由、依赖注入、SnackBar 等;喜欢书写简洁、API 丰富的库,那么 GetX 是一个一站式的解决方案。
最终选择取决于团队偏好、项目规模、个人对不同风格的熟悉度。
祝你在 Flutter 中愉快地使用 Provider、Riverpod 或 GetX 来管理状态!