flutter滚动视图之ProxyWidget、ProxyElement、NotifiableElementMixin源码解析(八)

ProxyWidget

scala 复制代码
/// 一个拥有子组件(child)的组件,它本身不会去构建新的组件。
///
/// 通常作为其他组件的基类使用,比如 [InheritedWidget] 和 [ParentDataWidget]。
///
/// 参见:
///
///  * [InheritedWidget] ------ 为后代组件提供可读取的环境状态的组件。  
///  * [ParentDataWidget] ------ 为子组件的 [RenderObject] 填充 [RenderObject.parentData],  
///    从而配置父组件的布局。  
///  * [StatefulWidget] 和 [State] ------ 可以在生命周期内多次构建、变化的组件。  
///  * [StatelessWidget] ------ 在给定配置和环境状态下始终以相同方式构建的组件。  
///  * [Widget] ------ 总览 Flutter 中所有组件的基类。
abstract class ProxyWidget extends Widget {
  /// 创建一个拥有且仅能拥有一个子组件的组件。
  const ProxyWidget({super.key, required this.child});

  /// 该组件在树中的子组件。
  ///
  /// {@template flutter.widgets.ProxyWidget.child}
  /// 此组件只能有一个子组件。  
  /// 如果要布局多个子组件,请让该组件的 child 使用 [Row]、[Column] 或 [Stack] 之类  
  /// 拥有 `children` 属性的组件,然后将多个子组件传递给它们。
  /// {@endtemplate}
  final Widget child;
}

_NotificationElement

scala 复制代码
/// 一个用于承载 [NotificationListener] 的 Element。
class _NotificationElement<T extends Notification> extends ProxyElement
    with NotifiableElementMixin {
  _NotificationElement(NotificationListener<T> super.widget);

  @override
  bool onNotification(Notification notification) {
    final NotificationListener<T> listener = widget as NotificationListener<T>;
    // 如果监听器提供了 onNotification 回调,并且通知类型匹配 T,
    // 则调用回调并返回其结果。
    if (listener.onNotification != null && notification is T) {
      return listener.onNotification!(notification);
    }
    // 否则返回 false,表示允许通知继续冒泡。
    return false;
  }

  @override
  void notifyClients(covariant ProxyWidget oldWidget) {
    // Notification 树不需要去通知客户端。
  }
}

方法 onNotificationNotifiableElementMixin提供

NotifiableElementMixin

csharp 复制代码
/// 将此 mixin 添加到类上,可以接收由子元素分发的 [Notification] 对象。
///
/// 参见:
///   * [NotificationListener] ------ 一个允许消费通知的组件。
mixin NotifiableElementMixin on Element {
  /// 当合适类型的通知到达此树中该位置时被调用。
  ///
  /// 返回 true 表示取消通知冒泡;返回 false 表示允许通知继续向上分发到更多的祖先节点。
  bool onNotification(Notification notification);

  @override
  void attachNotificationTree() {
    // 绑定通知树节点,将当前元素挂载到父节点的通知树上
    _notificationTree = _NotificationNode(_parent?._notificationTree, this);
  }
}

_NotificationNode

kotlin 复制代码
class _NotificationNode {
  _NotificationNode(this.parent, this.current);

  /// 当前节点,持有实现了 [NotifiableElementMixin] 的 Element。
  NotifiableElementMixin? current;

  /// 父节点,用于形成通知冒泡链。
  _NotificationNode? parent;

  /// 分发通知。
  ///
  /// 先交给当前节点的 [onNotification] 处理:
  /// - 如果返回 true,表示拦截通知,停止冒泡。
  /// - 如果返回 false,表示继续冒泡,则交由父节点处理。
  void dispatchNotification(Notification notification) {
    if (current?.onNotification(notification) ?? true) {
      return;
    }
    parent?.dispatchNotification(notification);
  }
}

🔎 解释:

  • _NotificationNode 就是一个 链表结构 ,把一层层 Element(实现了 NotifiableElementMixin 的,比如 NotificationListener)串联起来。
  • 当某个子组件调用 notification.dispatch(context) 时,通知会沿着这条链 一层层往上冒泡
  • 在冒泡过程中,每个节点都会调用自己的 onNotification,决定是否拦截(返回 true)还是继续向上传递(返回 false)。

Element - dispatchNotification

typescript 复制代码
@override
void dispatchNotification(Notification notification) {
  _notificationTree?.dispatchNotification(notification);
}

Element 里实现的。

这个方法就是 Notification 冒泡的入口

🔎 整体流程回顾

子组件触发通知

ini 复制代码
notification.dispatch(context);

Element 转交给 NotificationTree

javascript 复制代码
void dispatchNotification(Notification notification) {
  _notificationTree?.dispatchNotification(notification);
}
  • Element 不直接处理,而是交给 _notificationTree(也就是 _NotificationNode 链表)。

_NotificationNode 遍历

javascript 复制代码
void dispatchNotification(Notification notification) {
  if (current?.onNotification(notification) ?? true) {
    return; // 如果返回 true 就拦截
  }
  parent?.dispatchNotification(notification); // 否则继续往上
}
  • 这里的 current 就是 _NotificationElement

_NotificationElement.onNotification

kotlin 复制代码
@override
bool onNotification(Notification notification) {
  final NotificationListener<T> listener = widget as NotificationListener<T>;
  if (listener.onNotification != null && notification is T) {
    return listener.onNotification!(notification); // 调用开发者传入的回调
  }
  return false;
}

这就是写的 NotificationListener(onNotification: ...) 被调用的地方。

相关推荐
答案answer8 小时前
three.js着色器(Shader)实现数字孪生项目中常见的特效
前端·three.js
城管不管8 小时前
SpringBoot与反射
java·开发语言·前端
JackJiang8 小时前
即时通讯安全篇(三):一文读懂常用加解密算法与网络通讯安全
前端
一直_在路上8 小时前
Go架构师实战:玩转缓存,击破医疗IT百万QPS与“三大天灾
前端·面试
早八睡不醒午觉睡不够的程序猿8 小时前
Vue DevTools 调试提示
前端·javascript·vue.js
恋猫de小郭8 小时前
基于 Dart 的 Terminal UI ,pixel_prompt 这个 TUI 库了解下
android·前端·flutter
天天向上10248 小时前
vue el-form 自定义校验, 校验用户名调接口查重
前端·javascript·vue.js
忧郁的蛋~8 小时前
前端实现网页水印防移除的实战方案
前端
喝奶茶的Blair8 小时前
PHP应用-组件框架&前端模版渲染&三方插件&富文本编辑器&CVE审计(2024小迪安全DAY30笔记)
前端·安全·php
浪潮行舟8 小时前
WebGIS:在 Vue 2 项目中使用 Mapbox 时,如果需要加载的 GIS 数据量过大,怎么让接口一次性获取的geojson数据分批加载
前端·javascript·vue.js