riverpod2.x中的 Provider 简介

原文来自:https://codewithandrea.com/articles/flutter-state-management-riverpod/

是什么

Riverpod是一个响应式缓存和数据绑定框架,由Provider软件包演化而来。根据官方文档的说法,riverpod 是把 provider 重写了,实现了原来无法实现的功能。

为什么使用 riverpod

从设计上来说,Provider是对InheritedWidget的改进,因此它依赖于小部件树。
这是一个不幸的设计决策,可能会导致常见的ProviderNotFoundException
provider_demo
另一方面,Riverpod是编译时安全的,因为所有的 provider 都被声明为全局的,可以在任何地方访问,这也意味着这些 provider 可以不依赖于widget tree 来存储应用状态和业务逻辑。由于 Riverpod 是一个反应式框架,因此只需在需要时重建providers和widgets即可。

安装

3.0 已经有 preview 版本了,但还没有出正式版,这里使用稍微旧一点的版本

1
2
dependencies:
flutter_riverpod: ^2.4.10

为了简单起见,这里没有使用hooks_riverpod,至于使用注解来生成 provier,会在后续介绍。

使用

首先我们需要改造一下程序入口,提供一个ProviderScope

1
2
3
4
5
6
7
void main() {
runApp(
ProviderScope(
child: MyApp(),
),
);
}

接着声明一个全局的Provider,和这个 main 方法平级就可以

1
2
3
final authorNameProvider = Provider<String>(
(ref) => "xuan",
);

然后改造一下MyApp,让它可以读取到数据。改造前是继承StatelessWidget,这里需要改成继承自ConsumerWidget.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class MyApp extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final String value = ref.watch(helloWorldProvider);

return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('ProviderWidget')),
body: Center(
child: Text(value),
),
),
);
}
}

可以看到,继承ConsumerWidget之后,build方法的签名变了,多了一个WidgetRef对象,我们就是使用这个对象来读取、观察、监听各种 provider。

除了继承ConsumerWidget之外,还有其他方案可供使用

使用 Consumer

在旧代码中新增使用 riverpod,但又不打算大面积重构时,可以考虑这种方案:使用Consumer包裹需要更新的控件。

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
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_widget/third_part/riverpod/riverpod_widget.dart';
import 'package:widget_with_codeview/widget_with_codeview.dart';

class ProviderWithConsumer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("ProviderWithConsumer"),
centerTitle: true,
),
body: WidgetWithCodeView(
child: Column(children: [
Text(
"有一个包含复杂布局的大型小部件类,您可以使用 Consumer 来仅重新构建依赖于提供者的小部件\n创建小而可重用的小部件有利于组合,使代码更加简洁、性能更高,更易于理解。\n如果您遵循这一原则并创建小而可重用的小部件,那么大部分时间您将自然而然地使用 ConsumerWidget。",
style: TextStyle(color: Colors.blue, fontSize: 16),
),
Consumer(builder: (_, ref, __) {
return Text(ref.read(helloWorldProvider));
})
]),
filePath: "lib/third_part/riverpod/provider_with_consumer.dart",
),
);
}
}

上述代码中的WidgetWithCodeView是三方库widget_with_codeview用来展示代码的控件。去除后没有影响。

使用ConsumerStatefulWidget

相对于ConsumerWidget来代替StatelessWidget,使用ConsumerStatefulWidget是用来代替StatefulWidget.

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
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_widget/third_part/riverpod/riverpod_widget.dart';
import 'package:widget_with_codeview/widget_with_codeview.dart';

class ProviderWithConsumerStatefulWidget extends ConsumerStatefulWidget {
@override
ConsumerState<ProviderWithConsumerStatefulWidget> createState() {
return ProviderWithConsumerStatefulWidgetState();
}
}

class ProviderWithConsumerStatefulWidgetState
extends ConsumerState<ProviderWithConsumerStatefulWidget> {
@override
void initState() {
super.initState();
// 3. if needed, we can read the provider inside initState
final helloWorld = ref.read(helloWorldProvider);
print(helloWorld); // "Hello world"
}

@override
Widget build(BuildContext context) {
// 4. use ref.watch() to get the value of the provider
final helloWorld = ref.watch(helloWorldProvider);

return Scaffold(
appBar: AppBar(
title: Text("ProviderWithConsumerStatefulWidget"),
centerTitle: true,
),
body: WidgetWithCodeView(
child: Column(
children: [
Text(
"ConsumerStatefulWidget可以用来代替StatefulWidget\n当我们从ConsumerState进行子类化时,我们可以在所有小部件生命周期方法中访问ref对象。这是因为ConsumerState声明WidgetRef作为属性,类似于Flutter的State类声明BuildContext作为可以在所有小部件生命周期方法中直接访问的属性。",
style: TextStyle(color: Colors.blue, fontSize: 16),
),
Text(helloWorld),
],
),
filePath:
"lib/third_part/riverpod/provider_with_consumerStatefulWidget.dart",
),
);
}
}

同样的需要使用ConsumerState代替原来的State.需要注意的是,在这里面我们并没有看到WidgetRef,但我们依旧可以在整个页面声明周期内访问WidgetRef对象的实例ref,这是因为ConsumerStateWidgetRef声明为属性。

WidgetRef是什么

官方文档定义WidgetRef为允许Widgets和Provider交互的对象。可以用来观察(watch)Provider 值的改变,也可以作为ConsumerConsumerWidget的参数,同样也可以作为ConsumerState的属性。
WidgetRefBuildContext做一下比较:我们可以通过BuildContext在 Widget tree 中访问祖先节点,比如Theme.of(context)MediaQuery.of(context)
WidgetRef可以让我们在 app 中访问任意provider,前提是provider被声明为全局的。这也是设计的初衷。
当然,将 provider 声明在类内部,只允许该类访问在编译、运行时也不会报错,但这会影响关注点分离的原则。

八中不同的 Provider

  • Provider
  • StateProvider (被NotifierProvider代替)
  • StateNotifierProvider (被NotifierProvider代替)
  • FutureProvider
  • StreamProvider
  • ChangeNotifierProvider (被NotifierProvider代替)
  • NotifierProvider (Riverpod 2.0新增)
  • AsyncNotifierProvider (Riverpod 2.0新增)

以后会提到使用riverpod_generator包中的@riverpod注解来代替我们的手动声明provider

Provider

上面示例中的代码就是用的这一个,更适合提供一些不会变的常量值,比如 Dio 实例、版本号、格式化对象、数据库对象等

1
2
3
4
5
6
7
8
9
10
11
12
13
final dateFormatterProvider = Provider<DateFormat>((ref) {
return DateFormat.MMMEd();
});

class SomeWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
// retrieve the formatter
final formatter = ref.watch(dateFormatterProvider);
// use it
return Text(formatter.format(DateTime.now()));
}
}

StateProvider

可以用来存储一些简单的变量,比如计数器

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
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

final globalStateProvider = StateProvider((ref) => 1);
class StateProviderDemoWidget extends ConsumerWidget {
//不推荐
final localStateProvider = StateProvider((ref) => 1);

@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(
title: Text("StateProviderDemoWidget"),
centerTitle: true,
),
body: Center(
child: Flex(
direction: Axis.vertical,
children: [
Text("globalStateProvider count ${ref.watch(globalStateProvider)}"),
Text("localStateProvider count ${ref.watch(localStateProvider)}"),
Text(
"StateProvider.state标记为过时,并将在3.0移除",
style: TextStyle(color: Colors.red, fontSize: 14),
),
Text("声明为全局,则状态一直被保存,直到应用重启。",
style: TextStyle(color: Colors.blue, fontSize: 14)),
Text("声明为类成员,则页面重启则重新初始化。",
style: TextStyle(color: Colors.blue, fontSize: 14)),
Text("StateProvider适用于存储简单的状态变量,如枚举、字符串、布尔值和数字",
style: TextStyle(color: Colors.blue, fontSize: 14)),
IconButton(
icon: Icon(Icons.plus_one_sharp),
onPressed: () {
ref.read(globalStateProvider.notifier).state++;
ref.read(localStateProvider.notifier).state++;
},
),
],
)),
);
}
}

这里有一些有意思的事情。我们声明了两个 Provider,一个是全局的,一个是类内部的。
当我们打开页面点击几次按钮之后,页面内容更新,这时候我们返回上一页再次打开这个页面,会发现globalStateProvider提供的值还是上一次返回时候的值,而localStateProvider提供的值是初始值。因为localStateProvider在页面关闭时就被销毁了,这种情况似乎更符合我们没有使用 riverpod 的习惯。这里可以使用 autoDispose 让globalStateProvider在所有 watcher 被销毁也可以重置。

StateNotifierProvider

可以用它来监听和导出一个 StateNotifier ,StateNotifierProviderStateNotifier 非常适合管理因事件或用户交互而改变的状态。这里有一个 todolist示例。
我们先定义一个 todo 的对象,实现一个 copyWith 方法来复制当前对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@immutable
class Todo {
const Todo(
{required this.id, required this.description, required this.completed});

final String id;
final String description;
final bool completed;
Todo copyWith({String? id, String? description, bool? completed}) {
return Todo(
id: id ?? this.id,
description: description ?? this.description,
completed: completed ?? this.completed,
);
}
}

然后我们继承StateNotifier实现自己的TodosNotifier类,并完善所需逻辑

  1. 统计已完成的待办
  2. 添加待办
  3. 删除待办
  4. 完成、取消完成待办
  5. 全选,反全选
  6. 是否选中全部
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

class TodosNotifier extends StateNotifier<List<Todo>> {
TodosNotifier() : super([]);

void addTodo(Todo todo) {
state = [...state, todo];
}

int completedCount() {
int count = 0;
for (final todo in state) {
if (todo.completed) {
count++;
}
}
return count;
}

void removeTodo(String todoId) {
state = [
for (final todo in state)
if (todo.id != todoId) todo,
];
}

void toggle(String todoID) {
state = [
for (final todo in state)
if (todo.id == todoID)
todo.copyWith(completed: !todo.completed)
else
todo
];
}

void selectAll() {
state = [for (final todo in state) todo.copyWith(completed: true)];
}

void unSelectAll() {
state = [for (final todo in state) todo.copyWith(completed: false)];
}

bool isSelectAll() {
bool result = true;
for (final todo in state) {
if (!todo.completed) {
result = false;
break;
}
}
return result;
}
}

接着声明一个全局的StateNotifierProvider

1
2
final todoProvider =
StateNotifierProvider<TodosNotifier, List<Todo>>((ref) => TodosNotifier());

最后我们就可以在页面中使用了

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
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class StateNotifierProviderWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
List<Todo> todos = ref.watch(todoProvider);
return Scaffold(
appBar: AppBar(
title: Text("State Notifier Provider Widget"),
centerTitle: true,
),
body: Column(
children: [
Text("StateNotifierProvider和StateNotifier非常适合管理可能会因事件或用户交互而发生变化的状态。"),
Text("注意StateNotifier中通知更新时是比较的对象内存地址"),
Divider(),
Container(
child: Text(
"共${todos.length.toString()},完成-》${ref.read(todoProvider.notifier).completedCount()}"),
),
CheckboxListTile(
value: ref.read(todoProvider.notifier).isSelectAll(),
onChanged: (value) {
if (value ?? false) {
ref.read(todoProvider.notifier).selectAll();
} else {
ref.read(todoProvider.notifier).unSelectAll();
}
},
title: Text("全选"),
),
Expanded(
child: ListView(
shrinkWrap: true,
children: [
for (final todo in todos)
Dismissible(
key: Key(todo.id),
child: CheckboxListTile(
value: todo.completed,
onChanged: (value) =>
ref.read(todoProvider.notifier).toggle(todo.id),
title: Text(todo.description),
),
onDismissed: (direction) =>
{ref.read(todoProvider.notifier).removeTodo(todo.id)},
),
],
)),
],
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
final notifier = ref.read(todoProvider.notifier);
notifier.addTodo(Todo(
id: DateTime.now().microsecond.toString(),
description: "${DateTime.now().microsecondsSinceEpoch}",
completed: false));
},
),
);
}
}

这样我们就完成了一个简易版的待办页面

state_notifier_demo

FutureProvider

我们经常会遇到一些耗时操作,比如请求服务器接口获取到数据后展示在页面上;比如读取配置文件等。这种情况我们就需要 FutureProvider了。
这里以读取 assets 中的配置文件为例:
首先声明一个FutureProvider泛型中填入返回值,使用rootBundle.loadString来读取资源文件的内容。

1
2
3
4
5
6
7
final getConfigFutureProvider =
FutureProvider<Map<String, dynamic>>((ref) async {
String configContant =
await rootBundle.loadString("assets/fake_configurations.json");
final configMap = jsonDecode(configContant) as Map<String, dynamic>;
return configMap;
});

然后再声明一个用来模拟请求服务端的FutureProvider

1
2
3
4
5
final wordPairProvider = FutureProvider<String>((ref)  async{
final wordPair = generateWordPairs().first;
await Future.delayed(Duration(seconds: 2));
return "${wordPair.first} - ${wordPair.second}";
});

注意这里的generateWordPairs是三方包english_words: ^4.0.0提供的。在wordPairProvider中我们延迟 2 秒返回了结果,用以模拟网络耗时。
在页面中我们可以这么使用:

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

class FutureProviderWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final AsyncValue<Map<String, dynamic>> getConfigResult =
ref.watch(getConfigFutureProvider);
final AsyncValue<String> wordPairResult = ref.watch(wordPairProvider);
return Scaffold(
appBar: AppBar(
title: Text("FutureProvider"),
centerTitle: true,
),
body: RefreshIndicator(
onRefresh: () => ref.refresh(wordPairProvider.future),
child: ListView(
children: [
getConfigResult.when(
data: (data) {
return Text(data.toString());
},
error: (error, stack) {
return Text(error.toString());
},
loading: () => const CircularProgressIndicator()),
wordPairResult.when(data: (data) {
return Text(data);
}, error: (error, stack) {
return ElevatedButton(
onPressed: () {
ref.refresh(wordPairProvider.future);
},
child: Text("点击刷新"));
}, loading: () {
return CircularProgressIndicator();
}),
],
),
),
);
}
}

注意,ref.watch(FutureProvider)的返回值是AsyncValue类型,我们可以使用模式匹配的方式来更新UI。这里会有三个状态:data,loading,error。
我们在页面上添加了一个下拉刷新的控价来刷新模拟网络请求的 provider:ref.refresh(wordPairProvider.future)

  • FutureProvider功能非常强大,可以用它来
  • 执行和缓存异步操作(如网络请求)
  • 处理异步操作的错误和加载状态
  • 将多个异步值合并为另一个值
  • 重新获取和刷新数据(适用于拉到刷新操作)
    如果退出页面需要重新进行刷新,我们可以配合autoDispose使用:FutureProvider.autoDispose<String>((ref) async {}

StreamProvider

这里例子不大好写,它和 FutureProvider 类似,被 watch 时返回的也是 AsyncValue 类型,这里是一个计时器的示例。
同样的,先声明一个StreamProvider

1
2
3
4
5
6
7
8
9
10
final streamProvider = StreamProvider.autoDispose<int>((ref) {
ref.onDispose(() {
debugPrint("streamProvider autoDispose");
});

return Stream.periodic(Duration(seconds: 1), (number) {
return number + 1;
});
});

然后在页面中使用

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

class StreamProviderWidget extends ConsumerWidget {

@override
Widget build(BuildContext context, WidgetRef ref) {
final AsyncValue<int> streamResult = ref.watch(streamProvider);
return Scaffold(
appBar: AppBar(
title: Text("StreamProviderWidget"),
centerTitle: true,
),
body: Column(
children: [
Text(streamResult.toString()),
streamResult.when(
data: (data) {
return Text(data.toString());
},
error: (error, stack) {
return Text(error.toString());
},
loading: () => CircularProgressIndicator())
],
),
);
}
}

ChangeNotifierProvider

建议使用 NotifierProvider 代替。
这里是一个计数器示例。

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

class CounterNotifier extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // 当计数器值改变时通知所有监听者
}
}

// 定义ChangeNotifierProvider
final counterProvider = ChangeNotifierProvider<CounterNotifier>((ref) {
return CounterNotifier();
});

class ChangeNotifierProviderWidget extends ConsumerWidget{
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(title: Text('ChangeNotifierProviderWidget')),
body: Center(
child: Consumer(builder: (context, ref, _) { // 使用Consumer读取并显示状态
final counter = ref.watch(counterProvider);
return Text('Count: ${counter.count}');
}),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// 在点击按钮时,通过ref调用notifier的方法更新状态
ref.read(counterProvider.notifier).increment();
},
child: Icon(Icons.add),
),
);
}
}

NotifierProvider

用来代替 StateProvider 和 ChangeNotifierProvider。
同样的,我们先定义一个数据类 Student

1
2
3
4
5
6
7
8
9
10
11
12
class Student {
Student({required this.name, required this.id});
String name;
String id;

Student copyWith({String? copyID, String? copyName}) {
return Student(name: copyName ?? this.name, id: copyID ?? this.id);
}
String toString(){
return "$id : $name";
}
}

定义一个继承自 Notifier 的类

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

class StudentNotifier extends Notifier<Student> {
@override
Student build() {
return generateStudent();
}

Student generateStudent() {
final wordPair = generateWordPairs().first;
final id = Random().nextInt(100);

return Student(
name: "${wordPair.first} - ${wordPair.second}", id: id.toString());
}

String toString() {
return "${state.id} : ${state.name}";
}

void change() {
state = generateStudent();
}

void changeNameWithCopy(String name) {
state = state.copyWith(copyName: name);
}

void justChangeName(String name) {
state.name = name;
}

void changeIdWithCopy(String id) {
state = state.copyWith(copyID: id);
}

void justChangeId(String id) {
state.id = id;
}
}

注意,这里的 build 方法是必须的。同时为了解释刷新页面的条件,这里定义了几个改变属性的方法。xxxWithCopy是重新生成了对象,justxxx 则是在原对象上直接修改属性值。
然后声明NotifierProvider,这里提供了两种方式,哪一种都可以。

1
2
3
4
5
final counterProvider = NotifierProvider<StudentNotifier, Student>(() {
return StudentNotifier();
});
final counterProviderOther =
NotifierProvider<StudentNotifier, Student>(StudentNotifier.new);

在页面中我们这样使用

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

class CounterNotifierWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final student = ref.watch(counterProvider);
ref.listen(counterProvider, (oldValue, newValue) {
logger.d(
"oldValue--> ${oldValue?.id.toString()} \n newValue--> ${newValue.id}");
});

return Scaffold(
appBar: AppBar(
title: Text("CounterNotifierWidget"),
centerTitle: true,
),
body: Column(
children: [
Text("student ${student.id.toString()} : ${student.name} --> ${student.toString()}"),

ElevatedButton(
onPressed: () {
ref
.read(counterProvider.notifier)
.changeIdWithCopy(Random().nextInt(100).toString());
},
child: Text("changeIdWithCopy")),
ElevatedButton(
onPressed: () {
final wordPair = generateWordPairs().first;
ref.read(counterProvider.notifier).changeNameWithCopy(
"${wordPair.first} - -${wordPair.second}");
},
child: Text("changeNameWithCopy")),

ElevatedButton(
onPressed: () {
ref
.read(counterProvider.notifier)
.justChangeId(Random().nextInt(100).toString());
},
child: Text("justChangeId")),
ElevatedButton(
onPressed: () {
final wordPair = generateWordPairs().first;
ref
.read(counterProvider.notifier)
.justChangeName("${wordPair.first} - -${wordPair.second}");
},
child: Text("justChangeName")),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
ref.read(counterProvider.notifier).change();
},
child: Icon(Icons.change_circle),
),
);
}
}

运行之后可以看到,只有当我们调用xxxwithCopy时才会刷新页面。戳到NotifierProviderElement源码中查看updateShouldNotify方法,发现是调用的identical(previous, next)方法来做判断的。

1
2
3
4
@protected
bool updateShouldNotify(State previous, State next) {
return !identical(previous, next);
}

AsyncNotifierProvider

相比NotifierProvider,它是异步而已.
还是先声明一个数据类Student

1
2
3
4
5
6
7
8
9
class Student{
Student({required this.name,required this.id});
String name;
String id;

String toString(){
return "$id - $name";
}
}

声明一个继承自AsyncNotifier的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

class StudentAsyncNotifier extends AsyncNotifier<Student>{
@override
FutureOr<Student> build() async {

return getInfo();
}

Future<Student> getInfo()async{

await Future.delayed(Duration(seconds: 3));
final wordPair = generateWordPairs().first;
return Student(name: "${wordPair.first} - ${wordPair.second}", id: Random().nextInt(100).toString());

}

Future<void> refresh() async{
state = AsyncValue.loading();
state = await AsyncValue.guard(()async {
return getInfo();
});
}
}

声明一个 Provider

1
2
3
final studentAsyncNotifier = AsyncNotifierProvider<StudentAsyncNotifier, Student>(() {
return StudentAsyncNotifier();
});

在页面中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

class AsyncNotifierProviderWidget extends ConsumerWidget{
@override
Widget build(BuildContext context, WidgetRef ref) {

final asyncValue = ref.watch(studentAsyncNotifier);

return Scaffold(
appBar: AppBar(title: Text("AsyncNotifierProviderWidget"),centerTitle: false,),
body: asyncValue.when(data: (data){
return Center(child: Text(data .toString()),);
}, error: (error,stack){
return Center(child: Text(error.toString()),);
}, loading: (){
return Center(child: CircularProgressIndicator());
}),
floatingActionButton: FloatingActionButton(onPressed: (){
ref.read(studentAsyncNotifier.notifier).refresh();
},child: Icon(Icons.refresh),),
);
}
}

这里需要注意的是:使用AsyncNotifier与autoDispose的正确方式是继承 AutoDisposeAsyncNotifier类而不是使用 autoDispose 修饰符。

以上就是 riverpod 中常见的 Provider 的用法示例,后面会介绍一些修饰符、注解相关的信息。


以上


riverpod2.x中的 Provider 简介
https://blog.huangyuanlove.com/2024/03/27/riverpod2-x中的-Provider-简介/
作者
HuangYuan_xuan
发布于
2024年3月27日
许可协议
BY HUANG兄