最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

flutter 基础 —— 事件监听

来源:博客园


(资料图片仅供参考)

事件机制:

  1. 命中测试的过程是从上层组件到下层组件,但是加入 HitTestResult 的顺序是从下到上,分发事件的顺序同加入顺序。
  2. 通常,若用户点击坐标不在当前节点的 size 范围内,则 hitTest 直接返回 false。
  3. 命中测试过程中,① 兄弟节点的加入顺序是倒序的,这个可以结合 Stack 组件来理解,即上层组件(children 中靠后)优先遍历。② 一旦有一个子节点的 hitTest 返回了 true,就会终止遍历。因为通常兄弟节点通常是不重叠的,且用户点击的坐标位置只会有一个节点,所以只要一个子节点命中了 hitTest 就返回 true,并且终止遍历。
  4. 节点自身是否通过命中测试的标志是它被添加到 HitTestResult 列表中,而不是它 hitTest 的返回值。但是 hitTest 的返回值会影响父组件是否被添加到 HitTestResult 中。

示例:

① RenderBox

bool hitTest(BoxHitTestResult result, { required Offset position }) {  if (_size!.contains(position)) {    if (hitTestChildren(result, position: position) || hitTestSelf(position)) {      //添加到命中测试列表      result.add(BoxHitTestEntry(this, position));      return true;    }  }  return false;}// 通常会该重写该方法@protectedbool hitTestSelf(Offset position) => false;

② RenderProxyBoxWithHitTestBehavior

// ColoredBox(初始化 behavior 是 HitTestBehavior.opaque)、// Listener(初始化 behavior 是 HitTestBehavior.deferToChild) 均继承自该组件@overridebool hitTest(BoxHitTestResult result, { required Offset position }) {  bool hitTarget = false;  if (size.contains(position)) {    hitTarget = hitTestChildren(result, position: position) || hitTestSelf(position);    if (hitTarget || behavior == HitTestBehavior.translucent) {      result.add(BoxHitTestEntry(this, position));    }  }  return hitTarget;}@overridebool hitTestSelf(Offset position) => behavior == HitTestBehavior.opaque;

③ IgnorePointer

@overridebool hitTest(BoxHitTestResult result, { required Offset position }) {// 未调用 result.add,则当前节点没有命中测试;// hitTest 返回 false(ignoring 默认为true),表示当前节点不参与测试,// 如果有其它兄弟节点,则会继续测试下一个兄弟节点。  return !ignoring && super.hitTest(result, position: position);}

④ AbsorbPointer

@overridebool hitTest(BoxHitTestResult result, { required Offset position }) {// 未调用 result.add,则当前节点没有命中测试;// hitTest 返回 true(absorbing 默认为true),表示前节点已参与测试,// 如果有其它兄弟节点,则不会继续测试下一个兄弟节点。  return absorbing      ? size.contains(position)      : super.hitTest(result, position: position);}

⑤ 自定义事件透传组件

代码:

class PassPointer extends SingleChildRenderObjectWidget {  const PassPointer({this.onTap, super.child});  final VoidCallback? onTap;  @override  RenderObject createRenderObject(BuildContext context) {    return RenderPassPointer(onTap: onTap);  }}class RenderPassPointer extends RenderProxyBox {  RenderPassPointer({    this.onTap,    RenderBox? child,  }) : super(child);  final VoidCallback? onTap;  @override  bool hitTest(BoxHitTestResult result, {required Offset position}) {    if (size.contains(position)) {      // 将当前节点添加到 HitTestResult 表中,即通过命中测试      result.add(BoxHitTestEntry(this, position));      // 返回 false,表示当前节点不参与测试,则会继续测试下一个兄弟节点      return false;    } else {      return super.hitTest(result, position: position);    }  }  @override  void handleEvent(PointerEvent event, HitTestEntry entry) {    if (event is PointerDownEvent) {      onTap?.call();    }  }}// 展示效果class HomePage extends StatelessWidget {  const HomePage({super.key});  @override  Widget build(BuildContext context) {    return Scaffold(      body: Center(          child: Stack(            alignment: Alignment.center,            // 多个兄弟节点,事件的遍历从下到上            children: [              Container(                width: 150,                height: 150,                color: Colors.yellow,                child: InkWell(                  onTap: () {                    print("C");                  },                ),              ),              Listener(                behavior: HitTestBehavior.translucent,                onPointerDown: (e) => print("bbb"),                child: IgnorePointer(                  child: Container(                    width: 100,                    height: 100,                    color: Colors.green,                    child: InkWell(                      onTap: () {                        print("B");                      },                    ),                  ),                ),              ),              PassPointer(                onTap: () => print("aaa"),                child: Container(                  width: 60,                  height: 60,                  color: Colors.pink,                  child: InkWell(                    onTap: () {                      print("A");                    },                  ),                ),              ),            ],          )),    );  }}

关键词: 兄弟节点 参与测试 从下到上