创建项目

在指定文件夹创建一个新的 Flutter 项目, 如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
PS D:\sources\flutter_repos> flutter create my_example_app
Flutter assets will be downloaded from https://storage.flutter-io.cn. Make sure you trust this source!
Creating project my_example_app...
Running "flutter pub get" in my_example_app...
Resolving dependencies in my_example_app...
+ async 2.10.0 (2.11.0 available)
+ boolean_selector 2.1.1
+ characters 1.2.1 (1.3.0 available)
+ clock 1.1.1
+ collection 1.17.0 (1.17.1 available)
+ cupertino_icons 1.0.5
+ fake_async 1.3.1
+ flutter 0.0.0 from sdk flutter
+ flutter_lints 2.0.1
+ flutter_test 0.0.0 from sdk flutter
+ js 0.6.5 (0.6.7 available)
+ lints 2.0.1
+ matcher 0.12.13 (0.12.15 available)
+ material_color_utilities 0.2.0 (0.3.0 available)
+ meta 1.8.0 (1.9.1 available)
+ path 1.8.2 (1.8.3 available)
+ sky_engine 0.0.99 from sdk flutter
+ source_span 1.9.1
+ stack_trace 1.11.0
+ stream_channel 2.1.1
+ string_scanner 1.2.0
+ term_glyph 1.2.1
+ test_api 0.4.16 (0.5.0 available)
+ vector_math 2.1.4
Changed 24 dependencies in my_example_app!
Wrote 127 files.

All done!
You can find general documentation for Flutter at: https://docs.flutter.dev/
Detailed API documentation is available at: https://api.flutter.dev/
If you prefer video documentation, consider: https://www.youtube.com/c/flutterdev

In order to run your application, type:

$ cd my_example_app
$ flutter run

Your application code is in my_example_app\lib\main.dart.

Visual Studio Code 中打开创建好的my_example_app项目

添加依赖

Riverpod 有三种方式, 可以根据下图进行选择:

这里不使用 flutter_hooks, 所以 选择flutter_riverpod

flutter_riverpod 的最新版本: https://pub.flutter-io.cn/packages/flutter_riverpod

pubspec.yaml 中添加引用:

1
2
3
4
5
6
7
8
9
dependencies:
flutter:
sdk: flutter


cupertino_icons: ^1.0.2

# 状态管理, 建议使用最新版本
flutter_riverpod: ^2.3.2

使用基础

ProviderRiverpod 应用程序中最重要的部分。 Provider 是一个对象,它 封装 了一些 状态 并允许 监听状态

要使 Provider 程序正常工作,必须在 Flutter根应用程序 中添加 ProviderScope, 代码如下所示:

1
2
3
4
5
6
7
8
9
10
void main() {
runApp(
// 为了能让 widgets 能读取 providers,
// 需要将整个应用程序包装在 "ProviderScope" 中
// 这是 provider 存储状态的地方
const ProviderScope(
child: MyApp(),
),
);
}

不同类型的 Providers

对于多个不同的用例,有多种类型的 providers

由于所有这些 providers 都可用,有时很难理解何时使用一种类型的 provider 而不是另一种。以下表格列出了适合于不同使用场景的 provider

Provider 类型 Provider 创建功能 使用示例
Provider 返回任何类型 服务类/计算属性(过滤列表)
StateProvider 返回任何类型 过滤条件/简单状态对象
FutureProvider 返回任意类型的 Future 来自 API 调用的返回值
StreamProvider 返回任何类型的 Stream 来自 API 的 Stream 返回值
StateNotifierProvider 返回 StateNotifier 的子类 一个复杂的状态对象,只能通过接口改变的状态的值
ChangeNotifierProvider 返回 ChangeNotifier 的子类 需要可变性的复杂状态对象

警告:
虽然所有的 providers 都有存在的目的,但对于可扩展的应用程序来说,不建议使用 ChangeNotifierProviders,因为它存在可变状态的问题。它存在于 flutter_riverpod 包中,以提供一个简单的从包:package:provider,并允许一些 flutter 特定的使用情况,如与一些 Navigator 2 包的集成。

provider

Provider是所有Providers中最基本的。它返回了一个Value… 仅此而已。

Provider通常用于下面的场景:

  • 缓存计算后的值

  • 将一个值暴露给其他Provider(比如Repository/HttpClient)

  • 提供了一个可供测试的覆写Provider

  • 通过不使用select,来减少Provider/widget的重建

使用 RiverpodProvider 需要三步就可以。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

// 1.创建一个全局的provider,里面储存“Hello World!”
final Provider<String> helloWorldProvider = Provider((_) => 'Hello World!');

void main() {
runApp(
// 2.添加“ProviderScope”。所有使用Riverpod的Flutter程序都必须
// 在widget tree的根部添加它,用来储存各个provider。
ProviderScope(
child: MyApp(),
),
);
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Riverpod Example',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: ProviderExample(),
);
}
}

// 3.使用“ConsumerWidget”,在“build”中获取对应的provider
class ProviderExample extends ConsumerWidget {

@override
Widget build(BuildContext context, WidgetRef ref) {
// ref.watch() 获得一个 Provider 的值并监听变化
final String value = ref.watch(helloWorldProvider);

return Scaffold(
appBar: AppBar(title: const Text('Example')),
body: Center(
child: Text(value),
),
);
}
}

这里储存 Hello World! 使用的是Provider,它提供一个永远不变的对象。不过大部分场景下状态都是可变的,下面用计数器来举例。

StateProvider

StateProvider 是一个公开了修改其状态的方法的Provider。它是StateNotifierProvider的简化版,旨在避免为非常简单的用例编写一个StateNotifier类。

StateProvider的存在主要是为了允许用户对简单的变量进行修改。一个StateProvider所维护的状态通常是下面几种。

  • 一个枚举,比如一个filter,用来做筛选

  • 一个字符串,通常是一些固定的文本,可以借助family关键字来做Switch

  • 一个布尔值,用于checkbox这类的状态切换

  • 一个数字,用于分页或者PagerIndex

而下面这些场景,就不适合使用StateProvider:

  • 你的状态中包含对校验逻辑

  • 你的状态是一个复杂的对象,比如一个自定义类,一个ListMap

  • 状态的修改逻辑比较复杂

定义一个全局 StateProvider

1
final StateProvider<int> counterProvider = StateProvider((ref) => 0);

使用ConsumerWidget代替StatelessWidget

Flutter Riverpod 使用ConsumerWidget代替StatelessWidget

ConsumerWidget在使用上与StatelessWidget相同,唯一的区别是它的构建方法上有一个额外的参数:"ref "对象。

1
2
3
4
5
6
7
// ConsumerWidget: riverpod 无状态组件
class MyHomePage extends ConsumerWidget {
const MyHomePage({super.key});

@override
// WidgetRef ref: 获取状态的变量
Widget build(BuildContext context, WidgetRef ref) {

监听及更新状态

1
2
3
4
5
// 监听状态
final count = ref.watch(counterProvider);

// 更新状态
ref.read(counterProvider.notifier).state++;

完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

final StateProvider<int> counterProvider = StateProvider((ref) => 0);

void main() {
runApp(
// 为了能让 widgets 能读取 providers,
// 需要将整个应用程序包装在 "ProviderScope" 中
// 这是 provider 存储状态的地方
const ProviderScope(
child: MyApp(),
),
);
}

class MyApp extends StatelessWidget {
const MyApp({super.key});

// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}

// ConsumerWidget: riverpod 无状态组件
class MyHomePage extends ConsumerWidget {
const MyHomePage({super.key});

@override
// WidgetRef ref: 获取状态的变量
Widget build(BuildContext context, WidgetRef ref) {
// ref.watch() 获得一个 Provider 的值并监听变化
final count = ref.watch(counterProvider);
return Scaffold(
appBar: AppBar(
title: const Text('StateProvider Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$count', // 使用状态变量
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
// 使用 ref.read() 在不监听的情况下获取Provider的状态, 或更新状态
onPressed: () => ref.read(counterProvider.notifier).state++,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}