flutter_riverpod: ^2.6.1 应用笔记
flutter_riverpod是一个反应式缓存和数据绑定的框架,Riverpod 提供了下述这些不同类型的 Provider,以满足不同的需求:
- Provider :只存储 不可变 的值或对象,最简单的状态提供者,只对外提供访问状态值的接口,外部无法对状态值进行修改。
- FutureProvider :处理 异步操作 ,如:从网络请求数据数据,它会再Future完成时通知其观察者。通常与 autoDispose 修饰符一起使用。
- StreamProvider :处理 基于流的异步数据,监听一个Stream,并在新数据到达前通知其观察者。
基本使用
- 添加依赖
yaml
dependencies:
flutter:
sdk: flutter
# Riverpod核心库
flutter_riverpod: ^2.5.1
# Riverpod注解
riverpod_annotation: ^2.3.5
dev_dependencies:
# Dart代码生成文件
build_runner:
# 为Dart和Flutter项目自定义lint规则,Lint有助于捕获潜在错误,并强制执行一致的编程风格
custom_lint:
# Riverpod代码生成器
riverpod_generator: ^2.4.0
# 专为Riverpod设计的一套lint规则,有助于再使用Riverpod时执行最佳实践
riverpod_lint: ^2.3.10
- 实现计数功能
-
创建一个状态提供者,StateProvider会观察一个值,并再改变时得到通知
-
用Riverpod 的 Provider 必须用 ProviderScope 包裹MyApp!!!。
-
通过ref.watch() 来监听Provider的值,当Provider的值改变时,会自动刷新UI。
-
获取Provider的通知器修改状态值(自增)。`
lessimport 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; //创建一个状态提供者,StateProvider会观察一个值,并再改变时得到通知 final clickCountProvider = StateProvider<int>((ref) => 0); void main() { // 想使用Riverpod 的 Provider 必须用 ProviderScope 包裹MyApp! runApp(const ProviderScope(child: MyApp())); } //继承ConsumerWidget,它是可以提供监听Provider的Widget class MyApp extends ConsumerWidget { const MyApp({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { // 通过ref.watch() 来监听Provider的值,当Provider的值改变时,会自动刷新UI final clickCount = ref.watch(clickCountProvider); return MaterialApp( home: Scaffold( appBar: AppBar(title: const Text('Riverpod Demo')), body: Center( child: Column( children: [ //第一种方式 Text('点击了 $clickCount 次'), Builder( builder: (context) => ElevatedButton( onPressed: () { Navigator.push( context, MaterialPageRoute(builder: (context) => const ContPage()), ); }, child: Text("跳转到计数项目"), ), ), ], ), ), ), ); } }
在main.dart文件中创建了一个状态提供者并通过StateProvider观察这个值,当观察的这个值改变时刷新ui。点击按钮跳转到计数页面,在计数页面点击按钮实现数字加一的操作。计数页面代码如下:
dart
class ContPage extends ConsumerWidget {
const ContPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
//第一种方式
final int count = ref.watch(clickCountProvider);
return Scaffold(
appBar: AppBar(title: const Text('增加计数')),
body: Center(
child: Column(
children: [
//第一种方式
Text("点击计数$count"),
Builder(
builder: (context) => ElevatedButton(
onPressed: () {
//第一种方式 获取Provider的通知器修改状态值(自增)
ref.read(clickCountProvider.notifier).state++;
},
child: const Text("点击增加"),
),
),
],
),
),
);
}
}
这样就实现了一个简单的计数功能。

-
使用NotifierProvider管理业务状态和业务逻辑实现计数功能
scalaclass ClickCountProviderNotifier extends Notifier<int> { //如何修改状态的逻辑 void increment() { // state 表示当前状态 state++; } // 重写此方法返回Notifier的初始状态 @override int build() => 0; } final clickCountProviderNotifier = NotifierProvider<ClickCountProviderNotifier, int>( () => ClickCountProviderNotifier(), );
lessclass NotifierIncrement extends ConsumerWidget { const NotifierIncrement({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { // 监听 ClickCountProviderNotifier 的状态 final count = ref.watch(clickCountProviderNotifierProvider); return Scaffold( appBar: AppBar(title: const Text('NotifierIncrement')), body: Center( child: Column( children: [ Text("数字:$count"), Builder( builder: (context) => ElevatedButton( onPressed: () { ref .read(clickCountProviderNotifierProvider.notifier) .increment(); }, child: Text("点击增加计数"), ), ), ], ) ); } }
-
使用riverpod实现计数功能
获取WidgetRef的方式 ,除了上面继承 ConsumerWidget ,直接通过它的 build() 获取外,还可以:
- 继承 ConsumerStatefulWidget ,通过它的 State.build() 获取。
- 使用 Consumer/ConsumerBuilder 包裹需要使用 ref 的 Widget ,在 builder() 中获取。
在这里使用了ConsumerBuilder获取WidgetRef。
scala@riverpod class ClickCountProviderNotifier extends _$ClickCountProviderNotifier { @override int build() => 0; void increment() { state++; } }
打开终端 ,执行 flutter pub run build_runner build 生成对应的Provider代码,也可以执行 flutter pub run build_runner watch 监听相关文件改动触发代码文件的重新生成。
- flutter pub run build_runner build只执行一次。
- flutter pub run build_runner watch。当代码改变时以监听的方式自动执行。
less
//定义的ConsumerBuilder
ConsumerBuilder consumerBuilder() {
return (BuildContext context, WidgetRef ref, Widget? child) {
// 监听 ClickCountProviderNotifier 的状态
final count = ref.watch(clickCountProviderNotifierProvider);
return Column(
children: [
Text("数字:$count"),
Builder(
builder: (context) => ElevatedButton(
onPressed: () {
ref.read(clickCountProviderNotifierProvider.notifier).increment();
},
child: Text("点击增加计数"),
),
),
],
);
};
}
// ConsumerBuilder
class NotifierIncrement extends StatelessWidget {
const NotifierIncrement({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('NotifierIncrement')),
body: Center(child: Consumer(builder: consumerBuilder())),
);
}
}
使用Provider发起你的第一个请求
dart
class User {
final String id;
final String username;
final String nickname;
final DateTime createAt;
final DateTime updateAt;
const User({
required this.id,
required this.username,
required this.nickname,
required this.createAt,
required this.updateAt,
});
factory User.fromJson(Map<String, dynamic> json) => User(
id: json['id'],
username: json['username'],
nickname: json['nickname'],
createAt: DateTime.fromMillisecondsSinceEpoch(json['createAt'] as int),
updateAt: DateTime.fromMillisecondsSinceEpoch(json['updateAt'] as int),
);
}
scala
// 基于函数式的方式
// @riverpod
// Future<User> fetchUser(Ref ref) async {
// final response = await Dio().get(
// 'https://mock.presstime.cn/mock/68807bdbf037b14c6b75f6a2/example/user/1',
// );
//
// return User.fromJson(response.data['data']);
// }
//基于类的使用方式
@riverpod
class fetchUser extends _$fetchUser {
@override
FutureOr<User> build() async {
final response = await Dio().get(
'https://mock.presstime.cn/mock/68807bdbf037b14c6b75f6a2/example/user/1',
);
return User.fromJson(response.data['data']);
}
}
这里的实现方式任选一种即可。
less
class MyWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final userAsync = ref.watch(fetchUserProvider);
return Scaffold(
appBar: AppBar(title: Text("FutureDemo")),
body: userAsync.when(
data: (user) => Text('用户: ${user.nickname}'),
loading: () => CircularProgressIndicator(),
error: (error, stack) => Text('错误: $error'),
),
);
}
}
使用代码生成定义提供者程序时,记住以下几点会很有帮助:
-
提供者程序可以定义为带注释的函数或 带注释的类。它们几乎相同, 但基于类的提供者程序的优点是包含公共方法,使 外部对象能够修改提供者程序的状态(副作用)。 函数提供者程序是用于编写基于类的提供者程序的语法糖,只有
build
方法, 因此不能由 UI 修改。 -
支持所有 Dart 异步原语(Future、FutureOr 和 Stream)。
-
当函数被标记为async时, 提供者程序会自动处理错误/加载状态并公开 AsyncValue。这里引用了官方文档的内容主要为了帮助自己理解:官方文档地址
函数式的 基于类的 同步的 @riverpod String example(Ref ref) { return 'foo'; } @riverpod class Example extends _$Example { @override String build() { return 'foo'; } // Add methods to mutate the state } 异步的 - Future @riverpod Future example(Ref ref) async { return Future.value('foo'); } @riverpod class Example extends _$Example { @override Future build() async { return Future.value('foo'); } // Add methods to mutate the state } 异步的 - Stream @riverpod Stream example(Ref ref) async* { yield 'foo'; } @riverpod class Example extends _$Example { @override Stream build() async* { yield 'foo'; } // Add methods to mutate the state }
我对Provider 的理解水平仅限于此,虽然也查找了一些资料但都介绍的不够深,这里参考了杰哥的笔记有兴趣的可以去看看。