Flutter值扩展功能插件value_extensions的使用
Flutter值扩展功能插件value_extensions的使用
value_extensions
是一组扩展功能,它们在 ValueListenable
上提供了类似于 Rx 的流操作。这个包包含转换器、便捷对象和方法,以及可以将所有这些实体集成到小部件中的小部件。
动机
在 Flutter 中,有两种 PubSub 方法用于小部件:Stream
和 Listenable
。Stream
被广泛使用,在某些情况下非常有用,并且功能强大,但代价是大量的样板代码。
要拥有一个具有获取其最后一个值的 getter 的状态化 Stream
,需要执行以下步骤:
- 创建一个
StreamController
- 暴露一个
Stream
- 创建一个可变缓存变量
- 暴露该变量的 getter
- 每次缓存值发生变化时,必须将新值添加到
Sink
此外,要在小部件中通过 StreamBuilder
使用它,还需要提供 stream
参数和 initialValue
参数。
这需要大量的代码。
另一方面,ValueListenable
更简单,需要的代码更少。但是,它们缺乏 Stream
的功能——很难创建派生通知器、自动处理取消订阅、订阅和取消订阅,以及在不进行疯狂嵌套的情况下将其集成到 UI 中。Value Extensions
解决了这个问题。
开始使用
以下是所有扩展及其用例的列表。有关更多信息,请参阅 API 参考或示例。
工具扩展
这些扩展提供基础功能:创建、设置、订阅和处理通知器。
设置
.set(...)
扩展提供了一个更好的 API 来设置 ValueNotifier
的值。使用 .set
方法更简洁,并且更好地表达了意图。以下两个示例是等效的:
someNotifier.value = 10;
someNotifier.set(10);
更新
.update((current) => ...)
扩展提供了一个更好的 API 来设置 ValueNotifier
的值,当新值依赖于旧值时。
例如:
int timesTwo(int x) => x * 2;
/// 传统方式
someNotifier.value = timesTwo(someNotifier.value);
/// 扩展方式
someNotifier.update(timesTwo);
订阅
ValueListenable
提供了内置的订阅功能,但获取其状态和取消订阅有些棘手。此扩展提供了这些情况的功能。
final subscription = someValueListenable.subscribe(print);
print(subscription.isCanceled); // 打印 'false'
someValueListenable.set((_) => 10); // 打印 10
subscription.cancel();
print(subscription.isCanceled); // 打印 'true'
someValueListenable.set((_) => 100); // 不打印
还可以暂停和恢复订阅。
提取值
有时,需要将 Stream
转换为 ValueListenable
。此扩展正是为此而设计的。
final stream = Stream.periodic(Duration(seconds: 1), (second) => second);
final secondsPassed = stream.extractValue(initial: 0);
转换器
接下来的部分重点介绍了 ValueListenables
的转换。它们实现了迭代器模式并复制了 Stream
的方法,因此当基本 ValueListenable
发生变化时,派生的 ValueListenables
也会随之变化。
映射
创建一个新的 ValueListenable
,从基本的 ValueListenable
中派生其值。
final stringNotifier = ValueNotifier('Hello');
final lengthListenable = stringNotifier.map((value) => value.length); // 5
stringNotifier.update((hello) => '$hello, World!'); // lengthNotifier 值变为 13
平展映射
类似于 map
,但转换函数必须返回另一个 ValueListenable
而不是常规值,每当调用 flatMap
的 ValueListenable
发生变化时,都会重新计算其值。
final intNotifier = ValueNotifier(0);
final stringNotifier = ValueNotifier('Hello!');
final lengthListenable = stringNotifier.map((string) => string.length);
final isShowingInt = ValueNotifier(true);
final currentValue = isShowingInt.flatMap((isInt) => isInt ? intNotifier : lengthListenable);
过滤
通过过滤基本 ValueListenable
的值来创建新的 ValueListenable
,不包括第一个值。
final intNotifier = ValueNotifier(1);
final evenListenable = intNotifier.where((value) => value.isEven); // 1
final oddListenable = intNotifier.where((value) => value.isOdd); // 1
// evenListenable: 2
// oddListenable: 1
intNotifier.update((value) => value + 1);
最新组合
通过将 other
的值与其自身的值结合,并将这对值传递给转换函数来创建新的 ValueListenable
。
final word = ValueNotifier('Flutter');
final isUppercased = ValueNotifier(false);
final textListenable = word.combineLatest<bool, String>(
isUppercased,
(word, isUppercased) => isUppercased ? word.toUpperCase() : word,
); // Flutter
isUppercased.set(true); // textNotifier 值变为 FLUTTER
并行
这是对 .combineLatest(...)
扩展的包装。将 ValueListenable
的最新值打包在一个元组中。在小部件中避免嵌套的 ValueListenableBuilder
或 .bind
调用时非常有用。
final intNotifier = ValueNotifier(0);
final boolNotifier = ValueNotifier(false);
final paralleled = intNotifier.parallelWith(boolNotifier); // (0, false)
intNotifier.update((current) => current + 1); // paralleled 变为 (1, false)
boolNotifier.set(true); // paralleled 变为 (1, true)
小部件集成
最后一部分重点介绍小部件的集成。
Streams
和 ValueListenable
都旨在通过构建器在 UI 中显示。唯一的问题是——构建器相当冗长。为了“绑定”一个可观察变量到某个小部件,需要输入大约 86 个字符。Value Extensions
将这个数字减少到了大约 23 个字符,几乎减少了 4 倍!
绑定
要将 ValueListenable
绑定到小部件,使用 .bind(...)
扩展。
考虑这两个示例:使用 ValueListenableBuilder
和 .bind(...)
。
ValueListenableBuilder(
valueListenable: nameNotifier,
builder: (context, name, child) => Text("Hello, $name!"),
)
nameNotifier.bind(
(name) => Text("Hello, $name!"),
)
.bind(...)
扩展内部使用 ValueListenableBuilder
,因此结果是相同的有效目标重建,但需要输入的代码更少。
注意:在 .parallelWith(...)
扩展获得的 ValueListenable
上使用的 .bind(...)
扩展会自动解构其值在生成器回调中。
完整示例代码
以下是一个完整的示例代码,展示了如何使用 value_extensions
插件。
import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:value_extensions/value_extensions.dart';
class StateObject {
/// 基础私有通知器,其他所有通知器都从中派生其值。
final _counter = ValueNotifier(0);
late final StreamValueListenable<int> _secondsPassed =
_timer.extractValue(initial: 0);
/// 基本流用于转换演示
final _timer =
Stream.periodic(const Duration(seconds: 1), (second) => second);
/// 使用 [where()] 和 [subscribe()] 扩展的派生订阅
late final Subscription evenPrintSubscription;
StateObject() {
evenPrintSubscription =
_counter.where((value) => value.isEven).subscribe(print);
}
ValueListenable<Color> get counterColor =>
_counter.map((value) => value.isEven ? Colors.red : Colors.blue);
ValueListenable<String> get stringCounterValue =>
_counter.map((value) => value.toString());
ValueListenable<int> get secondsPassed => _secondsPassed;
List<ChangeNotifier> get _disposable => [_counter, _secondsPassed];
void increment() => _counter.update((value) => value + 1);
void dispose() {
evenPrintSubscription.cancel();
_disposable.disposeAll();
print("Disposed $this");
}
}
class CounterScreen extends StatefulWidget {
[@override](/user/override)
CounterScreenState createState() => CounterScreenState();
}
class CounterScreenState extends State {
final state = StateObject();
[@override](/user/override)
void dispose() {
state.dispose();
super.dispose();
}
[@override](/user/override)
Widget build(BuildContext context) => Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
state.secondsPassed.bind(
(seconds) => Text("Seconds passed: $seconds"),
),
const SizedBox(height: 100),
state.stringCounterValue.parallelWith(state.counterColor).bind(
(text, color) => Text(
text,
style: TextStyle(color: color),
),
),
OutlinedButton(
onPressed: state.increment,
child: const Text("Increment"),
),
OutlinedButton(
onPressed: state.evenPrintSubscription.cancel,
child: const Text("Cancel print"),
),
OutlinedButton(
onPressed: Navigator.of(context).pop,
child: const Text("Navigate back"),
),
],
),
),
);
}
class Home extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: const Text("Home")),
body: Center(
child: OutlinedButton(
onPressed: () => Navigator.push<void>(
context,
MaterialPageRoute(builder: (context) => CounterScreen()),
),
child: const Text("Navigate to Counter"),
),
),
);
}
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) => MaterialApp(
home: Home(),
);
}
void main() {
runApp(MyApp());
}
更多关于Flutter值扩展功能插件value_extensions的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter值扩展功能插件value_extensions的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用value_extensions
插件的示例代码。value_extensions
插件通常用于为常见的数据类型(如int
、double
、String
等)添加一些实用的扩展函数。假设你已经在pubspec.yaml
文件中添加了该插件的依赖:
dependencies:
flutter:
sdk: flutter
value_extensions: ^最新版本号 # 请替换为当前最新版本号
然后运行flutter pub get
来安装依赖。
示例代码
以下是一个简单的Flutter应用,展示了如何使用value_extensions
插件中的一些扩展函数。
main.dart
import 'package:flutter/material.dart';
import 'package:value_extensions/value_extensions.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Value Extensions Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text('Flutter Value Extensions Demo'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Int extension example: ${42.isEven}',
style: TextStyle(fontSize: 20),
),
SizedBox(height: 20),
Text(
'Double extension example: ${3.14.roundTo(2)}',
style: TextStyle(fontSize: 20),
),
SizedBox(height: 20),
Text(
'String extension example: "${"hello".capitalize}"',
style: TextStyle(fontSize: 20),
),
],
),
),
),
);
}
}
解释
-
Int 扩展:
42.isEven
:检查整数是否为偶数。value_extensions
插件为整数类型添加了诸如isEven
和isOdd
等实用函数。
-
Double 扩展:
3.14.roundTo(2)
:将浮点数四舍五入到指定的小数位数。这里将3.14
四舍五入到小数点后两位。
-
String 扩展:
"hello".capitalize
:将字符串的首字母大写。这里将"hello"
转换为"Hello"
。
注意事项
- 请确保你已经安装了最新版本的
value_extensions
插件。 - 根据插件文档,可能会有更多的扩展函数可用,这里只展示了几个常见的例子。
- 插件的功能可能会随着版本更新而变化,因此建议查看插件的官方文档以获取最新信息和完整的功能列表。
通过这种方式,你可以在Flutter项目中轻松地使用value_extensions
插件提供的各种实用扩展函数,从而提高代码的可读性和简洁性。