Flutter之通知 Notification

8.6 通知 Notification | 《Flutter实战·第二版》

概念理解

通知(Notification)是Flutter中一个重要的机制,在widget树中,每一个节点都可以分发通知,通知会沿着当前节点向上传递,所有父节点都可以通过NotificationListener来监听通知。

Flutter中将这种由子向父的传递通知的机制称为通知冒泡(Notification Bubbling)。

通知冒泡和用户触摸事件冒泡是相似的,但有一点不同:通知冒泡可以中止,但用户触摸事件不行。

  • 子widget可以通过Notification类及其子类来发送通知
  • 父Widget可以通过NotificationListener或NotificationListenerTree来监听并处理这些通知。
  • 当子Widget发送通知时,Flutter会自动沿着Widget树向上冒泡,直到找到第一个处理该通知的父Widget为止。
  • 如果没有能够处理该通知的父Widget,通知将会一直冒泡到根Widget(通常是MaterialApp或WidgetsApp),如果根Widget仍然无法处理该通知,则该通知将被忽略。 

NotificationListener  

NotificationListener 继承自StatelessWidget类,所以它可以直接嵌套到 Widget 树中,它可以指定一个监听的模板T,该模板参数类型必须是继承自Notification。指定后,只有当监听到模板后才会调用回调函数。

监听的模板T,就是子widget发出的t通知 Notification

class NotificationListener<T extends Notification> extends StatelessWidget {
  const NotificationListener({
    Key key,
    required this.child,
    this.onNotification,
  }) : super(key: key);
 ...//省略无关代码 
}  
  • child,是被监听的子widget

  • onNotification,回调为通知处理回调的函数方法

typedef NotificationListenerCallback<T extends Notification> = bool Function(T notification);

onNotification的返回值是一个bool型。

当返回值为true时,阻止冒泡,其父级Widget将再也收不到该通知;当返回值为false 时继续向上冒泡通知。 

Notification

发送通知

Notification有一个dispatch(context)方法,它是用于分发通知的,它要求一个BuildContext作为参数传入。

context实际上就是操作Element的一个接口,它与Element树上的节点是对应的,通知会从context对应的Element节点向上冒泡。

void dispatch(BuildContext target) {
  target?.visitAncestorElements(visitAncestor);
}
  • dispatch(context)中调用了当前context的 visitAncestorElements 方法,该方法会从当前Element开始向上遍历父级Element; 
  • visitAncestorElements有一个遍历回调参数 visitAncestor,在遍历过程中对遍历到的父级Element都会执行该回调。
  • 遍历的终止条件是:已经遍历到根Element或某个遍历回调返回false(此时返回的false是由NotificationListener的 _dispatch 方法返回的
//遍历回调,会对每一个父级Element执行此回调
bool visitAncestor(Element element) {
  //判断当前element对应的Widget是否是NotificationListener。
  
  //由于NotificationListener是继承自StatelessWidget,
  //故先判断是否是StatelessElement
  if (element is StatelessElement) {
    //是StatelessElement,则获取element对应的Widget,判断
    //是否是NotificationListener 。
    final StatelessWidget widget = element.widget;
    if (widget is NotificationListener<Notification>) {
      //是NotificationListener,则调用该NotificationListener的_dispatch方法
      if (widget._dispatch(this, element)) 
        return false;
    }
  }
  return true;
}
  • visitAncestor会判断每一个遍历到的父级Widget是否是NotificationListener,如果不是,则返回true继续向上遍历,如果是,则调用 NotificationListener的 _dispatch 方法 
bool _dispatch(Notification notification, Element element) {
    // 如果通知监听器不为空,并且当前通知类型是该NotificationListener
    // 监听的通知类型,则调用当前NotificationListener的onNotification
    if (onNotification != null && notification is T) {
      final bool result = onNotification(notification);
      // 返回值决定是否继续向上遍历
      return result == true; 
    }
    return false;
  }
  • NotificationListeneronNotification回调最终也是在_ _dispatch 方法中执行的,执行条件是:通知监听器不为空且通知监听器的类型与该父widget所监听的类型相同。
  • 最后根据回调函数的返回值,判断是否向上继续冒泡

个人理解梳理:

子widget发出通知 Notification,父widget 通过 NotificationListener 进行监听,当子widget发出通知时,flutter会根据子widget所在的 BuildContext 的 Element 节点调用 visitAncestorElements 向上遍历父Element,每遍历一个父Element都会回调 visitAncestor 方法

因为,NotificationListener继承自 StatelessWidget ,所以判断当前 Element 是否为statelessElement ,如果是则继续判断其 Element对应的widget是否为NotificationListener;

如果是,则调用 NotificationListener的 _dispatch 方法 ,根据此处的返回值判断是否继续查看

如果不是,则返回true,继续向上调用检查。

自定义Notification

自定义notification

自定义notification时需要继承Notification

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';

class CustomNotification extends Notification {
  final String data;

  CustomNotification(this.data);
}

main 

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'notification.dart';

main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("初始定义"),
        ),
        body: BodyPage(),
      ),
    );
  }
}

class BodyPage extends StatelessWidget {
  List<String> datas = ["朱一龙", "肖战", "王一博", "白宇"];

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        width: 500,
        child: NotificationListener<CustomNotification>(
          onNotification: (notification) {
            print("监听的子widget是Center:${notification.data}");
            return false;
          },
          child: Center(
            child: NotificationListener<CustomNotification>(
              onNotification: (notification) {
                print("监听的子widget是Column:${notification.data}");
                return false;
              },
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Text("这是一些名字"),
                  Container(
                    height: 500,
                    width: 300,
                    color: Colors.amber,
                    child: ListView.builder(
                        itemCount: datas.length,
                        itemBuilder: (context, index) {
                          return Container(
                            height: 30,
                            child: Row(
                              children: [
                                Text("${datas[index]}"),
                                SizedBox(
                                  width: 30,
                                ),
                                ElevatedButton(
                                    onPressed: () {
                                      CustomNotification(datas[index])
                                          .dispatch(context);
                                    },
                                    child: Text("分发"))
                              ],
                            ),
                          );
                        }),
                  )
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

此时有两层嵌套的 NotificationListener ,它们都是监听 CustomNotification 通知的。

  • 当子NotificationListener 的 onNotification 回调返回了false,表示不阻止冒泡,所以父NotificationListener 仍然会受到通知,所以控制台会打印出通知信息;

  • 当子NotificationListener 的 onNotification 回调的返回值改为true,则父NotificationListener便不会再打印通知了,因为子 NotificationListener 已经终止通知冒泡了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值