Flutter笔记--ValueNotifier

这一节了解一下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()。

相关推荐
sbjdhjd2 小时前
网工进阶笔记:H3CSE 园区网全栈技术与协议汇总
网络·笔记·云计算·ssh·网络工程·vlan·aaa
传奇开心果编程4 小时前
【Flutter踩坑排雷】第7课:巨型 build 函数坑|全部 UI 写在同一个 build,维护难、隐性异常
android·flutter·ios·移动开发·跨平台·dart·声明式ui
海绵宝宝的月光宝盒5 小时前
14-AutoCAD-阵列命令应用与案例实战(AR)
经验分享·笔记·自动化·课程设计·制造·学习方法·cad
白山编程大哥6 小时前
接口幂等性是什么?如何设计?
笔记
ACP广源盛139246256736 小时前
6 盘位存储扩展方案选型笔记:YLB3116 @ACP评估记录
笔记·硬件架构·硬件工程·国产芯片
小李不想当小白7 小时前
SPI通信外设与W25Q64 (硬件SPI)(STM32标准库学习笔记)
笔记·stm32·单片机·嵌入式硬件·学习·嵌入式
程序员老刘7 小时前
从短剧App的bug看响应式编程在AI时代的优势
flutter·ai编程·客户端
weixin_448119949 小时前
Datawhale Jev Cookbook 202610 笔记3
笔记
姚瑞南9 小时前
【PRD】招投标商机雷达爬虫系统
人工智能·经验分享·笔记·爬虫·产品运营·产品经理
Gnevergiveup10 小时前
ATT&CK红蓝对抗实战指南-Windows安全基础
笔记·网络安全