盘点主流 Flutter 状态管理库2024
视频
https://www.bilibili.com/video/BV1U6421M7dw/
前言
原文 https://ducafecat.com/blog/flutter-state-management-libraries-2024
状态管理是每个应用不可缺少的,本文将会盘点下主流的状态管理包。
对了 我还对插件包做了整理,欢迎移步查看 https://flutter.ducafecat.com .
状态管理作用
- 数据共享和同步:在应用程序中,不同部分可能需要共享和同步数据。通过状态管理,可以轻松地在应用程序的各个部分之间共享数据,并确保数据的一致性。
- UI更新:Flutter状态管理可以帮助开发者管理应用程序中的UI状态,以便在数据变化时更新用户界面。这样可以确保应用程序的UI与数据的状态保持同步。
- 复杂状态管理:随着应用程序变得越来越复杂,管理应用程序的状态变得更加困难。Flutter状态管理工具可以帮助开发者更有效地管理应用程序的状态,使代码更具可维护性和可扩展性。
- 性能优化:有效的状态管理可以帮助应用程序避免不必要的重绘和重新构建,从而提高应用程序的性能和响应速度。
- 代码结构:通过良好的状态管理,开发者可以更好地组织应用程序的代码结构,使其更易于理解和维护。
考量纬度
- 编写代码的复杂度,是否简洁
- 适用规模(小型、中大型、参与人数、迭代频率)
- 开发团队的前端组件化经验
- 项目是否快速开发,MVP
Provider
轻量级、易学习、内置于Flutter中,适用于基本状态管理。
https://pub.dev/packages/provider
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
/// This is a reimplementation of the default Flutter application using provider + [ChangeNotifier].
void main() {
runApp(
/// Providers are above [MyApp] instead of inside it, so that tests
/// can use [MyApp] while mocking the providers
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => Counter()),
],
child: const MyApp(),
),
);
}
/// Mix-in [DiagnosticableTreeMixin] to have access to [debugFillProperties] for the devtool
// ignore: prefer_mixin
class Counter with ChangeNotifier, DiagnosticableTreeMixin {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
/// Makes `Counter` readable inside the devtools by listing all of its properties
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(IntProperty('count', count));
}
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Example'),
),
body: const Center(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('You have pushed the button this many times:'),
/// Extracted as a separate widget for performance optimization.
/// As a separate widget, it will rebuild independently from [MyHomePage].
///
/// This is totally optional (and rarely needed).
/// Similarly, we could also use [Consumer] or [Selector].
Count(),
],
),
),
floatingActionButton: FloatingActionButton(
key: const Key('increment_floatingActionButton'),
/// Calls `context.read` instead of `context.watch` so that it does not rebuild
/// when [Counter] changes.
onPressed: () => context.read<Counter>().increment(),
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
class Count extends StatelessWidget {
const Count({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Text(
/// Calls `context.watch` to make [Count] rebuild when [Counter] changes.
'${context.watch<Counter>().count}',
key: const Key('counterState'),
style: Theme.of(context).textTheme.headlineMedium,
);
}
}
BLoC/Cubit:
基于块模式构建,将业务逻辑与用户界面解耦,适用于复杂的状态管理。
https://pub.dev/packages/flutter_bloc
- Bloc 架构
https://bloclibrary.dev/#/zh-cn/coreconcepts?id=bloc
- 快手上手文档
https://bloclibrary.dev/#/zh-cn/gettingstarted
- 计数器 项目结构
├── lib
│ ├── app.dart
│ ├── counter
│ │ ├── counter.dart
│ │ ├── cubit
│ │ │ └── counter_cubit.dart
│ │ └── view
│ │ ├── counter_page.dart
│ │ └── counter_view.dart
│ ├── counter_observer.dart
│ └── main.dart
├── pubspec.lock
├── pubspec.yaml
这个应用中我们使用的是功能驱动(feature-driven)的项目结构。
这种项目结构可以让我们通过一个个独立的功能来扩展项目。
- login登录 项目结构
Riverpod
适用于中大型应用程序的具有类型、作用域和关注点分离的改进版提供程序。
https://pub.dev/packages/riverpod
https://riverpod.dev/zh-hans/docs/introduction/getting_started
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'main.g.dart';
// 我们创建了一个 "provider",它可以存储一个值(这里是 "Hello world")。
// 通过使用提供者程序,这可以允许我们模拟或者覆盖一个暴露的值。
@riverpod
String helloWorld(HelloWorldRef ref) {
return 'Hello world';
}
void main() {
runApp(
// 为了使小组件可以读取提供者程序,
// 我们需要将整个应用程序包装在“ProviderScope”小部件中。
// 这是我们的提供者程序的状态将被存储的地方。
ProviderScope(
child: MyApp(),
),
);
}
// 继承父类使用 ConsumerWidget 替代 StatelessWidget,这样可以获取到提供者程序的引用
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),
),
),
);
}
}
GetX
全能框架,用于状态管理、路由和依赖注入,非常适用于低到中等复杂度的应用程序。
优点
- 简单易用:
- GetX提供了简洁而直观的API,使得状态管理和导航等功能变得非常容易实现。
- 开发者可以通过少量的代码实现复杂的功能,提高开发效率。
- 性能优秀:
- GetX被设计为高性能的状态管理库,具有出色的性能表现。
- GetX使用响应式编程和观察者模式,可以确保只有在数据变化时才会触发UI更新,从而提高应用程序的性能。
- 依赖注入:
- GetX内置了依赖注入功能,可以方便地管理应用程序中的依赖关系。
- 通过GetX的依赖注入功能,开发者可以更好地组织和管理应用程序的代码。
- 路由管理:
- GetX提供了强大的路由管理功能,支持命名路由、动画过渡等。
- 开发者可以轻松地管理应用程序的导航逻辑,实现页面之间的切换和传递参数。
- 轻量级:
- GetX是一个轻量级的库,不会给应用程序增加过多的负担。
- 使用GetX可以避免引入过多的依赖,使应用程序保持简洁和高效。
示例
订阅更新
void main() => runApp(MaterialApp(home: Home()));
class Home extends StatelessWidget {
var count = 0.obs;
@override
Widget build(context) => Scaffold(
appBar: AppBar(title: Text("counter")),
body: Center(
child: Obx(() => Text("$count")),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () => count ++,
));
}
手动更新
@override
Widget build(BuildContext context) {
return GetBuilder<MatchIndexController>(
init: MatchIndexController(fixtureId),
id: "match_index",
tag: tag, // 区分不同控制器
builder: (_) {
return controller.fixturesInfo == null
? const ProgressIndicatorWidget().paddingTop(100)
: _buildView();
},
);
}
_initData() async {
await _loadData();
update(["match_index"]);
}
官方脚手架
https://github.com/jonataslaw/get_cli/blob/master/README-zh_CN.md
猫哥 vsc 插件
https://marketplace.visualstudio.com/items?itemName=ducafecat.getx-template
getx 实战视频+代码模版
https://ducafecat.com/course/flutter-woo
MobX
MobX是一种状态管理库,它让应用程序的响应式数据与 UI 关联起来变得很简单。
https://pub.dev/packages/flutter_mobx
https://github.com/mobxjs/mobx.dart/tree/master/mobx_examples/lib/counter
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
part 'counter.g.dart';
class Counter = CounterBase with _$Counter;
abstract class CounterBase with Store {
@observable
int value = 0;
@action
void increment() {
value++;
}
}
class CounterExample extends StatefulWidget {
const CounterExample({Key key}) : super(key: key);
@override
_CounterExampleState createState() => _CounterExampleState();
}
class _CounterExampleState extends State<CounterExample> {
final _counter = Counter();
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: const Text('Counter'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Observer(
builder: (_) => Text(
'${_counter.value}',
style: const TextStyle(fontSize: 20),
)),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _counter.increment,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
监视变化
import 'package:mobx/mobx.dart';
final greeting = Observable('Hello World');
final dispose = reaction((_) => greeting.value, (msg) => print(msg));
greeting.value = 'Hello MobX'; // Cause a change
// Done with the reaction()
dispose();
// Prints:
// Hello MobX
Redux
具有集中式存储、操作和减速器的可预测状态管理,适用于需要强大可预测性和工具的应用程序。
https://pub.dev/packages/flutter_redux
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:redux/redux.dart';
// One simple action: Increment
enum Actions { Increment }
// The reducer, which takes the previous count and increments it in response
// to an Increment action.
int counterReducer(int state, dynamic action) {
return action == Actions.Increment ? state + 1 : state;
}
void main() {
// Create your store as a final variable in the main function or inside a
// State object. This works better with Hot Reload than creating it directly
// in the `build` function.
final store = Store<int>(counterReducer, initialState: 0);
runApp(FlutterReduxApp(
title: 'Flutter Redux Demo',
store: store,
));
}
class FlutterReduxApp extends StatelessWidget {
final Store<int> store;
final String title;
FlutterReduxApp({Key key, this.store, this.title}) : super(key: key);
@override
Widget build(BuildContext context) {
// The StoreProvider should wrap your MaterialApp or WidgetsApp. This will
// ensure all routes have access to the store.
return StoreProvider<int>(
// Pass the store to the StoreProvider. Any ancestor `StoreConnector`
// Widgets will find and use this value as the `Store`.
store: store,
child: MaterialApp(
theme: ThemeData.dark(),
title: title,
home: Scaffold(
appBar: AppBar(title: Text(title)),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Connect the Store to a Text Widget that renders the current
// count.
//
// We'll wrap the Text Widget in a `StoreConnector` Widget. The
// `StoreConnector` will find the `Store` from the nearest
// `StoreProvider` ancestor, convert it into a String of the
// latest count, and pass that String to the `builder` function
// as the `count`.
//
// Every time the button is tapped, an action is dispatched and
// run through the reducer. After the reducer updates the state,
// the Widget will be automatically rebuilt with the latest
// count. No need to manually manage subscriptions or Streams!
StoreConnector<int, String>(
converter: (store) => store.state.toString(),
builder: (context, count) {
return Text(
'The button has been pushed this many times: $count',
style: Theme.of(context).textTheme.display1,
);
},
)
],
),
),
// Connect the Store to a FloatingActionButton. In this case, we'll
// use the Store to build a callback that will dispatch an Increment
// Action.
//
// Then, we'll pass this callback to the button's `onPressed` handler.
floatingActionButton: StoreConnector<int, VoidCallback>(
converter: (store) {
// Return a `VoidCallback`, which is a fancy name for a function
// with no parameters and no return value.
// It only dispatches an Increment action.
return () => store.dispatch(Actions.Increment);
},
builder: (context, callback) {
return FloatingActionButton(
// Attach the `callback` to the `onPressed` attribute
onPressed: callback,
tooltip: 'Increment',
child: Icon(Icons.add),
);
},
),
),
),
);
}
}
Fish-Redux
一个针对Flutter优化的Redux版本,减少样板代码,同时保持架构概念。
https://pub.dev/packages/fish_redux
GetIt
这是一个依赖管理工具,当然也能用来作为简单的状态管理,比如全局、局部的数据维护。
https://pub.dev/packages/get_it
我专门做一个视频可以移步看下。
视频 https://www.bilibili.com/video/BV1EG411X7zz
文档 https://ducafecat.com/blog/use-get_it-in-getx
小结
每个库都有其优点和缺点,适用于不同的应用程序大小和复杂性。对于基本应用程序,推荐使用Provider和GetX,而对于较大的应用程序,可以从BLoC/Cubit、Riverpod或MobX中受益。在选择库时,请考虑您的应用程序需求、团队偏好和架构风格。
总之,强大的状态管理对于应用的稳定性和增长至关重要。Flutter提供了一系列的库来满足不同的需求,确保稳定性、可测试性和可维护性。无论你是初创公司还是已经建立起来的企业,选择合适的状态管理库对于成功至关重要。
感谢阅读本文
如果有什么建议,请在评论中让我知道。我很乐意改进。
flutter 学习路径
- Flutter 优秀插件推荐 https://flutter.ducafecat.com
- Flutter 基础篇1 - Dart 语言学习 https://ducafecat.com/course/dart-learn
- Flutter 基础篇2 - 快速上手
https://ducafecat.com/course/flutter-quickstart-learn - Flutter 实战1 - Getx Woo 电商APP
https://ducafecat.com/course/flutter-woo - Flutter 实战2 - 上架指南 Apple Store、Google Play https://ducafecat.com/course/flutter-upload-apple-google
- Flutter 基础篇3 - 仿微信朋友圈 https://ducafecat.com/course/flutter-wechat
- Flutter 实战3 - 腾讯 tim 即时通讯开发 https://ducafecat.com/course/flutter-tim
© 猫哥 ducafecat.com
end