Flutter结果通知插件result_notifier的使用
Flutter结果通知插件result_notifier的使用
质量生活增强插件 - 简单且适度的Flutter状态管理
Result Notifier 是一个简单的包,用于增强Flutter已经提供的内置状态管理。换句话说,它基于熟悉的平台原生概念,而不是引入新的抽象和思维模型。事实上,这个包其实只是对 ValueNotifier
和 ChangeNotifier
的一些扩展(好吧,可能不止一点,但也没什么疯狂的东西)。正如这个包的名字所暗示的,最重要的添加之一就是Result
类型的概念,它可以表示某些数据、错误或加载状态。
注意:v0.5.0 包含了破坏性更改,请参阅 changelog。
Result Notifier 的本质
ResultNotifier
与 ValueNotifier
和 ChangeNotifier
不同的基本概念有四个:
-
它持有一个
Result
值(一种代数数据类型),并提供了访问和修改该值的方法。该值(结果)可以处于三种不同的状态之一:Data
:结果包含某些具体数据。Error
:表示错误,并附带之前的任何数据。Loading
:表示加载/重新加载状态,并附带之前的任何数据。
-
它是可刷新的 - 通过提供同步或异步的“获取器”函数,您可以指定在需要时(或数据过期时)如何刷新数据。
-
它是可缓存的 - 通过设置
cacheDuration
,您可以指定数据在被认为是过期之前应该被缓存多长时间。 -
它是可组合的 - 您可以轻松地将多个
ResultNotifier
的数据(甚至其他ValueListenable
)组合在一起,例如使用CombineLatestNotifier
或应用效果使用EffectNotifier
。
Result 类型中的代数数据类型支持
如上所述,Result
是一种代数数据类型,这意味着您可以在 switch
语句中像这样使用它:
switch (result) {
Data(data: var d, lastUpdate: var t) => Text(d),
Error(error: var e, stackTrace: var s, data: var d, lastUpdate: var t) => Text('Error: $e'),
Loading(data: var d, lastUpdate: var t) => const CircularProgressIndicator(),
}
…或者简单地像这样(随你便):
switch (result) {
(Data d) => Text(d.data),
(Error e) => Text('Error: ${e.error}'),
(_) => const CircularProgressIndicator()
}
开始使用
- 简单地 添加依赖 并开始编写一些通知器!
- 深入了解入门 示例 (更多信息 在这里) …或者 - 只需继续阅读以下快速介绍基本概念。👇
一个简单的开始
最简单的通知器形式只持有某个值,就像 ValueNotifier
一样。但是使用 ResultNotifier
,值被包裹在一个 Result
类型中,可以表示某些数据、错误或加载状态。
final notifier = ResultNotifier<String>(data: 'Hello...');
print(notifier.data); // 打印 'Hello...'
print(notifier.result); // 打印 'Data<String>(data: Hello..., lastUpdate: 2024-01-02 03:04:05.000006)'
notifier.toLoading(); // 便捷方法来设置值为 Loading,保留之前的值。
// 举例说明如何使用只读属性获取当前通知器的状态:
notifier.isLoading;
notifier.isData;
notifier.isError;
notifier.hasData;
// 如果使用缓存 `expiration`,还可以检查数据是否新鲜或过期:
notifier.isFresh;
notifier.isStale;
// 使用实际数据类型直接设置新数据值(Data),替换之前的值/结果:
notifier.data = 'Hello Flutter!';
// 或者,使用新的 Result(在这种情况下为 Data)替换之前的值/结果:
notifier.value = Data('Hello Flutter!');
// 或者,使用 Future 设置值:
notifier.future = Future.value('Hello again Flutter!');
// await notifier.future; // 可选地等待 Future 完成。
异步获取数据(例如从API)
通常你会想要做一些更复杂的事情,比如从API获取数据。在这种情况下,你可以使用 FutureNotifier
(或 ResultNotifier.future),这是一个使用“获取器”函数返回 Future 的 ResultNotifier
。
final notifier = ResultNotifier<String>.future(
(_) async {
final response = await http.get(Uri.parse('https://www.boredapi.com/api/activity/'));
final json = jsonDecode(response.body) as Map<String, dynamic>;
return json['activity'] as String;
},
data: 'Test in production', // 可选地设置初始值。
expiration: const Duration(seconds: 42), // 可选地设置缓存过期时间。
);
// 刷新数据(即调用获取器函数以更新通知器的数据):
notifier.refresh();
阅读更多关于缓存的部分。
观察(监听、监视…)变化
基于 ValueListenableBuilder 的观察
由于 ResultNotifier
实现了 ValueListenable
,您可以简单地使用 ValueListenableBuilder
来观察小部件中的变化。但是,此包还提供了 ResultBuilder
上的方法 (builder
),使这更方便。
notifier.builder((context, result, child) => switch (result) {
Data(data: var d) => Text(d),
Error(error: var e) => Text('Error: $e'),
Loading() => const CircularProgressIndicator(),
}),
“钩子风格”的内联观察
如果你喜欢“钩子风格”构建小部件,你可以使用 watch
方法(通过扩展方法在 ResultNotifier
、ValueNotifier
、ChangeNotifier
等上可用)来观察 Listenable
的变化。虽然表面上类似于 flutter_hooks
,但这种实现没有那些架构风格的一些复杂性,主要是因为范围较窄并且依赖现有的 ResultNotifier
(或 Listenable
)实例。
Watcher(builder: (context) {
final result = notifier.watch(context);
return Text(switch (result) {
Data(data: var d) => d,
Error(error: var e, data: var d) => 'Error - $d - $e',
Loading(data: var d) => 'Loading - $d',
});
});
这种类型的观察非常轻量级,特别是如果使用无状态的 Watcher
或 WatcherWidget
(也有状态化实现)。观察是通过使用 WatcherRef
接口实现的,该接口由 WatcherContext
实现(传给 Watcher
的 builder
和 WatcherWidget
的 build
方法等中的 BuildContext
)。尽管 watch
方法是由 WatcherRef
提供的,但通常更方便使用 ResultNotifier
(和 Listenable
等)上的扩展方法。
虽然使用 watch
很简单,但也有一些需要注意的地方:
watch
方法只能在WatcherWidget
的build
方法中或混合了WatcherMixin
或StatefulWatcherMixin
的小部件中调用。- 当不再调用
watch
或小部件从树中移除(即处置)时,会自动处理处置。 - 在
build
方法中的条件逻辑与watch
兼容,记住上面的要点 - 即小部件不会因为上次build
调用中未观察到的Listenable
而重建。
这种观察风格的一个额外好处是,它使得更容易观察多个 Listenable
或 ResultNotifier
的变化,而无需大量嵌套的 ValueListenableBuilder
或 ResultBuilder
。例如:
Watcher(builder: (context) {
final result1 = notifier1.watch(context);
final result2 = notifier2.watch(context);
final combined = [result1, result2].combine((data) => '${data[0]} and ${data[1]}');
return Text(switch (result) {
(Data<String> d) => d.data,
(Loading<String> l) => 'Loading - ${l.data}',
(Error<String> e) => 'Error - ${e.data} - ${e.error}',
});
});
深入了解
缓存
当使用远程数据时,通常会缓存数据一段时间并在其过期时刷新。这可以通过设置 cacheDuration
来完成。但是记住,缓存是一切罪恶的根源,所以除非你确定真的需要它,并且在实现应用程序核心功能之后,否则不要启用它。
final notifier = ResultNotifier<String>.future(
(_) async { ... },
data: 'Test in production', // 可选地设置初始值。
expiration: const Duration(seconds: 42), // 可选地设置缓存过期时间。
);
// 如果使用缓存 `expiration`,还可以检查数据是否新鲜或过期:
notifier.isFresh; // 返回 true
notifier.isStale; // 返回 false
// 刷新数据(即调用获取器函数以更新通知器的数据):
notifier.refresh(); // 在这种情况下,只有在刷新调用后经过 42 秒,数据才会更新。
效果
你也可以使用效果(见 EffectNotifier
),以构建更复杂的链式通知器:
final notifier = ResultNotifier<String>(data: 'Þetta er frábært!');
final effect = notifier.effect((_, input) => input.toUpperCase());
effect.onData(print);
notifier.data = 'Þetta er frábært!'; // 打印: "ÞETTA ER FRÁBÆRT!"
请参阅 这里 的效果示例以获得这些概念的更完整的演示。
内联效果
使用钩子风格的方法观察变化的一个优点是,您可以轻松地组合多个通知器的结果(无需创建一个新的通知器)。这种效果既通过 Iterable
的扩展方法也通过一系列 Record
定义(例如 ResultTuple
)来支持。
Widget build(BuildContext context) {
final activity = activityRepository.watch();
final counter = experiencePoints.watch();
/// 这里我们使用 `combine` 或 `combineData` 扩展方法(定义在 `ResultTuple` 中)组合两个通知器的数据。
final resultFromRecord = (activity, counter).combine((a, b) => '$a - total experience points: $b');
/// 你也可以使用类似的功能作为 Iterable 的扩展方法。
final resultFromList = [activity, counter].combine((data) => '${data[0]} - count: ${data[1]}');
/// 或者如果你只需要数据:
final resultData = (activity, counter).combineData((a, b) => '$a - count: $b');
}
更多关于Flutter结果通知插件result_notifier的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter结果通知插件result_notifier的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter中使用result_notifier
插件的示例。这个插件通常用于在不同的组件或页面之间传递结果或状态变化。假设你已经在pubspec.yaml
文件中添加了result_notifier
依赖,并运行了flutter pub get
来安装它。
首先,确保你的pubspec.yaml
文件中包含以下依赖项:
dependencies:
flutter:
sdk: flutter
result_notifier: ^最新版本号 # 请替换为实际可用的最新版本号
接下来,我们可以创建一个简单的示例来展示如何使用result_notifier
。
1. 创建一个ResultNotifier实例
在需要传递结果的地方(例如,一个按钮点击事件中),我们可以创建一个ResultNotifier
实例并发送结果。
2. 监听ResultNotifier的变化
在接收结果的地方(例如,另一个页面或组件),我们可以监听这个ResultNotifier
的变化并处理结果。
示例代码
main.dart
import 'package:flutter/material.dart';
import 'package:result_notifier/result_notifier.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: ResultNotifierExample(),
);
}
}
class ResultNotifierExample extends StatefulWidget {
@override
_ResultNotifierExampleState createState() => _ResultNotifierExampleState();
}
class _ResultNotifierExampleState extends State<ResultNotifierExample> {
final ResultNotifier<String> resultNotifier = ResultNotifier<String>();
void _sendResult(String result) {
resultNotifier.postResult(result);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Result Notifier Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Send a result to the ResultNotifier:'),
ElevatedButton(
onPressed: () => _sendResult('Hello, ResultNotifier!'),
child: Text('Send Result'),
),
ResultNotifierListener<String>(
notifier: resultNotifier,
builder: (context, result, hasResult) {
return Column(
children: [
if (hasResult)
Text('Received Result: $result'),
else
Text('No result received yet.'),
],
);
},
),
],
),
),
);
}
}
解释
-
ResultNotifier实例:
- 我们创建了一个
ResultNotifier<String>
实例,用于传递字符串类型的结果。
- 我们创建了一个
-
发送结果:
- 当点击按钮时,调用
_sendResult
方法,该方法使用postResult
方法发送结果到ResultNotifier
。
- 当点击按钮时,调用
-
监听结果:
- 使用
ResultNotifierListener
组件监听resultNotifier
的变化。当结果变化时,builder
方法会被调用,并传递当前的结果和是否有结果的布尔值。
- 使用
这样,你就可以在Flutter应用中使用result_notifier
插件来在不同组件或页面之间传递结果或状态变化。这个示例展示了基本的用法,你可以根据实际需求进行扩展和修改。