mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Add the ability to have translucent gesture detectors
A translucent gesture detector still listens for gestures but also lets the content visually behind the detector receive events.
This commit is contained in:
parent
f210b94996
commit
fdb351b620
@ -977,6 +977,12 @@ class RenderCustomPaint extends RenderProxyBox {
|
||||
|
||||
typedef void PointerEventListener(PointerInputEvent e);
|
||||
|
||||
enum HitTestBehavior {
|
||||
deferToChild,
|
||||
opaque,
|
||||
translucent,
|
||||
}
|
||||
|
||||
/// Invokes the callbacks in response to pointer events.
|
||||
class RenderPointerListener extends RenderProxyBox {
|
||||
RenderPointerListener({
|
||||
@ -984,6 +990,7 @@ class RenderPointerListener extends RenderProxyBox {
|
||||
this.onPointerMove,
|
||||
this.onPointerUp,
|
||||
this.onPointerCancel,
|
||||
this.behavior: HitTestBehavior.deferToChild,
|
||||
RenderBox child
|
||||
}) : super(child);
|
||||
|
||||
@ -991,6 +998,20 @@ class RenderPointerListener extends RenderProxyBox {
|
||||
PointerEventListener onPointerMove;
|
||||
PointerEventListener onPointerUp;
|
||||
PointerEventListener onPointerCancel;
|
||||
HitTestBehavior behavior;
|
||||
|
||||
bool hitTest(HitTestResult result, { Point position }) {
|
||||
bool hitTarget = false;
|
||||
if (position.x >= 0.0 && position.x < size.width &&
|
||||
position.y >= 0.0 && position.y < size.height) {
|
||||
hitTarget = hitTestChildren(result, position: position) || hitTestSelf(position);
|
||||
if (hitTarget || behavior == HitTestBehavior.translucent)
|
||||
result.add(new BoxHitTestEntry(this, position));
|
||||
}
|
||||
return hitTarget;
|
||||
}
|
||||
|
||||
bool hitTestSelf(Point position) => behavior == HitTestBehavior.opaque;
|
||||
|
||||
void handleEvent(InputEvent event, HitTestEntry entry) {
|
||||
if (onPointerDown != null && event.type == 'pointerdown')
|
||||
@ -1017,6 +1038,8 @@ class RenderPointerListener extends RenderProxyBox {
|
||||
if (listeners.isEmpty)
|
||||
listeners.add('<none>');
|
||||
settings.add('listeners: ${listeners.join(", ")}');
|
||||
if (behavior != HitTestBehavior.deferToChild)
|
||||
settings.add('behavior: $behavior');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -31,6 +31,7 @@ export 'package:flutter/rendering.dart' show
|
||||
FontWeight,
|
||||
FractionalOffset,
|
||||
Gradient,
|
||||
HitTestBehavior,
|
||||
ImageFit,
|
||||
ImageRepeat,
|
||||
InputEvent,
|
||||
@ -1229,19 +1230,24 @@ class Listener extends OneChildRenderObjectWidget {
|
||||
this.onPointerDown,
|
||||
this.onPointerMove,
|
||||
this.onPointerUp,
|
||||
this.onPointerCancel
|
||||
}) : super(key: key, child: child);
|
||||
this.onPointerCancel,
|
||||
this.behavior: HitTestBehavior.deferToChild
|
||||
}) : super(key: key, child: child) {
|
||||
assert(behavior != null);
|
||||
}
|
||||
|
||||
final PointerEventListener onPointerDown;
|
||||
final PointerEventListener onPointerMove;
|
||||
final PointerEventListener onPointerUp;
|
||||
final PointerEventListener onPointerCancel;
|
||||
final HitTestBehavior behavior;
|
||||
|
||||
RenderPointerListener createRenderObject() => new RenderPointerListener(
|
||||
onPointerDown: onPointerDown,
|
||||
onPointerMove: onPointerMove,
|
||||
onPointerUp: onPointerUp,
|
||||
onPointerCancel: onPointerCancel
|
||||
onPointerCancel: onPointerCancel,
|
||||
behavior: behavior
|
||||
);
|
||||
|
||||
void updateRenderObject(RenderPointerListener renderObject, Listener oldWidget) {
|
||||
@ -1249,6 +1255,7 @@ class Listener extends OneChildRenderObjectWidget {
|
||||
renderObject.onPointerMove = onPointerMove;
|
||||
renderObject.onPointerUp = onPointerUp;
|
||||
renderObject.onPointerCancel = onPointerCancel;
|
||||
renderObject.behavior = behavior;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -48,7 +48,8 @@ class GestureDetector extends StatefulComponent {
|
||||
this.onPanEnd,
|
||||
this.onScaleStart,
|
||||
this.onScaleUpdate,
|
||||
this.onScaleEnd
|
||||
this.onScaleEnd,
|
||||
this.behavior
|
||||
}) : super(key: key);
|
||||
|
||||
final Widget child;
|
||||
@ -77,6 +78,8 @@ class GestureDetector extends StatefulComponent {
|
||||
final GestureScaleUpdateCallback onScaleUpdate;
|
||||
final GestureScaleEndCallback onScaleEnd;
|
||||
|
||||
final HitTestBehavior behavior;
|
||||
|
||||
_GestureDetectorState createState() => new _GestureDetectorState();
|
||||
}
|
||||
|
||||
@ -224,9 +227,14 @@ class _GestureDetectorState extends State<GestureDetector> {
|
||||
_scale.addPointer(event);
|
||||
}
|
||||
|
||||
HitTestBehavior get _defaultBehavior {
|
||||
return config.child == null ? HitTestBehavior.translucent : HitTestBehavior.deferToChild;
|
||||
}
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return new Listener(
|
||||
onPointerDown: _handlePointerDown,
|
||||
behavior: config.behavior ?? _defaultBehavior,
|
||||
child: config.child
|
||||
);
|
||||
}
|
||||
|
||||
@ -134,4 +134,69 @@ void main() {
|
||||
expect(didEndPan, isTrue);
|
||||
});
|
||||
});
|
||||
|
||||
test('Translucent', () {
|
||||
testWidgets((WidgetTester tester) {
|
||||
bool didReceivePointerDown;
|
||||
bool didTap;
|
||||
|
||||
void pumpWidgetTree(HitTestBehavior behavior) {
|
||||
tester.pumpWidget(
|
||||
new Stack([
|
||||
new Listener(
|
||||
onPointerDown: (_) {
|
||||
didReceivePointerDown = true;
|
||||
},
|
||||
child: new Container(
|
||||
width: 100.0,
|
||||
height: 100.0,
|
||||
decoration: const BoxDecoration(
|
||||
backgroundColor: const Color(0xFF00FF00)
|
||||
)
|
||||
)
|
||||
),
|
||||
new Container(
|
||||
width: 100.0,
|
||||
height: 100.0,
|
||||
child: new GestureDetector(
|
||||
onTap: () {
|
||||
didTap = true;
|
||||
},
|
||||
behavior: behavior
|
||||
)
|
||||
)
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
didReceivePointerDown = false;
|
||||
didTap = false;
|
||||
pumpWidgetTree(null);
|
||||
tester.tapAt(new Point(10.0, 10.0));
|
||||
expect(didReceivePointerDown, isTrue);
|
||||
expect(didTap, isTrue);
|
||||
|
||||
didReceivePointerDown = false;
|
||||
didTap = false;
|
||||
pumpWidgetTree(HitTestBehavior.deferToChild);
|
||||
tester.tapAt(new Point(10.0, 10.0));
|
||||
expect(didReceivePointerDown, isTrue);
|
||||
expect(didTap, isFalse);
|
||||
|
||||
didReceivePointerDown = false;
|
||||
didTap = false;
|
||||
pumpWidgetTree(HitTestBehavior.opaque);
|
||||
tester.tapAt(new Point(10.0, 10.0));
|
||||
expect(didReceivePointerDown, isFalse);
|
||||
expect(didTap, isTrue);
|
||||
|
||||
didReceivePointerDown = false;
|
||||
didTap = false;
|
||||
pumpWidgetTree(HitTestBehavior.translucent);
|
||||
tester.tapAt(new Point(10.0, 10.0));
|
||||
expect(didReceivePointerDown, isTrue);
|
||||
expect(didTap, isTrue);
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user