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()。

相关推荐
南村群童欺我老无力.2 小时前
Flutter 框架跨平台鸿蒙开发 - 阅读进度追踪应用开发指南
flutter·华为·harmonyos
世人万千丶2 小时前
鸿蒙跨端框架 Flutter 学习 Day 4:程序生存法则——异常捕获与异步错误处理的熔断艺术
学习·flutter·华为·harmonyos·鸿蒙
阿豪只会阿巴2 小时前
项目心得——发布者和订阅者问题解决思路
linux·开发语言·笔记·python·ubuntu·ros2
向前V2 小时前
Flutter for OpenHarmony数独游戏App实战:底部导航栏
javascript·flutter·游戏
Hello_Embed2 小时前
RS485 双串口通信 + LCD 实时显示(中断版)
c语言·笔记·单片机·学习·操作系统·嵌入式
小白阿龙2 小时前
鸿蒙+Flutter 跨平台开发——简易猜数字竞猜游戏实现
flutter·游戏·harmonyos
鱼子酱酱酱3 小时前
#Flutter中使用Shelf做服务器框架
flutter·shelf
IT陈图图3 小时前
基于 Flutter × OpenHarmony 图书馆管理系统之构建借阅记录模块
flutter·正则表达式·openharmony
宵时待雨3 小时前
数据结构(初阶)笔记归纳4:单链表的实现
c语言·开发语言·数据结构·笔记·算法