这一节了解一下Flutter中的ValueNotifier,ValueNotifier是一个轻量级的状态管理工具,继承自ChangeNotifier,用于管理单一值的变化并通知监听器。简单总结如下:
API:
ValueNotifier(T initialValue):构造函数,创建ValueNotifier实例并初始化持有的值,初始化一个可监听的状态容器,用于存储单一类型的状态
value:存储ValueNotifier持有的当前值,修改该值时会自动触发通知,读取/更新状态,是状态变更的核心入口,更新时会通知所有已注册的监听者
ValueListenableBuilder:配合ValueNotifier使用的组件,自动监听value变化并重建UI,无需手动管理监听/移除
一般场景:
1 单个组件/局部UI的状态管理,比如开关、输入框等
2 表单控件的临时状态管理
栗子:
Dart
import 'package:flutter/material.dart';
import 'dart:async';
void main() => runApp(const MyApp());
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final ValueNotifier<String> _searchValue = ValueNotifier<String>('');
Timer? _debounceTimer;
void _onSearch(String value) {
if(_debounceTimer != null) _debounceTimer!.cancel();
_debounceTimer = Timer(const Duration(milliseconds: 500),(){
print('搜索: $value');
});
}
@override
void dispose() {
_searchValue.dispose();
_debounceTimer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('输入框防抖'),),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
TextField(
decoration: const InputDecoration(
hintText: '输入搜索内容',
border: OutlineInputBorder()
),
onChanged: (value) {
_searchValue.value = value;
_onSearch(value);
},
),
const SizedBox(height: 20),
ValueListenableBuilder<String>(valueListenable:_searchValue
, builder: (context,value,child){
return Text('当前输入: $value');
})
],
),
),
),
);
}
}
Dart
import 'package:flutter/material.dart';
class VolumeControlDemo extends StatelessWidget {
const VolumeControlDemo({super.key});
@override
Widget build(BuildContext context) {
final ValueNotifier<double> _volume = ValueNotifier<double>(50);
final ValueNotifier<bool> _muted = ValueNotifier<bool>(false);
return Scaffold(
appBar: AppBar(title: const Text('音量控制')),
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
SwitchListTile(
title: const Text('静音'),
value: _muted.value,
onChanged: (value) {
_muted.value = value;
if (value) _volume.value = 0;
},
),
ValueListenableBuilder<double>(
valueListenable: _volume,
builder: (context, value, child) {
return Slider(
value: value,
min: 0,
max: 100,
onChanged: (newValue) {
_volume.value = newValue;
if (_muted.value) _muted.value = false;
},
);
},
),
ValueListenableBuilder<double>(
valueListenable: _volume,
builder: (context, value, child) {
return Text(
_muted.value ? '音量:静音' : '音量:${value.toInt()}%',
style: const TextStyle(fontSize: 18),
);
},
),
],
),
),
);
}
}
注意:
1 类型安全 通过泛型T确保值的类型安全,避免运行时类型错误。
2 避免频繁重建 结合ValueListenableBuilder使用,仅更新依赖该值的UI部分,减少不必要的重建。
3 避免在addListener回调中执行耗时操作,否则可能阻塞UI线程。
源码:
Dart
class ValueNotifier<T> extends ChangeNotifier implements ValueListenable<T>
说明: 1 继承自ChangeNotifier:ChangeNotifier是Flutter提供的观察者模式基类,维护一个监听器列表,并在状态变化时通知所有监听器。2 实现ValueListenable<T>接口:该接口定义了value属性的getter方法,使ValueNotifier能存储一个可变值。
Dart
T _value; // 存储当前值
说明:_value是私有字段,用于存储当前值,外部通过value属性访问或修改。
Dart
T get value => _value;
set value(T newValue) {
if (_value == newValue) return;
_value = newValue;
notifyListeners();
}
说明: 1 Getter: 直接返回_value。 2 Setter: 2.1 比较新旧值(通过==运算符),若相同则跳过通知。 2.2 若值变化,更新_value并调用notifyListeners()触发监听器回调。
Dart
void notifyListeners() {
assert(ChangeNotifier.debugAssertNotDisposed(this));
if (_count == 0) {
return;
}
_notificationCallStackDepth++;
final int end = _count;
for (int i = 0; i < end; i++) {
try {
_listeners[i]?.call();
} catch (exception, stack) {
FlutterError.reportError(
FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'foundation library',
context: ErrorDescription('while dispatching notifications for $runtimeType'),
informationCollector: () => <DiagnosticsNode>[
DiagnosticsProperty<ChangeNotifier>(
'The $runtimeType sending notification was',
this,
style: DiagnosticsTreeStyle.errorProperty,
),
],
),
);
}
}
_notificationCallStackDepth--;
if (_notificationCallStackDepth == 0 && _reentrantlyRemovedListeners > 0) {
final int newLength = _count - _reentrantlyRemovedListeners;
if (newLength * 2 <= _listeners.length) {
final List<VoidCallback?> newListeners = List<VoidCallback?>.filled(newLength, null);
int newIndex = 0;
for (int i = 0; i < _count; i++) {
final VoidCallback? listener = _listeners[i];
if (listener != null) {
newListeners[newIndex++] = listener;
}
}
_listeners = newListeners;
} else {
for (int i = 0; i < newLength; i += 1) {
if (_listeners[i] == null) {
int swapIndex = i + 1;
while (_listeners[swapIndex] == null) {
swapIndex += 1;
}
_listeners[i] = _listeners[swapIndex];
_listeners[swapIndex] = null;
}
}
}
_reentrantlyRemovedListeners = 0;
_count = newLength;
}
}
说明: 调用ChangeNotifier的函数,1 notifyListeners()通过遍历监听器列表,依次调用每个监听器的回调函数,通知状态变更。2 为支持在通知过程中安全移除监听器,它将被移除的监听器标记为null而不立即缩容,避免遍历时索引错乱。3 使用_notificationCallStackDepth计数器跟踪递归调用层级,确保仅在最外层通知结束后才清理null监听器。4 清理时根据内存使用情况选择重建紧凑数组,兼顾性能与内存效率。
简单总结 ValueNotifier通过封装ChangeNotifier,实现值变化时的通知机制,核心是value的setter中调用notifyListeners()。