0%

Flutter状态管理

之前项目用过provider,但是最近看到riverpod和GetX也很火,所以想对比一下这三个状态管理库。

下面我们用「计数器」这样一个简单的例子,分别展示 ProviderRiverpodGetX 在 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 {
@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';

// 一个最简单的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 {
@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

原理:一个集成路由管理、依赖注入和状态管理的多功能库。状态管理部分可以通过 GetBuilderGetxControllerObx(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());

@override
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.ofConsumer 在根部用 ProviderScope;组件中 ref.watch / ref.read 通过 GetMaterialApp 启动,并用 Get.putObxGetBuilderRx 等模式
学习曲线 上手较快,文档多;适合初中小项目 概念稍多但更灵活;更易测试、无上下文限制 功能非常多(路由、DI、状态管理),满足“全家桶”需求;入门简单,API 较多需谨慎使用
测试性 需要构建 Widget 树或 Mock Context 更好测试,可用 ProviderContainer 覆盖或模拟状态 可以直接测试控制器 (如 CounterController);mock 相对简单,但需注意路由/依赖注入部分
依赖注入 需手动声明 Provider,层层嵌套 Provider + ref.notifier 等,声明式注入 自带 Get.put()Get.lazyPut() 等依赖注入,或用 Bindings
社区生态 很多项目和教程;Flutter 官方文档也常引用 新一代状态管理,逐渐流行;作者同一人(Remi) 国外、国内社区都很火;Star 很多,但也有对“大而全”包不够精细化的争议
典型场景 小中型项目,喜欢 ChangeNotifier 简单模式 中大型项目,对可测试性、灵活依赖注入要求高 需要路由管理 + 状态管理 + 依赖注入一次搞定,或者喜欢 “命令式”的编程风格

混合使用

是的,在 Flutter 开发中,ProviderRiverpodGetX 可以根据项目需求混合使用。每个框架都有其独特的优势,适用于不同的场景。通过合理组合它们,可以发挥各自的长处,提升开发效率和代码可维护性。

混合使用的建议

  • 职责分离:在项目中混合使用多个状态管理库时,要清晰地划分每个库的职责,避免冗余。建议使用 GetX 处理局部或 UI 层的状态,而让 Riverpod 处理数据层的复杂逻辑。

  • 避免冗余的状态管理:在项目中混合使用多个状态管理库时,要清晰地划分每个库的职责,避免冗余。建议使用 GetX 处理局部或 UI 层的状态,而让 Riverpod 处理数据层的复杂逻辑。

  • 性能考虑:两者的性能都很优异,但在状态较为复杂或频繁变动时,可能需要根据实际场景选择更适合的框架。比如,GetX 更擅长处理频繁的 UI 状态更新,Riverpod 则适合异步数据处理和全局状态的维护。

  • 团队协作:在混合使用这些框架时,确保团队成员都熟悉各自的工作原理和最佳实践,以保持代码的一致性和可维护性。

示例

以下是一个将 GetXRiverpod 混合使用的简单示例:

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 {
@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: [
// 使用 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 {
@override
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 的简洁路由管理功能。

注意事项

  • 学习成本:混合使用多个框架可能增加学习成本。确保团队成员熟悉所使用框架的概念和用法,以避免开发过程中的混乱。

  • 文档和社区支持:在选择和混合使用框架时,参考官方文档和社区资源,以获取最佳实践和支持。

总的来说,ProviderRiverpodGetX 可以根据项目需求进行混合使用。关键在于明确每个框架的职责范围,合理分工,充分发挥各自优势,以实现高效的状态管理和路由控制。

什么时候用哪个?

  • Provider:如果你偏好在 Flutter 官方文档中能看到大量 Provider 示例,或已有 Provider 的使用基础,并且项目规模不算很大,Provider 就够用了。
  • Riverpod:想要更清晰的无上下文状态管理、更强的可测试性、复杂依赖注入(如网络、数据库、repo 等层层 Provider)时,推荐 Riverpod。
  • GetX:除了状态管理,还想统一管理路由、依赖注入、SnackBar 等;喜欢书写简洁、API 丰富的库,那么 GetX 是一个一站式的解决方案。

最终选择取决于团队偏好项目规模个人对不同风格的熟悉度

祝你在 Flutter 中愉快地使用 Provider、Riverpod 或 GetX 来管理状态!