Flutter 中的状态
在 Flutter 中,一切都是 widget 。 Flutter 在每个给定时间维护应用程序的当前状态,因此每当我们更新用户界面时,我们就说我们正在更新状态。
Flutter 有自己的状态管理系统,Stateful Widget 中流行的 setState 函数,它在调用时执行 build 方法。默认情况下,程序遵循其生命周期,但当调用 setState 时,程序会重新运行 build 方法并重建 widget 树。
在大多数情况下使用 setState 是合适的。如果使用不当,setState 可能会出现一些问题:
- 前端和后端之间的紧密耦合。
- 它再次重建整个 widget ,这可能既昂贵又耗时。
这就是为什么今天我们要研究另一种使用 ValueNotifier 更新状态的方法。它用于我们想要实现选择性重建的情况,这是大多数应用程序设计中的情况。
Flutter 中的 ValueNotifier 介绍
ValueNotifier 是一种特殊类型的类,它扩展了 ChangeNotifier。 ValueNotifier 是 Flutter 本身原生的,它提供了单个元素的响应性。 Flutter 默认还有一个 widget ,使用它我们可以监听名为 ValueListenableBuilder 的 ValueNotifier。也可以使用 AnimationBuilder 来监听,因为它是可监听的。
将 ValueNotifier 视为保持某个值的数据流。我们为它提供一个值,每个侦听器都会收到值更改的通知。
我们可以创建任何类型 int、bool、list 或任何自定义数据类型的 ValueNotifier。你可以像这样创建一个 ValueNotifier 对象:
ValueNotifier<int> counter = ValueNotifier<int>(0);
我们可以像这样更新值:
counter.value = counter.value++;
//OR
counter.value++;
另外,我们可以像这样监听 ValueNotifier:
counter.addListener((){
print(counter.value);
});
如果我们想监听 widget 中的值,我们使用 ValueListenableBuilder widget ,该 widget 开箱即用,负责在视图之外删除订阅。因此,当使用 ValueListenableBuilder 时,我们不需要手动添加和删除侦听器,它会自动为我们完成。
在 Flutter 中移除 Value Notifier Listener
如果我们手动收听 ValueNotifier,并且我们不释放它,因为它在应用程序的某个地方需要。当当前页面不使用时,我们可以使用 removeListener 函数从 ValueNotifier 中手动删除监听器。
ValueNotifier<int> valueNotifier = ValueNotifier(0);
void removeDemo() {
valueNotifier.removeListener(doTaskWhenNotified);
}
void addDemo(){
valueNotifier.addListener(doTaskWhenNotified);
}
void doTaskWhenNotified() {
print(valueNotifier.value);
}
在 Flutter 中处理 ValueNotifier
最好在不再使用时释放值通知器,否则可能会导致内存泄漏。通知器上的 dispose 方法将释放任何订阅的侦听器。
@override void dispose() {
counter.dispose();
super.dispose();
}
什么是 ValueListenableBuilder?
Flutter 中有许多类型的构建器,例如 StreamBuilder、AnimatedBuilder、FutureBuilder 等。它们的名字表明它们使用的对象类型。 ValueListenableBuilder 使用 ValueNotifier 对象。每次我们收到值更新时都会执行 builder 方法。当我们路由到另一个页面时,ValueListenableBuilder 会在内部自动删除监听器。
const ValueListenableBuilder({
@required this.valueListenable, @required this.builder, this.child,
})
这是 ValueListenableBuilder 的构造函数。这里,valueListenable 是要监听的 ValueNotifier。 builder 函数接收 3 个参数(BuildContext context, dynamic value, Widget child)。该值是从提供的 valueNotifier 接收到的数据。可以使用 child 参数。如果孩子的构建成本很高并且不依赖于通知器的值,我们将使用它进行优化。
在 Flutter 中使用 Value Notifier 的 Counter App
使用 ValueNotifer 和 ValueListenableBuilder 的计数器应用程序如下所示。请注意如何没有使用 setState,并且我们仅重建 Text 部分以防值更改。
import 'package:flutter/material.dart';
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
ValueNotifier<int> counter = ValueNotifier<int>(0);
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () => counter.value++,
child: const Icon(Icons.add, color: Colors.white),
),
body: Center(
child: ValueListenableBuilder(
valueListenable: counter,
builder: (context, value, child) {
return Text(value.toString());
},
),
),
);
}
}
这种方法,即使代码稍长一点,也比传统的 setState 更有效。唯一需要重新绘制的部分是 Text widget 。对于此示例,无需重新构建整个 widget、Scaffold、AppBar,甚至是触发更改的 FloatingActionButton。
可以轻松扩展 ValueNotifier 类并使用自定义函数制作自定义 ValueNotifier。值数据类型应在扩展时指定。该类本身将包含一个属性值,该属性值指定您的自定义 ValueNotifier 类的当前值。
class CounterNotifier extends ValueNotifier<int> {
CounterNotifier({int? value}) : super(value ?? 0);
void increment() {
value++;
}
void decrement() {
value--;
}
}
带有自定义 ValueNotifier 的完整计数器代码:
import 'package:flutter/material.dart';
class CounterNotifier extends ValueNotifier<int> {
CounterNotifier({int? value}) : super(value ?? 0);
void increment() {
value++;
}
void decrement() {
value--;
}
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
CounterNotifier counter = CounterNotifier();
@override
void dispose() {
counter.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: Row(
children: [
FloatingActionButton(
onPressed: () => counter.increment(),
child: const Icon(Icons.add, color: Colors.white),
),
FloatingActionButton(
onPressed: () => counter.increment(),
child: const Icon(Icons.add, color: Colors.white),
),
],
),
body: Center(
child: ValueListenableBuilder(
valueListenable: counter,
builder: (context, value, child) {
return Text(value.toString());
},
),
),
);
}
}
自定义 ValueNotifier。
让我们先看看为什么我们需要自定义 ValueNotifier:
- 用相关函数封装数据。
- 数据相关逻辑和 UI 逻辑之间的抽象。
- 提供自定义函数来修改和转换数据。
默认情况下,我们可以像这样直接与通知器的值交互:
Modify an <int> ValueNotifier:
- valueNotifier.value = 10;
- valueNotifier.value++;
- valueNotifier.value--;
我们能够与 ValueNotifier 值进行交互。但是,我们必须在前端一次又一次地声明所有这些语句。如果我们想将所有这些功能声明为函数并将它们绑定到 ValueNotifier 类,那么我们需要创建我们的自定义 ValueNotifier。我们像这样扩展 ValueNotifier 类:
下面你可以看到一个如何在 Flutter 中创建自定义 ValueNotifier 类的实例:
import 'dart:math';
import 'package:flutter/material.dart';
class MyNotifier extends ValueNotifier<int> {
MyNotifier(int value) : super(value);
void increment() {
value++;
}
void decrement() {
value--;
}
void performSomeCalcuations() {
value = pow(value, 2).toInt();
}
void reset() {
value = -1;
}
}
当我们将 ValueNotifier 扩展到任何类时,我们可以使用函数轻松修改我们的数据。在类中,我们可以使用 value 关键字访问 ValueNotifier 的当前值。您可能会注意到 value 没有在任何地方声明,因为它是由 ValueNotifier 类本身提供的。
我们可以像使用任何 ValueNotifier 一样简单地使用这个类。我们使用 ValueListenableBuilder 来使用我们的自定义 ValueNotifier。
在 Flutter 中使用自定义 ValueNotifier
我们可以使用 AnimatedBuilder 或 ValueListenableBuilder 使用我们的 ValueNotifier。现在让我们看看如何在 Flutter 中使用 ValueListenableBuilder 来消费我们的自定义 ValueNotifier:
import "package:flutter/material.dart";
class CustomValueNotifierExample extends StatefulWidget {
const CustomValueNotifierExample({Key? key}) : super(key: key);
@override
State<CustomValueNotifierExample> createState() =>
_CustomValueNotifierExampleState();
}
// 1
final MyNotifier myNotifier = MyNotifier(0);
class _CustomValueNotifierExampleState
extends State<CustomValueNotifierExample> {
@override
Widget build(BuildContext context) {
return ValueListenableBuilder( // 2
valueListenable: myNotifier,
builder: (context, value, child) {
return Scaffold(
floatingActionButton: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
onPressed: () => myNotifier.performSomeCalcuations(),// 3
child: const Icon(Icons.arrow_circle_up),
),
const SizedBox(width: 10),
FloatingActionButton(
onPressed: () => myNotifier.increment(),// 4
child: const Icon(Icons.plus_one),
),
const SizedBox(width: 10),
FloatingActionButton(
onPressed: () => myNotifier.reset(),// 5
child: const Icon(Icons.restore_page),
),
],
),
body: Center(
child: Text(value.toString()), // 6
),
);
},
);
}
}
分步说明:
- 首先,我们创建自定义 ValueNotifier 的单例实例。
- 在这里,我们使用 ValueListenableBuilder 使用我们的通知器。
- 在步骤 3、4、5 和 6 中,我们使用自定义 ValueNotifier 中描述的方法。
如果我们要消费ValueNotifier,有两种方式:
- 如果需要跨两个或多个页面的 ValueNotifier,则使用 Singleton 实例。在上面的示例中,我们使用的是单例实例。
- 仅在需要时创建实例,仅限于 Widget 范围。
处理 ValueNotifier
在我们使用完自定义 ValueNotifier 对象后,必须处理 ValueNotifier。处理掉 ValueNotifier 后,它就不能再使用了。因此,请确保您不需要任何其他页面上的 ValueNotifier。
@override
void dispose() {
myNotifier.dispose();
super.dispose();
}
结论:
Flutter 有很多状态管理技术。无论是使用 GetX、BLoC、RiverPod、Provider 等高级软件包,还是仅使用 setState。 ValueNotifier 是 Flutter 中的原生功能,ValueListenableBuilder 提供了对特定段的优化重建。如果想通知应用程序的某些部分有关值更改,请记住解决方案 ValueNotifer。
由于 Flutter 可以快速渲染一棵 widget 树,因此两种解决方案都是正确的。然而,作为一名开发人员,我相信优化和干净的代码。各种建设者帮助我们做到这一点。 (例如:StreamBuilder、FutureBuilder、ValueListenableBuilder 等)。