flutter滚动视图之Notification、NotificationListener、LayoutChangedNotification源码解析(七)

Notification

dart 复制代码
/// [Notification] 监听器的回调签名。
///
/// 返回 true 表示取消通知冒泡;返回 false 表示允许通知继续向上分发到更多的祖先节点。
///
/// [NotificationListener] 在监听 [ListView]、[NestedScrollView]、[GridView] 或任意可滚动组件的滚动事件时很有用。  
/// 由 [NotificationListener.onNotification] 使用。
typedef NotificationListenerCallback<T extends Notification> = bool Function(T notification);

/// 可以沿着组件树冒泡的通知。
///
/// 你可以使用 `is` 运算符来判断通知的类型,检查 [runtimeType]。
///
/// 若要在子树中监听通知,请使用 [NotificationListener]。
///
/// 若要发送通知,请调用要发送的通知实例的 [dispatch] 方法。  
/// 通知将会分发给给定 [BuildContext] 的祖先中,类型参数匹配的 [NotificationListener] 组件。
///
/// {@tool dartpad}
/// 这个示例展示了一个 [NotificationListener] 组件,它监听 [ScrollNotification] 通知。  
/// 当 [NestedScrollView] 中发生滚动事件时,这个组件会被通知。  
/// 事件可能是 [ScrollStartNotification] 或 [ScrollEndNotification]。
///
/// ** 代码见 examples/api/lib/widgets/notification_listener/notification.0.dart **
/// {@end-tool}
///
/// 参见:
///
///  * [ScrollNotification] ------ 描述滚动通知的生命周期。  
///  * [ScrollStartNotification] ------ 返回滚动开始的位置。  
///  * [ScrollEndNotification] ------ 返回滚动结束的位置。  
///  * [NestedScrollView] ------ 创建一个嵌套滚动视图。
///
abstract class Notification {
  /// 抽象常量构造函数。  
  /// 这个构造函数使子类能够提供 const 构造函数,从而可以在 const 表达式中使用。
  const Notification();

  /// 从给定的构建上下文开始冒泡此通知。
  ///
  /// 通知将会分发给给定 [BuildContext] 的祖先中,类型参数匹配的 [NotificationListener] 组件。  
  /// 如果 [BuildContext] 为 null,则不会分发通知。
  void dispatch(BuildContext? target) {
    target?.dispatchNotification(this);
  }

  @override
  String toString() {
    final List<String> description = <String>[];
    debugFillDescription(description);
    return '${objectRuntimeType(this, 'Notification')}(${description.join(", ")})';
  }

  /// 为 [toString] 添加额外的信息。
  ///
  /// 这个方法让子类更方便地协调,提供高质量的 [toString] 实现。  
  /// [Notification] 基类的 [toString] 会调用 [debugFillDescription] 来收集子类的信息并拼接到返回值中。
  ///
  /// 子类在实现这个方法时,应该首先调用父类的方法,例如 `super.debugFillDescription(description)`。
  @protected
  @mustCallSuper
  void debugFillDescription(List<String> description) {}
}

NotificationListener

scala 复制代码
/// 一个监听沿着组件树向上传递的 [Notification] 的组件。
///
/// {@youtube 560 315 https://www.youtube.com/watch?v=cAnFbFoGM50}
///
/// 只有当通知的 [runtimeType] 是 `T` 的子类型时,通知才会触发 [onNotification] 回调。
///
/// 若要分发通知,请使用 [Notification.dispatch] 方法。
class NotificationListener<T extends Notification> extends ProxyWidget {
  /// 创建一个监听通知的组件。
  const NotificationListener({super.key, required super.child, this.onNotification});

  /// 当合适类型的通知到达此树中的位置时调用。
  ///
  /// 返回 true 表示取消通知冒泡;返回 false 表示允许通知继续向上分发到更多的祖先节点。
  ///
  /// 通知在何时被分发是有区别的,主要有两种情况:
  /// - 在帧之间分发  
  /// - 在布局过程中分发
  ///
  /// 对于在布局过程中分发的通知(例如继承自 [LayoutChangedNotification] 的通知),
  /// 此时调用 [State.setState] 已经太晚了(因为布局正在子组件中进行,而通知此时正在向上冒泡)。  
  /// 对于依赖布局信息的组件,可以考虑使用 [LayoutBuilder]。
  final NotificationListenerCallback<T>? onNotification;

  @override
  Element createElement() {
    return _NotificationElement<T>(this);
  }
}

_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>;
    // 如果监听器存在回调,并且通知类型是 T(或其子类),则调用回调。
    if (listener.onNotification != null && notification is T) {
      return listener.onNotification!(notification);
    }
    // 否则返回 false,表示继续冒泡。
    return false;
  }

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

LayoutChangedNotification

scala 复制代码
/// 表示接收到此通知的对象的某个子节点的布局发生了变化,  
/// 因此之前对该布局的任何假设都不再有效。
///
/// 例如:当你尝试对齐多个子节点时,这个通知就很有用。
///
/// 若要在子树中监听此通知,请使用
/// [NotificationListener<LayoutChangedNotification>]。
///
/// 若要发送通知,请调用要发送的通知实例的 [dispatch] 方法。  
/// 通知将会分发给给定 [BuildContext] 的祖先中,类型参数匹配的 [NotificationListener] 组件。
///
/// 在 widgets 库中,只有 [SizeChangedLayoutNotifier] 类和 [Scrollable] 类会分发此通知  
/// (具体来说,它们分别分发 [SizeChangedLayoutNotification] 和 [ScrollNotification])。  
/// 动画过渡(Transitions)不会分发此通知。  
/// 在 build 函数中改变自身的布局不会自动触发此通知。  
/// 如果某个祖先需要在布局变化时得到通知,要么只使用布局不会改变的组件,  
/// 要么确保在合适的时机通知祖先,或者在需要时手动分发此通知。
///
/// 另外,由于此通知在布局变化时才会发送,因此它仅适用于依赖布局的绘制效果。  
/// 如果你用它来改变 build,例如调用 [setState],那么总是会落后一帧,  
/// 这会导致界面看起来很丑且很卡顿。
class LayoutChangedNotification extends Notification {
  /// 创建一个新的 [LayoutChangedNotification]。
  const LayoutChangedNotification();
}
相关推荐
liangshanbo1215几秒前
Speculation Rules API
前端·javascript·html
石国旺几秒前
前端javascript在线生成excel,word模板-通用场景(免费)
前端·javascript·excel
Jenna的海糖21 分钟前
Vue 项目首屏加载速度优化
前端·javascript·vue.js
前端梭哈攻城狮27 分钟前
js计算精度溢出,自定义加减乘除类
前端·javascript·算法
北辰alk30 分钟前
React JSX 内联条件渲染完全指南:四招让你的UI动态又灵活
前端
前端小巷子32 分钟前
最长递增子序列:从经典算法到 Vue3 运行时核心优化
前端·vue.js·面试
zayyo32 分钟前
深入解读 SourceMap:如何实现代码反解与调试
前端
龙在天35 分钟前
以为 Hooks 是银弹,结果是新坑
前端
wayhome在哪1 小时前
前端高频考题(css)
前端·css·面试
wayhome在哪1 小时前
前端高频考题(html)
前端·面试·html