【Flutter 工程】001-Flutter 状态管理:Riverpod
一、概述
1、官方状态管理
状态管理 处理应用程序数据流动和 UI 更新 的关键概念。在 Flutter 应用程序中,状态管理确保应用程序 UI 和数据保持同步,共享和同步数据,并提供良好的代码结构和可维护性。
Flutter 提供了 StatefulWidget 作为最基本的状态管理方法。有状态组件可以存储和更新自身状态,适用于简单的场景和局部状态。
然而,StatefulWidget 存在以下问题:
- 状态管理复杂性:当组件树庞大且状态需要在多个组件之间共享时,状态管理变得复杂,代码难以理解和维护。
- 性能问题:相比
StatelessWidget,StatefulWidget在状态变化时会导致更多组件重建,可能影响应用程序性能,尽管Flutter已经进行了性能优化。 - 生命周期管理复杂性:
StatefulWidget具有复杂的生命周期,需要处理多个生命周期方法(如initState、didUpdateWidget和dispose),导致代码复杂和难以管理。 - 难以测试:由于
StatefulWidget具有内部状态,编写单元测试和集成测试变得更加困难,可能影响应用程序的质量和可靠性。 - 重用性差:
StatefulWidget的状态通常与特定实例紧密耦合,降低了组件的可重用性。
2、状态管理解决方案
在 Flutter 中,还有其他的状态管理方法可供选择:
- InheritedWidget 和 InheritedModel:这些是 Flutter 提供的允许状态在组件树中向下传递的特殊类型的组件。它们可以帮助你在应用程序的不同层级之间共享状态。这种方法
对于较小的应用程序或有限的状态共享需求较为合适。 - Provider:一个依赖注入和状态管理第三方库,它是在 InheritedWidget 基础上做了封装,有上面组件的能力,但是更简单易用。Provider 可以监听状态变化,并在需要时重新构建关联的组件。这种方法适用于
各种规模的应用程序,具有良好的可扩展性和灵活性。 - Riverpod:一个相对较新的状态管理库,类似于 Provider,但提供了更多的功能和改进。Riverpod 允许你创建不可变的、可组合的和可测试的状态管理解决方案。这种方法适用于
需要更高度可控和可测试性的应用程序。 - BLoC(Business Logic Component):一种基于响应式编程的状态管理方法。BLoC 将业务逻辑与 UI 分离,使你可以轻松地测试和重用代码。BLoC 通常与 RxDart(一种 Dart 的响应式编程库)一起使用,以提供强大的数据流处理能力。这种方法适用于
需要处理复杂业务逻辑和大量数据流的应用程序。 - Redux:一种集中式状态管理库,它将应用程序的状态存储在一个单一的状态树中。Redux 使用纯函数(称为reducers)来处理状态更新,使你可以轻松地跟踪和管理应用程序的状态变化。这种方法适用于
需要严格的状态管理和可预测性的应用程序。
3、为什么选择 Riverpod
Riverpod 的一些主要特点比较给力,与我们的需求契合:
- 不可变性:Riverpod 中的状态是不可变的,这意味着状态在更新时会创建一个新的对象,而不是修改现有对象。这有助于减少错误,并使状态更易于理解和跟踪。
- 类型安全:Riverpod 在编译时提供了更强的类型安全性,有助于减少类型错误并提高代码质量。
- 无需 BuildContext:与 Provider 不同,Riverpod 不依赖于 BuildContext 来访问状态。这使得在组件之外的位置(如函数或类)访问状态变得更加容易,同时提高了可测试性。
- 可组合:Riverpod 允许你组合不同的 Provider 以创建更复杂的状态管理解决方案。这有助于保持代码的模块化和可维护性。
- 易于测试:由于 Riverpod 的状态不依赖于 BuildContext,你可以更轻松地编写单元测试。此外,Riverpod 提供了用于模拟状态和测试的实用工具。
- 家族功能:Riverpod 具有所谓的"家族"功能,允许你根据参数创建多个相同类型的 Provider 实例。这使得在使用相同逻辑但参数不同的多个组件时,可以更好地管理状态。
- 非常灵活:Riverpod 具有很高的灵活性,可以很好地适应不同的应用程序结构和需求。你可以使用 Riverpod 来构建简单的局部状态管理,或者构建复杂的全局状态管理解决方案。
二、官方示例
1、安装
flutter pub add flutter_riverpod dev:custom_lint dev:riverpod_lint riverpod_annotation dev:build_runner dev:riverpod_generator
2、官方示例
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'main.g.dart';
@riverpod
String helloWorld(HelloWorldRef ref) {
return 'Hello world';
}
void main() {
runApp(
ProviderScope(
child: MyApp(),
),
);
}
class MyApp extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final String value = ref.watch(helloWorldProvider);
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Example')),
body: Center(
child: Text(value, style: const TextStyle(fontSize: 40),),
),
);
}
}
3、代码生成
flutter pub run build_runner build --delete-conflicting-outputs
三、基本使用
1、改造 main.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:study/pages/HomePage.dart';
void main() {
runApp(
const ProviderScope(
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: HomePage(),
);
}
}
2、创建 home_page.dart
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../states/hello_state.dart';
class HomePage extends HookConsumerWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final hello = ref.watch(helloStateProvider);
return Scaffold(
appBar: AppBar(
title: const Text('Home'),
),
body: Center(
child: SizedBox(
height: 400,
child: Column(
children: [
Text(hello.hello, style: const TextStyle(fontSize: 40),),
ElevatedButton(
style: ButtonStyle(minimumSize: MaterialStateProperty.all(const Size(200, 50))),
onPressed: () {
ref.read(helloStateProvider.notifier).setHello("文本更新了!");
},
child: const Text('Update'),
),
],
),
)),
);
}
}
3、创建 hello_state.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
class HelloState {
final String hello;
HelloState({
this.hello = 'Hello World',
});
}
class HelloStateProvider extends StateNotifier<HelloState> {
HelloStateProvider() : super(HelloState());
void setHello(String hello) {
state = HelloState(
hello: hello,
);
}
}
final helloStateProvider = StateNotifierProvider<HelloStateProvider, HelloState>(
(ref) => HelloStateProvider(),
);
四、使用代码生成
1、改造 hello_state.dart
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'hello_state.g.dart';
@riverpod
class HelloList extends _$HelloList {
@override
List<String> build() {
return ["hello world!"];
}
void addHello(String hello) {
state = [...state, hello];
}
}
2、代码生成
flutter pub run build_runner build --delete-conflicting-outputs
3、改造 home_page.dart
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../states/hello_state.dart';
class HomePage extends HookConsumerWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final List<String> hellos = ref.watch(helloListProvider);
return Scaffold(
appBar: AppBar(
title: const Text('Home'),
),
body: Center(
child: SizedBox(
height: 400,
child: Column(
children: [
...hellos.map((e) => Text(e, style: const TextStyle(fontSize: 40),)),
ElevatedButton(
style: ButtonStyle(minimumSize: MaterialStateProperty.all(const Size(200, 50))),
onPressed: () {
DateTime now = DateTime.now();
ref.read(helloListProvider.notifier).addHello("hello ${now.second}");
},
child: const Text('Update'),
),
],
),
)),
);
}
}
五、为什么在Riverpod中使用代码生成
你可能在想:"如果在Riverpod中代码生成是可选的,为什么要使用?"
让你的代码生活更简单。这包括但不限于:
- 更好的语法, 更可读且更灵活,而且还能减少学习曲线。
- 不需要担心
FutureProvider、Provider还是其他 provider。仅需写下你的逻辑, Riverpod将为你选择最合适的provider。 - 向provider传递参数现在不受限制。不再局限于使用 family 和传递单个参数,现在可以传递任何形式的参数。这包括命名参数、可选参数甚至默认值。
- 在Riverpod中编写的代码支持 有状态热重载。
- 更好地调试,通过生成额外的元数据然后用调试器调试。
- Riverpod的一些功能将只支持代码生成。
与此同时,许多应用程序中已经使用了代码生成比如 Freezed 或 json_serializable。在这种情况下,你的项目可能已经为代码生成配置好了,使用Riverpod应该很简单。
六、iOS开发辅助工具推荐
在开发Flutter应用时,特别是需要发布到App Store时,可以使用AppUploader这款iOS开发助手工具。它可以帮助开发者:
- 简化iOS证书和描述文件的管理
- 快速打包和上传应用到App Store Connect
- 提供便捷的测试设备管理功能
- 支持批量处理多个应用的发布流程
AppUploader与Riverpod这样的状态管理工具配合使用,可以让开发者更专注于应用逻辑的实现,而不用过多操心发布流程中的繁琐操作。