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

相关推荐
一只大侠的侠8 分钟前
Flutter开源鸿蒙跨平台训练营 Day 3
flutter·开源·harmonyos
觉醒大王1 小时前
哪些文章会被我拒稿?
论文阅读·笔记·深度学习·考研·自然语言处理·html·学习方法
方安乐1 小时前
科普:股票 vs 债券的区别
笔记
一只大侠的侠1 小时前
【Harmonyos】Flutter开源鸿蒙跨平台训练营 Day 2 鸿蒙跨平台开发环境搭建与工程实践
flutter·开源·harmonyos
微祎_2 小时前
Flutter for OpenHarmony:构建一个 Flutter 平衡球游戏,深入解析动画控制器、实时物理模拟与手势驱动交互
flutter·游戏·交互
傻小胖3 小时前
22.ETH-智能合约-北大肖臻老师客堂笔记
笔记·区块链·智能合约
浅念-3 小时前
C++入门(2)
开发语言·c++·经验分享·笔记·学习
ZH15455891313 小时前
Flutter for OpenHarmony Python学习助手实战:面向对象编程实战的实现
python·学习·flutter
renke33644 小时前
Flutter for OpenHarmony:构建一个 Flutter 色彩调和师游戏,RGB 空间探索、感知色差计算与视觉认知训练的工程实现
flutter·游戏
王码码20354 小时前
Flutter for OpenHarmony 实战之基础组件:第三十一篇 Chip 系列组件 — 灵活的标签化交互
android·flutter·交互·harmonyos