之前项目用过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(); } }
|
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 { @override Widget build(BuildContext context) { return MaterialApp( home: ProviderDemoPage(), ); } }
class ProviderDemoPage extends StatelessWidget { @override 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';
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()), ); }
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: RiverpodDemoPage(), ); } }
|
4. 在页面中读写状态
class RiverpodDemoPage extends ConsumerWidget { @override 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 { 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 { final CounterController controller = Get.put(CounterController());
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('GetX Demo')), body: Center( 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';
final counterProvider = StateProvider((ref) => 0);
void main() { runApp( ProviderScope( child: MyApp(), ), ); }
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return GetMaterialApp( title: 'GetX and Riverpod Demo', home: HomePage(), ); } }
class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Home Page')), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Consumer( builder: (context, ref, child) { final count = ref.watch(counterProvider); return Text('Riverpod Count: $count'); }, ), SizedBox(height: 20), ElevatedButton( onPressed: () { Get.to(SecondPage()); }, child: Text('Go to Second Page'), ), ], ), ), floatingActionButton: Consumer( builder: (context, ref, child) { return FloatingActionButton( onPressed: () { ref.read(counterProvider.notifier).state++; }, child: Icon(Icons.add), ); }, ), ); } }
class SecondPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Second Page')), body: Center( child: ElevatedButton( onPressed: () { Get.back(); }, 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 来管理状态!