我正在参与「启航方案」

点击事情呼应

点击组件中的HitTestBehavior特点支撑三个值:opaquetranslucentdeferToChild。其在射中测验起到必定效果可改动原有射中逻辑从而是完成不同点击触发事情。

HitTestBehavior特点值

HitTestBehaviorRenderProxyBoxWithHitTestBehavior有详细完成运用场景。

/flutter/packages/flutter/lib/src/rendering/proxy_box.dart:RenderProxyBoxWithHitTestBehavior

@override
boolhitTest(BoxHitTestResult result, {requiredOffset position }) {
boolhitTarget =false;
if(size.contains(position)) {
hitTarget = hitTestChildren(result, position: position) || hitTestSelf(position);// 若behavior 等于HitTestBehavior.opaque可射中
if(hitTarget || behavior == HitTestBehavior.translucent) {//  若behavior等于HitTestBehavior.translucent可射中
result.add(BoxHitTestEntry(this, position));
}
}
returnhitTarget;
}
@override
boolhitTestSelf(Offset position) => behavior == HitTestBehavior.opaque;

在检查子节点射中情况时判别是否opaque,在检查本身射中时判别translucent,因而可知HitTestBehavior.opaque>HitTestBehavior.translucent>HitTestBehavior.deferToChild

因为基础组件关于hitTesthitTestChildrenfalse因而存在将事情消费情况(例如SizedBox无子节点情况下默许false)。为了验证HitTestBehavior可行性就需求自定义一个组件重写hitTesthitTestChildren方法。这里以ColoredBox为基础自定义组件重写_RenderColoredBox的射中判定默许都为false

@override
boolhitTest(BoxHitTestResult result, {requiredOffset position}) {
returnfalse;
}
@override
boolhitTestChildren(BoxHitTestResult result, {requiredOffset position}) {
returnfalse;
}

Stack中设置两个堆叠子节点:

  1. 默许情况下点击组件behaviorHitTestBehavior.deferToChild根据点击组件子节点射中测验要求而定
  2. 当为HitTestBehavior.deferToChild情况下,点击事情由最上层子节点呼应
  3. 当为HitTestBehavior.translucent情况下,点击事情都透传一切子节点都呼应
  4. 当为HitTestBehavior.deferToChild情况下,点击事情无呼应(因为自定义_ColoredBoxWithNoHitTest疏忽了射中测验)
Stack(
alignment: Alignment.center,
children: [
Listener(
behavior: value,
onPointerDown: (down) {
showSnackBarMsg(context,'onPointerDown -> Listener -> 外',
clear:false, duration:constDuration(milliseconds:500));
},
child: _ColoredBoxWithNoHitTest(
color: Colors.red.withOpacity(0.5),
child:constSizedBox(
height:200,
width:250,
),
)),
Listener(
behavior: value,
onPointerDown: (down) {
showSnackBarMsg(context,'onPointerDown -> Listener -> 内',
clear:false, duration:constDuration(milliseconds:500));
},
child: _ColoredBoxWithNoHitTest(
color: Colors.red.withOpacity(0.5),
child:constSizedBox(
height:100,
width:150,
),
),
),
],
);

在运用HitTestBehavior也需求注意到所效果节点是否支撑疏忽射中,因为很多情况下有些组件默许情况下完成hitTesttrue状况。

实战举例

一个布局完成如下:最外层Container设置边框可视化点击区域,内部子节点是带有GestureDetectorContainer无边框无背景色其内部子节点有ImageText等。

Container(
decoration: buildBoxDecorationBorder(),
child: GestureDetector(
// behavior: HitTestBehavior.deferToChild, // 设置后点击空白区域无呼应
behavior: HitTestBehavior.translucent,// 设置后点击空白区域有呼应
onTap: () {
showSnackBarMsg(context,'onTap -> GestureDetector -> Container');
},
child: Container(
alignment: Alignment.center,
height:150,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image.asset('images/img_640_640.jpg', width:50),
constText(
"文本区域\n"
"文本区域\n"
"文本区域\n"
"文本区域\n",
style:
TextStyle(color: Colors.white, backgroundColor: Colors.red),
),
ColoredBox(
color: Colors.red.withOpacity(0.5),
child:constSizedBox(
width:150,
height:100,
child: Center(
child: Text(
"behavior是opaque或translucent\n点击空白区域才干呼应",
style: TextStyle(color: Colors.white),
),
),
),
)
],
),
),
),
);
  1. 默许情况下点击子节点Container内部子组件能够呼应点击事情
  2. 点击子节点Container内部空白区域无法呼应点击事情
  3. 修改GestureDetectorbehaviorHitTestBehavior.translucent点击边框内任务区域都能呼应点击事情

剖析缘由

Container是复合组件由多种其他组件嵌套而成,例如装备color会嵌套ColoredBox,增加边框decoration会嵌套DecoratedBox

ColoredBox特点

ColoredBox内部完成_RenderColoredBox,它的射中测验逻辑是由RenderProxyBoxWithHitTestBehavior判别

/flutter/packages/flutter/lib/src/rendering/proxy_box.dart:RenderProxyBoxWithHitTestBehavior

@override
boolhitTest(BoxHitTestResult result, {requiredOffset position }) {
boolhitTarget =false;
if(size.contains(position)) {
hitTarget = hitTestChildren(result, position: position) || hitTestSelf(position);
if(hitTarget || behavior == HitTestBehavior.translucent) {
result.add(BoxHitTestEntry(this, position));
}
}
returnhitTarget;
}
@override
boolhitTestSelf(Offset position) => behavior == HitTestBehavior.opaque;

是否射中测验以hitTestChildren作为判别根据,因为默许情况下behaviordeferToChild。一般情况而言子节点射中测验都是true,所以有ColorContainer是一般是射中测验的。
非也非也阅览_RenderColoredBox源码可知默许情况下behaviorHitTestBehavior.opaque状况,因而触摸组件监听时能够直接通过射中测验。

/flutter/packages/flutter/lib/src/widgets/basic.dart:_RenderColoredBox

class_RenderColoredBoxextendsRenderProxyBoxWithHitTestBehavior{
_RenderColoredBox({requiredColor color })
: _color = color,
super(behavior: HitTestBehavior.opaque);
...
}

DecoratedBox特点

DecoratedBox内部完成了RenderDecoratedBoxhitTestSelf方法完成由BoxDecoration接收,从内部完成看射中测验逻辑可知

/flutter/packages/flutter/lib/src/painting/box_decoration.dart:BoxDecoration

@override
boolhitTest(Size size, Offset position, { TextDirection? textDirection }) {
...
switch(shape) {
caseBoxShape.rectangle:// 矩形求边框是否在范围内
if(borderRadius !=null) {
finalRRect bounds = borderRadius!.resolve(textDirection).toRRect(Offset.zero & size);
returnbounds.contains(position);
}
returntrue;
caseBoxShape.circle:// 圆形求半径是否在射中范围内
finalOffset center = size.center(Offset.zero);
finaldoubledistance = (position - center).distance;
returndistance <= math.min(size.width, size.height) /2.0;
}
}

因而设置了边框的Container只要在边框范围内射中测验都是true

小总结

如上所知当Container没有设置ColorDecoratedBox特点时若要让Container整体射中测验就必须为点击组件设置为translucent或者opaque,相反则只能呼应Container内部子节点事情。