Unverified Commit a48e1430 authored by Tong Mu's avatar Tong Mu Committed by GitHub

Expose GestureBinding.handlePointerEvent, replacing dispatchEvent as the...

Expose GestureBinding.handlePointerEvent, replacing dispatchEvent as the preferred way to dispatch events (#64846)
parent ea039ed3
...@@ -16,17 +16,6 @@ import 'package:e2e/e2e.dart'; ...@@ -16,17 +16,6 @@ import 'package:e2e/e2e.dart';
import 'package:complex_layout/main.dart' as app; import 'package:complex_layout/main.dart' as app;
class PointerDataTestBinding extends E2EWidgetsFlutterBinding { class PointerDataTestBinding extends E2EWidgetsFlutterBinding {
// PointerData injection would usually be considered device input and therefore
// blocked by [TestWidgetsFlutterBinding]. Override this behavior
// to help events go into widget tree.
@override
void dispatchEvent(
PointerEvent event,
HitTestResult hitTestResult, {
TestBindingEventSource source = TestBindingEventSource.device,
}) {
super.dispatchEvent(event, hitTestResult, source: TestBindingEventSource.test);
}
} }
/// A union of [ui.PointerDataPacket] and the time it should be sent. /// A union of [ui.PointerDataPacket] and the time it should be sent.
......
...@@ -144,13 +144,8 @@ class _Tester { ...@@ -144,13 +144,8 @@ class _Tester {
TestGesture get gesture { TestGesture get gesture {
return _gesture ??= TestGesture( return _gesture ??= TestGesture(
dispatcher: (PointerEvent event, HitTestResult result) async { dispatcher: (PointerEvent event) async {
RendererBinding.instance.dispatchEvent(event, result); RendererBinding.instance.handlePointerEvent(event);
},
hitTester: (Offset location) {
final HitTestResult result = HitTestResult();
RendererBinding.instance.hitTest(result, location);
return result;
}, },
kind: PointerDeviceKind.mouse, kind: PointerDeviceKind.mouse,
); );
......
...@@ -116,13 +116,8 @@ class _Tester { ...@@ -116,13 +116,8 @@ class _Tester {
TestGesture get gesture { TestGesture get gesture {
return _gesture ??= TestGesture( return _gesture ??= TestGesture(
dispatcher: (PointerEvent event, HitTestResult result) async { dispatcher: (PointerEvent event) async {
RendererBinding.instance.dispatchEvent(event, result); RendererBinding.instance.handlePointerEvent(event);
},
hitTester: (Offset location) {
final HitTestResult result = HitTestResult();
RendererBinding.instance.hitTest(result, location);
return result;
}, },
kind: PointerDeviceKind.mouse, kind: PointerDeviceKind.mouse,
); );
......
...@@ -166,13 +166,8 @@ class _Tester { ...@@ -166,13 +166,8 @@ class _Tester {
TestGesture get gesture { TestGesture get gesture {
return _gesture ??= TestGesture( return _gesture ??= TestGesture(
dispatcher: (PointerEvent event, HitTestResult result) async { dispatcher: (PointerEvent event) async {
RendererBinding.instance.dispatchEvent(event, result); RendererBinding.instance.handlePointerEvent(event);
},
hitTester: (Offset location) {
final HitTestResult result = HitTestResult();
RendererBinding.instance.hitTest(result, location);
return result;
}, },
kind: PointerDeviceKind.mouse, kind: PointerDeviceKind.mouse,
); );
......
...@@ -176,9 +176,9 @@ const Duration _defaultSamplingOffset = Duration(milliseconds: -38); ...@@ -176,9 +176,9 @@ const Duration _defaultSamplingOffset = Duration(milliseconds: -38);
/// ///
/// A pointer that is [PointerEvent.down] may send further events, such as /// A pointer that is [PointerEvent.down] may send further events, such as
/// [PointerMoveEvent], [PointerUpEvent], or [PointerCancelEvent]. These are /// [PointerMoveEvent], [PointerUpEvent], or [PointerCancelEvent]. These are
/// sent to the same [HitTestTarget] nodes as were found when the down event was /// sent to the same [HitTestTarget] nodes as were found when the
/// received (even if they have since been disposed; it is the responsibility of /// [PointerDownEvent] was received (even if they have since been disposed; it is
/// those objects to be aware of that possibility). /// the responsibility of those objects to be aware of that possibility).
/// ///
/// Then, the events are routed to any still-registered entrants in the /// Then, the events are routed to any still-registered entrants in the
/// [PointerRouter]'s table for that pointer. /// [PointerRouter]'s table for that pointer.
...@@ -237,7 +237,7 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H ...@@ -237,7 +237,7 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H
_resampler.stop(); _resampler.stop();
while (_pendingPointerEvents.isNotEmpty) while (_pendingPointerEvents.isNotEmpty)
_handlePointerEvent(_pendingPointerEvents.removeFirst()); handlePointerEvent(_pendingPointerEvents.removeFirst());
} }
/// A router that routes all pointer events received from the engine. /// A router that routes all pointer events received from the engine.
...@@ -247,8 +247,8 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H ...@@ -247,8 +247,8 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H
/// pointer events. /// pointer events.
final GestureArenaManager gestureArena = GestureArenaManager(); final GestureArenaManager gestureArena = GestureArenaManager();
/// The resolver used for determining which widget handles a pointer /// The resolver used for determining which widget handles a
/// signal event. /// [PointerSignalEvent].
final PointerSignalResolver pointerSignalResolver = PointerSignalResolver(); final PointerSignalResolver pointerSignalResolver = PointerSignalResolver();
/// State for all pointers which are currently down. /// State for all pointers which are currently down.
...@@ -257,7 +257,17 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H ...@@ -257,7 +257,17 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H
/// hit-testing on every frame. /// hit-testing on every frame.
final Map<int, HitTestResult> _hitTests = <int, HitTestResult>{}; final Map<int, HitTestResult> _hitTests = <int, HitTestResult>{};
void _handlePointerEvent(PointerEvent event) { /// Dispatch an event to the targets found by a hit test on its position.
///
/// This method sends the given event to [dispatchEvent] based on event types:
///
/// * [PointerDownEvent]s and [PointerSignalEvent]s are dispatched to the
/// result of a new [hitTest].
/// * [PointerUpEvent]s and [PointerMoveEvent]s are dispatched to the result of hit test of the
/// preceding [PointerDownEvent]s.
/// * [PointerHoverEvent]s, [PointerAddedEvent]s, and [PointerRemovedEvent]s
/// are dispatched without a hit test result.
void handlePointerEvent(PointerEvent event) {
assert(!locked); assert(!locked);
HitTestResult? hitTestResult; HitTestResult? hitTestResult;
if (event is PointerDownEvent || event is PointerSignalEvent) { if (event is PointerDownEvent || event is PointerSignalEvent) {
...@@ -276,7 +286,7 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H ...@@ -276,7 +286,7 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H
hitTestResult = _hitTests.remove(event.pointer); hitTestResult = _hitTests.remove(event.pointer);
} else if (event.down) { } else if (event.down) {
// Because events that occur with the pointer down (like // Because events that occur with the pointer down (like
// PointerMoveEvents) should be dispatched to the same place that their // [PointerMoveEvent]s) should be dispatched to the same place that their
// initial PointerDownEvent was, we want to re-use the path we found when // initial PointerDownEvent was, we want to re-use the path we found when
// the pointer went down, rather than do hit detection each time we get // the pointer went down, rather than do hit detection each time we get
// such an event. // such an event.
...@@ -302,18 +312,20 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H ...@@ -302,18 +312,20 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H
result.add(HitTestEntry(this)); result.add(HitTestEntry(this));
} }
/// Dispatch an event to a hit test result's path. /// Dispatch an event to [pointerRouter] and the path of a hit test result.
/// ///
/// This sends the given event to every [HitTestTarget] in the entries of the /// The `event` is routed to [pointerRouter]. If the `hitTestResult` is not
/// given [HitTestResult], and catches exceptions that any of the handlers /// null, the event is also sent to every [HitTestTarget] in the entries of the
/// might throw. The [hitTestResult] argument may only be null for /// given [HitTestResult]. Any exceptions from the handlers are caught.
/// [PointerHoverEvent], [PointerAddedEvent], or [PointerRemovedEvent] events. ///
/// The `hitTestResult` argument may only be null for [PointerHoverEvent]s,
/// [PointerAddedEvent]s, or [PointerRemovedEvent]s.
@override // from HitTestDispatcher @override // from HitTestDispatcher
void dispatchEvent(PointerEvent event, HitTestResult? hitTestResult) { void dispatchEvent(PointerEvent event, HitTestResult? hitTestResult) {
assert(!locked); assert(!locked);
// No hit test information implies that this is a pointer hover or // No hit test information implies that this is a [PointerHoverEvent],
// add/remove event. These events are specially routed here; other events // [PointerAddedEvent], or [PointerRemovedEvent]. These events are specially
// will be routed through the `handleEvent` below. // routed here; other events will be routed through the `handleEvent` below.
if (hitTestResult == null) { if (hitTestResult == null) {
assert(event is PointerHoverEvent || event is PointerAddedEvent || event is PointerRemovedEvent); assert(event is PointerHoverEvent || event is PointerAddedEvent || event is PointerRemovedEvent);
try { try {
...@@ -365,6 +377,16 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H ...@@ -365,6 +377,16 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H
} }
} }
/// Reset states of [GestureBinding].
///
/// This clears the hit test records.
///
/// This is typically called between tests.
@protected
void resetGestureBinding() {
_hitTests.clear();
}
void _handleSampleTimeChanged() { void _handleSampleTimeChanged() {
if (!locked) { if (!locked) {
_flushPointerEventQueue(); _flushPointerEventQueue();
...@@ -374,7 +396,7 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H ...@@ -374,7 +396,7 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H
// Resampler used to filter incoming pointer events when resampling // Resampler used to filter incoming pointer events when resampling
// is enabled. // is enabled.
late final _Resampler _resampler = _Resampler( late final _Resampler _resampler = _Resampler(
_handlePointerEvent, handlePointerEvent,
_handleSampleTimeChanged, _handleSampleTimeChanged,
); );
...@@ -429,7 +451,8 @@ class FlutterErrorDetailsForPointerEventDispatcher extends FlutterErrorDetails { ...@@ -429,7 +451,8 @@ class FlutterErrorDetailsForPointerEventDispatcher extends FlutterErrorDetails {
/// The hit test result entry for the object whose handleEvent method threw /// The hit test result entry for the object whose handleEvent method threw
/// the exception. May be null if no hit test entry is associated with the /// the exception. May be null if no hit test entry is associated with the
/// event (e.g. hover and pointer add/remove events). /// event (e.g. [PointerHoverEvent]s, [PointerAddedEvent]s, and
/// [PointerRemovedEvent]s).
/// ///
/// The target object itself is given by the [HitTestEntry.target] property of /// The target object itself is given by the [HitTestEntry.target] property of
/// the hitTestEntry object. /// the hitTestEntry object.
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
...@@ -1471,6 +1472,25 @@ void main() { ...@@ -1471,6 +1472,25 @@ void main() {
expect(find.text('first', skipOffstage: false), findsOneWidget); expect(find.text('first', skipOffstage: false), findsOneWidget);
expect(find.text('second'), findsOneWidget); expect(find.text('second'), findsOneWidget);
}); });
testWidgets('Popping routes should cancel down events', (WidgetTester tester) async {
await tester.pumpWidget(_TestPostRouteCancel());
final TestGesture gesture = await tester.createGesture();
await gesture.down(tester.getCenter(find.text('PointerCancelEvents: 0')));
await gesture.up();
await tester.pumpAndSettle();
expect(find.byType(CupertinoButton), findsNothing);
expect(find.text('Hold'), findsOneWidget);
await gesture.down(tester.getCenter(find.text('Hold')));
await tester.pump(const Duration(seconds: 2));
await tester.pumpAndSettle();
expect(find.text('Hold'), findsNothing);
expect(find.byType(CupertinoButton), findsOneWidget);
expect(find.text('PointerCancelEvents: 1'), findsOneWidget);
});
} }
class MockNavigatorObserver extends NavigatorObserver { class MockNavigatorObserver extends NavigatorObserver {
...@@ -1571,3 +1591,75 @@ Widget buildNavigator({ ...@@ -1571,3 +1591,75 @@ Widget buildNavigator({
), ),
); );
} }
// A test target for post-route cancel events.
//
// It contains 2 routes:
//
// * The initial route, 'home', displays a button showing 'PointerCancelEvents: #',
// where # is the number of cancel events received. Tapping the button pushes
// route 'sub'.
// * The 'sub' route, displays a text showing 'Hold'. Holding the button (a down
// event) will pop this route after 1 second.
//
// Holding the 'Hold' button at the moment of popping will force the navigator to
// cancel the down event, increasing the Home counter by 1.
class _TestPostRouteCancel extends StatefulWidget {
@override
State<StatefulWidget> createState() => _TestPostRouteCancelState();
}
class _TestPostRouteCancelState extends State<_TestPostRouteCancel> {
int counter = 0;
Widget _buildHome(BuildContext context) {
return Center(
child: CupertinoButton(
child: Text('PointerCancelEvents: $counter'),
onPressed: () => Navigator.pushNamed<void>(context, 'sub'),
),
);
}
Widget _buildSub(BuildContext context) {
return Listener(
onPointerDown: (_) {
Future<void>.delayed(const Duration(seconds: 1)).then((_) {
Navigator.pop(context);
});
},
onPointerCancel: (_) {
setState(() {
counter += 1;
});
},
child: const Center(
child: Text('Hold', style: TextStyle(color: Colors.blue)),
),
);
}
@override
Widget build(BuildContext context) {
return CupertinoApp(
initialRoute: 'home',
onGenerateRoute: (RouteSettings settings) {
return CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
switch (settings.name) {
case 'home':
return _buildHome(context);
case 'sub':
return _buildSub(context);
default:
throw UnimplementedError();
}
},
);
},
);
}
}
...@@ -16,22 +16,8 @@ import 'package:flutter/gestures.dart'; ...@@ -16,22 +16,8 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
class PointerDataAutomatedTestWidgetsFlutterBinding extends AutomatedTestWidgetsFlutterBinding {
// PointerData injection would usually considerred device input and therefore
// blocked by [AutomatedTestWidgetsFlutterBinding]. Override this behavior
// to help events go into widget tree.
@override
void dispatchEvent(
PointerEvent event,
HitTestResult hitTestResult, {
TestBindingEventSource source = TestBindingEventSource.device,
}) {
super.dispatchEvent(event, hitTestResult, source: TestBindingEventSource.test);
}
}
void main() { void main() {
final TestWidgetsFlutterBinding binding = PointerDataAutomatedTestWidgetsFlutterBinding(); final TestWidgetsFlutterBinding binding = AutomatedTestWidgetsFlutterBinding();
testWidgets('PointerEvent resampling on a widget', (WidgetTester tester) async { testWidgets('PointerEvent resampling on a widget', (WidgetTester tester) async {
assert(WidgetsBinding.instance == binding); assert(WidgetsBinding.instance == binding);
Duration currentTestFrameTime() => Duration(milliseconds: binding.clock.now().millisecondsSinceEpoch); Duration currentTestFrameTime() => Duration(milliseconds: binding.clock.now().millisecondsSinceEpoch);
......
...@@ -753,6 +753,7 @@ void main() { ...@@ -753,6 +753,7 @@ void main() {
await hoverGesture.addPointer(); await hoverGesture.addPointer();
await hoverGesture.moveTo(center); await hoverGesture.moveTo(center);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
await hoverGesture.moveTo(const Offset(0, 0));
inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) { inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) {
return object.runtimeType.toString() == '_RenderInkFeatures'; return object.runtimeType.toString() == '_RenderInkFeatures';
...@@ -761,7 +762,6 @@ void main() { ...@@ -761,7 +762,6 @@ void main() {
inkFeatures, inkFeatures,
paints..rect(color: theme.colorScheme.onSurface.withOpacity(0.04)), paints..rect(color: theme.colorScheme.onSurface.withOpacity(0.04)),
); );
await hoverGesture.removePointer();
// focusColor // focusColor
focusNode.requestFocus(); focusNode.requestFocus();
...@@ -770,6 +770,8 @@ void main() { ...@@ -770,6 +770,8 @@ void main() {
return object.runtimeType.toString() == '_RenderInkFeatures'; return object.runtimeType.toString() == '_RenderInkFeatures';
}); });
expect(inkFeatures, paints..rect(color: theme.colorScheme.onSurface.withOpacity(0.12))); expect(inkFeatures, paints..rect(color: theme.colorScheme.onSurface.withOpacity(0.12)));
await hoverGesture.removePointer();
}); });
testWidgets('Default InkWell colors - selected', (WidgetTester tester) async { testWidgets('Default InkWell colors - selected', (WidgetTester tester) async {
...@@ -825,7 +827,7 @@ void main() { ...@@ -825,7 +827,7 @@ void main() {
inkFeatures, inkFeatures,
paints..rect(color: theme.colorScheme.primary.withOpacity(0.04)), paints..rect(color: theme.colorScheme.primary.withOpacity(0.04)),
); );
await hoverGesture.removePointer(); await hoverGesture.moveTo(const Offset(0, 0));
// focusColor // focusColor
focusNode.requestFocus(); focusNode.requestFocus();
...@@ -834,6 +836,8 @@ void main() { ...@@ -834,6 +836,8 @@ void main() {
return object.runtimeType.toString() == '_RenderInkFeatures'; return object.runtimeType.toString() == '_RenderInkFeatures';
}); });
expect(inkFeatures, paints..rect(color: theme.colorScheme.primary.withOpacity(0.12))); expect(inkFeatures, paints..rect(color: theme.colorScheme.primary.withOpacity(0.12)));
await hoverGesture.removePointer();
}); });
testWidgets('Custom InkWell colors', (WidgetTester tester) async { testWidgets('Custom InkWell colors', (WidgetTester tester) async {
...@@ -894,7 +898,7 @@ void main() { ...@@ -894,7 +898,7 @@ void main() {
return object.runtimeType.toString() == '_RenderInkFeatures'; return object.runtimeType.toString() == '_RenderInkFeatures';
}); });
expect(inkFeatures, paints..rect(color: hoverColor)); expect(inkFeatures, paints..rect(color: hoverColor));
await hoverGesture.removePointer(); await hoverGesture.moveTo(const Offset(0, 0));
// focusColor // focusColor
focusNode.requestFocus(); focusNode.requestFocus();
...@@ -903,6 +907,8 @@ void main() { ...@@ -903,6 +907,8 @@ void main() {
return object.runtimeType.toString() == '_RenderInkFeatures'; return object.runtimeType.toString() == '_RenderInkFeatures';
}); });
expect(inkFeatures, paints..rect(color: focusColor)); expect(inkFeatures, paints..rect(color: focusColor));
await hoverGesture.removePointer();
}); });
testWidgets( testWidgets(
......
...@@ -429,7 +429,7 @@ void main() { ...@@ -429,7 +429,7 @@ void main() {
return object.runtimeType.toString() == '_RenderInkFeatures'; return object.runtimeType.toString() == '_RenderInkFeatures';
}); });
expect(inkFeatures, paints..rect(color: hoverColor)); expect(inkFeatures, paints..rect(color: hoverColor));
await hoverGesture.removePointer(); await hoverGesture.moveTo(const Offset(0, 0));
// focusColor // focusColor
focusNode.requestFocus(); focusNode.requestFocus();
...@@ -438,6 +438,8 @@ void main() { ...@@ -438,6 +438,8 @@ void main() {
return object.runtimeType.toString() == '_RenderInkFeatures'; return object.runtimeType.toString() == '_RenderInkFeatures';
}); });
expect(inkFeatures, paints..rect(color: focusColor)); expect(inkFeatures, paints..rect(color: focusColor));
await hoverGesture.removePointer();
}); });
......
...@@ -11,6 +11,5 @@ Future<void> scrollAt(Offset position, WidgetTester tester, [Offset offset = con ...@@ -11,6 +11,5 @@ Future<void> scrollAt(Offset position, WidgetTester tester, [Offset offset = con
final TestPointer testPointer = TestPointer(1, PointerDeviceKind.mouse); final TestPointer testPointer = TestPointer(1, PointerDeviceKind.mouse);
// Create a hover event so that |testPointer| has a location when generating the scroll. // Create a hover event so that |testPointer| has a location when generating the scroll.
testPointer.hover(position); testPointer.hover(position);
final HitTestResult result = tester.hitTestOnBinding(position); return tester.sendEventToBinding(testPointer.scroll(offset));
return tester.sendEventToBinding(testPointer.scroll(offset), result);
} }
...@@ -290,11 +290,10 @@ void main() { ...@@ -290,11 +290,10 @@ void main() {
final TestPointer testPointer = TestPointer(1, ui.PointerDeviceKind.mouse); final TestPointer testPointer = TestPointer(1, ui.PointerDeviceKind.mouse);
// Create a hover event so that |testPointer| has a location when generating the scroll. // Create a hover event so that |testPointer| has a location when generating the scroll.
testPointer.hover(scrollEventLocation); testPointer.hover(scrollEventLocation);
final HitTestResult result = tester.hitTestOnBinding(scrollEventLocation); await tester.sendEventToBinding(testPointer.scroll(const Offset(0.0, 20.0)));
await tester.sendEventToBinding(testPointer.scroll(const Offset(0.0, 20.0)), result);
expect(getScrollOffset(tester), 20.0); expect(getScrollOffset(tester), 20.0);
// Pointer signals should not cause overscroll. // Pointer signals should not cause overscroll.
await tester.sendEventToBinding(testPointer.scroll(const Offset(0.0, -30.0)), result); await tester.sendEventToBinding(testPointer.scroll(const Offset(0.0, -30.0)));
expect(getScrollOffset(tester), 0.0); expect(getScrollOffset(tester), 0.0);
}); });
...@@ -308,11 +307,10 @@ void main() { ...@@ -308,11 +307,10 @@ void main() {
final TestPointer testPointer = TestPointer(1, ui.PointerDeviceKind.mouse); final TestPointer testPointer = TestPointer(1, ui.PointerDeviceKind.mouse);
// Create a hover event so that |testPointer| has a location when generating the scroll. // Create a hover event so that |testPointer| has a location when generating the scroll.
testPointer.hover(scrollEventLocation); testPointer.hover(scrollEventLocation);
final HitTestResult result = tester.hitTestOnBinding(scrollEventLocation); await tester.sendEventToBinding(testPointer.scroll(const Offset(0.0, 20.0)));
await tester.sendEventToBinding(testPointer.scroll(const Offset(0.0, 20.0)), result);
expect(getScrollOffset(tester, last: true), 20.0); expect(getScrollOffset(tester, last: true), 20.0);
// Pointer signals should not cause overscroll. // Pointer signals should not cause overscroll.
await tester.sendEventToBinding(testPointer.scroll(const Offset(0.0, -30.0)), result); await tester.sendEventToBinding(testPointer.scroll(const Offset(0.0, -30.0)));
expect(getScrollOffset(tester, last: true), 0.0); expect(getScrollOffset(tester, last: true), 0.0);
}); });
...@@ -322,8 +320,7 @@ void main() { ...@@ -322,8 +320,7 @@ void main() {
final TestPointer testPointer = TestPointer(1, ui.PointerDeviceKind.mouse); final TestPointer testPointer = TestPointer(1, ui.PointerDeviceKind.mouse);
// Create a hover event so that |testPointer| has a location when generating the scroll. // Create a hover event so that |testPointer| has a location when generating the scroll.
testPointer.hover(scrollEventLocation); testPointer.hover(scrollEventLocation);
final HitTestResult result = tester.hitTestOnBinding(scrollEventLocation); await tester.sendEventToBinding(testPointer.scroll(const Offset(0.0, 20.0)));
await tester.sendEventToBinding(testPointer.scroll(const Offset(0.0, 20.0)), result);
expect(getScrollOffset(tester), 0.0); expect(getScrollOffset(tester), 0.0);
}); });
...@@ -334,8 +331,7 @@ void main() { ...@@ -334,8 +331,7 @@ void main() {
final TestPointer testPointer = TestPointer(1, ui.PointerDeviceKind.mouse); final TestPointer testPointer = TestPointer(1, ui.PointerDeviceKind.mouse);
// Create a hover event so that |testPointer| has a location when generating the scroll. // Create a hover event so that |testPointer| has a location when generating the scroll.
testPointer.hover(scrollEventLocation); testPointer.hover(scrollEventLocation);
final HitTestResult result = tester.hitTestOnBinding(scrollEventLocation); await tester.sendEventToBinding(testPointer.scroll(const Offset(0.0, -20.0)));
await tester.sendEventToBinding(testPointer.scroll(const Offset(0.0, -20.0)), result);
expect(getScrollOffset(tester), 20.0); expect(getScrollOffset(tester), 20.0);
}); });
......
...@@ -596,17 +596,14 @@ class FlutterDriverExtension with DeserializeFinderFactory { ...@@ -596,17 +596,14 @@ class FlutterDriverExtension with DeserializeFinderFactory {
final Offset startLocation = _prober.getCenter(target); final Offset startLocation = _prober.getCenter(target);
Offset currentLocation = startLocation; Offset currentLocation = startLocation;
final TestPointer pointer = TestPointer(1); final TestPointer pointer = TestPointer(1);
final HitTestResult hitTest = HitTestResult(); _prober.binding.handlePointerEvent(pointer.down(startLocation));
_prober.binding.hitTest(hitTest, startLocation);
_prober.binding.dispatchEvent(pointer.down(startLocation), hitTest);
await Future<void>.value(); // so that down and move don't happen in the same microtask await Future<void>.value(); // so that down and move don't happen in the same microtask
for (int moves = 0; moves < totalMoves; moves += 1) { for (int moves = 0; moves < totalMoves; moves += 1) {
currentLocation = currentLocation + delta; currentLocation = currentLocation + delta;
_prober.binding.dispatchEvent(pointer.move(currentLocation), hitTest); _prober.binding.handlePointerEvent(pointer.move(currentLocation));
await Future<void>.delayed(pause); await Future<void>.delayed(pause);
} }
_prober.binding.dispatchEvent(pointer.up(), hitTest); _prober.binding.handlePointerEvent(pointer.up());
return const ScrollResult(); return const ScrollResult();
} }
......
...@@ -196,6 +196,7 @@ abstract class TestWidgetsFlutterBinding extends BindingBase ...@@ -196,6 +196,7 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
/// prepare the binding for the next test. /// prepare the binding for the next test.
void reset() { void reset() {
_restorationManager = createRestorationManager(); _restorationManager = createRestorationManager();
resetGestureBinding();
} }
@override @override
...@@ -488,19 +489,25 @@ abstract class TestWidgetsFlutterBinding extends BindingBase ...@@ -488,19 +489,25 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
/// events from the device). /// events from the device).
Offset localToGlobal(Offset point) => point; Offset localToGlobal(Offset point) => point;
// The source of the current pointer event.
//
// The [pointerEventSource] is set as the `source` parameter of
// [handlePointerEvent] and can be used in the immediate enclosing
// [dispatchEvent].
TestBindingEventSource _pointerEventSource = TestBindingEventSource.device;
@override @override
void dispatchEvent( void handlePointerEvent(
PointerEvent event, PointerEvent event, {
HitTestResult hitTestResult, {
TestBindingEventSource source = TestBindingEventSource.device, TestBindingEventSource source = TestBindingEventSource.device,
}) { }) {
// This override disables calling this method from base class final TestBindingEventSource previousSource = source;
// [GestureBinding] when the runtime type is [TestWidgetsFlutterBinding], _pointerEventSource = source;
// while enables sub class [LiveTestWidgetsFlutterBinding] to override try {
// this behavior and use this argument to determine the souce of the event super.handlePointerEvent(event);
// especially when the test app is running on a device. } finally {
assert(source == TestBindingEventSource.test); _pointerEventSource = previousSource;
super.dispatchEvent(event, hitTestResult); }
} }
/// A stub for the system's onscreen keyboard. Callers must set the /// A stub for the system's onscreen keyboard. Callers must set the
...@@ -1486,14 +1493,13 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding { ...@@ -1486,14 +1493,13 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
HitTestDispatcher deviceEventDispatcher; HitTestDispatcher deviceEventDispatcher;
/// Dispatch an event to a hit test result's path. /// Dispatch an event to the targets found by a hit test on its position.
/// ///
/// Apart from forwarding the event to [GestureBinding.dispatchEvent], /// Apart from forwarding the event to [GestureBinding.dispatchEvent],
/// This also paint all events that's down on the screen. /// This also paint all events that's down on the screen.
@override @override
void dispatchEvent( void handlePointerEvent(
PointerEvent event, PointerEvent event, {
HitTestResult hitTestResult, {
TestBindingEventSource source = TestBindingEventSource.device, TestBindingEventSource source = TestBindingEventSource.device,
}) { }) {
switch (source) { switch (source) {
...@@ -1510,11 +1516,23 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding { ...@@ -1510,11 +1516,23 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
); );
_handleViewNeedsPaint(); _handleViewNeedsPaint();
} }
super.dispatchEvent(event, hitTestResult, source: source); super.handlePointerEvent(event, source: TestBindingEventSource.test);
break; break;
case TestBindingEventSource.device: case TestBindingEventSource.device:
if (deviceEventDispatcher != null) if (deviceEventDispatcher != null)
deviceEventDispatcher.dispatchEvent(event, hitTestResult); super.handlePointerEvent(event, source: TestBindingEventSource.device);
break;
}
}
@override
void dispatchEvent(PointerEvent event, HitTestResult hitTestResult) {
switch (_pointerEventSource) {
case TestBindingEventSource.test:
super.dispatchEvent(event, hitTestResult);
break;
case TestBindingEventSource.device:
deviceEventDispatcher.dispatchEvent(event, hitTestResult);
break; break;
} }
} }
......
...@@ -378,27 +378,26 @@ abstract class WidgetController { ...@@ -378,27 +378,26 @@ abstract class WidgetController {
assert(speed > 0.0); // speed is pixels/second assert(speed > 0.0); // speed is pixels/second
return TestAsyncUtils.guard<void>(() async { return TestAsyncUtils.guard<void>(() async {
final TestPointer testPointer = TestPointer(pointer ?? _getNextPointer(), PointerDeviceKind.touch, null, buttons); final TestPointer testPointer = TestPointer(pointer ?? _getNextPointer(), PointerDeviceKind.touch, null, buttons);
final HitTestResult result = hitTestOnBinding(startLocation);
const int kMoveCount = 50; // Needs to be >= kHistorySize, see _LeastSquaresVelocityTrackerStrategy const int kMoveCount = 50; // Needs to be >= kHistorySize, see _LeastSquaresVelocityTrackerStrategy
final double timeStampDelta = 1000000.0 * offset.distance / (kMoveCount * speed); final double timeStampDelta = 1000000.0 * offset.distance / (kMoveCount * speed);
double timeStamp = 0.0; double timeStamp = 0.0;
double lastTimeStamp = timeStamp; double lastTimeStamp = timeStamp;
await sendEventToBinding(testPointer.down(startLocation, timeStamp: Duration(microseconds: timeStamp.round())), result); await sendEventToBinding(testPointer.down(startLocation, timeStamp: Duration(microseconds: timeStamp.round())));
if (initialOffset.distance > 0.0) { if (initialOffset.distance > 0.0) {
await sendEventToBinding(testPointer.move(startLocation + initialOffset, timeStamp: Duration(microseconds: timeStamp.round())), result); await sendEventToBinding(testPointer.move(startLocation + initialOffset, timeStamp: Duration(microseconds: timeStamp.round())));
timeStamp += initialOffsetDelay.inMicroseconds; timeStamp += initialOffsetDelay.inMicroseconds;
await pump(initialOffsetDelay); await pump(initialOffsetDelay);
} }
for (int i = 0; i <= kMoveCount; i += 1) { for (int i = 0; i <= kMoveCount; i += 1) {
final Offset location = startLocation + initialOffset + Offset.lerp(Offset.zero, offset, i / kMoveCount); final Offset location = startLocation + initialOffset + Offset.lerp(Offset.zero, offset, i / kMoveCount);
await sendEventToBinding(testPointer.move(location, timeStamp: Duration(microseconds: timeStamp.round())), result); await sendEventToBinding(testPointer.move(location, timeStamp: Duration(microseconds: timeStamp.round())));
timeStamp += timeStampDelta; timeStamp += timeStampDelta;
if (timeStamp - lastTimeStamp > frameInterval.inMicroseconds) { if (timeStamp - lastTimeStamp > frameInterval.inMicroseconds) {
await pump(Duration(microseconds: (timeStamp - lastTimeStamp).truncate())); await pump(Duration(microseconds: (timeStamp - lastTimeStamp).truncate()));
lastTimeStamp = timeStamp; lastTimeStamp = timeStamp;
} }
} }
await sendEventToBinding(testPointer.up(timeStamp: Duration(microseconds: timeStamp.round())), result); await sendEventToBinding(testPointer.up(timeStamp: Duration(microseconds: timeStamp.round())));
}); });
} }
...@@ -739,7 +738,6 @@ abstract class WidgetController { ...@@ -739,7 +738,6 @@ abstract class WidgetController {
int buttons = kPrimaryButton, int buttons = kPrimaryButton,
}) async { }) async {
return TestGesture( return TestGesture(
hitTester: hitTestOnBinding,
dispatcher: sendEventToBinding, dispatcher: sendEventToBinding,
kind: kind, kind: kind,
pointer: pointer ?? _getNextPointer(), pointer: pointer ?? _getNextPointer(),
...@@ -777,9 +775,9 @@ abstract class WidgetController { ...@@ -777,9 +775,9 @@ abstract class WidgetController {
} }
/// Forwards the given pointer event to the binding. /// Forwards the given pointer event to the binding.
Future<void> sendEventToBinding(PointerEvent event, HitTestResult result) { Future<void> sendEventToBinding(PointerEvent event) {
return TestAsyncUtils.guard<void>(() async { return TestAsyncUtils.guard<void>(() async {
binding.dispatchEvent(event, result); binding.handlePointerEvent(event);
}); });
} }
...@@ -1087,9 +1085,7 @@ class LiveWidgetController extends WidgetController { ...@@ -1087,9 +1085,7 @@ class LiveWidgetController extends WidgetController {
// processing of the events. // processing of the events.
// Flush all past events // Flush all past events
handleTimeStampDiff.add(-timeDiff); handleTimeStampDiff.add(-timeDiff);
for (final PointerEvent event in record.events) { record.events.forEach(binding.handlePointerEvent);
_handlePointerEvent(event, hitTestHistory);
}
} else { } else {
await Future<void>.delayed(timeDiff); await Future<void>.delayed(timeDiff);
handleTimeStampDiff.add( handleTimeStampDiff.add(
...@@ -1098,9 +1094,7 @@ class LiveWidgetController extends WidgetController { ...@@ -1098,9 +1094,7 @@ class LiveWidgetController extends WidgetController {
// fake async this new diff should be zero. // fake async this new diff should be zero.
clock.now().difference(startTime) - record.timeDelay, clock.now().difference(startTime) - record.timeDelay,
); );
for (final PointerEvent event in record.events) { record.events.forEach(binding.handlePointerEvent);
_handlePointerEvent(event, hitTestHistory);
}
} }
} }
// This makes sure that a gesture is completed, with no more pointers // This makes sure that a gesture is completed, with no more pointers
...@@ -1109,46 +1103,4 @@ class LiveWidgetController extends WidgetController { ...@@ -1109,46 +1103,4 @@ class LiveWidgetController extends WidgetController {
return handleTimeStampDiff; return handleTimeStampDiff;
}); });
} }
// This method is almost identical to [GestureBinding._handlePointerEvent]
// to replicate the behavior of the real binding.
void _handlePointerEvent(
PointerEvent event,
Map<int, HitTestResult> _hitTests
) {
HitTestResult hitTestResult;
if (event is PointerDownEvent || event is PointerSignalEvent) {
assert(!_hitTests.containsKey(event.pointer));
hitTestResult = HitTestResult();
binding.hitTest(hitTestResult, event.position);
if (event is PointerDownEvent) {
_hitTests[event.pointer] = hitTestResult;
}
assert(() {
if (debugPrintHitTestResults)
debugPrint('$event: $hitTestResult');
return true;
}());
} else if (event is PointerUpEvent || event is PointerCancelEvent) {
hitTestResult = _hitTests.remove(event.pointer);
} else if (event.down) {
// Because events that occur with the pointer down (like
// PointerMoveEvents) should be dispatched to the same place that their
// initial PointerDownEvent was, we want to re-use the path we found when
// the pointer went down, rather than do hit detection each time we get
// such an event.
hitTestResult = _hitTests[event.pointer];
}
assert(() {
if (debugPrintMouseHoverEvents && event is PointerHoverEvent)
debugPrint('$event');
return true;
}());
if (hitTestResult != null ||
event is PointerHoverEvent ||
event is PointerAddedEvent ||
event is PointerRemovedEvent) {
binding.dispatchEvent(event, hitTestResult);
}
}
} }
...@@ -295,7 +295,7 @@ class TestPointer { ...@@ -295,7 +295,7 @@ class TestPointer {
/// Signature for a callback that can dispatch events and returns a future that /// Signature for a callback that can dispatch events and returns a future that
/// completes when the event dispatch is complete. /// completes when the event dispatch is complete.
typedef EventDispatcher = Future<void> Function(PointerEvent event, HitTestResult result); typedef EventDispatcher = Future<void> Function(PointerEvent event);
/// Signature for callbacks that perform hit-testing at a given location. /// Signature for callbacks that perform hit-testing at a given location.
typedef HitTester = HitTestResult Function(Offset location); typedef HitTester = HitTestResult Function(Offset location);
...@@ -324,27 +324,22 @@ class TestGesture { ...@@ -324,27 +324,22 @@ class TestGesture {
/// arguments are required. /// arguments are required.
TestGesture({ TestGesture({
@required EventDispatcher dispatcher, @required EventDispatcher dispatcher,
@required HitTester hitTester,
int pointer = 1, int pointer = 1,
PointerDeviceKind kind = PointerDeviceKind.touch, PointerDeviceKind kind = PointerDeviceKind.touch,
int device, int device,
int buttons = kPrimaryButton, int buttons = kPrimaryButton,
}) : assert(dispatcher != null), }) : assert(dispatcher != null),
assert(hitTester != null),
assert(pointer != null), assert(pointer != null),
assert(kind != null), assert(kind != null),
assert(buttons != null), assert(buttons != null),
_dispatcher = dispatcher, _dispatcher = dispatcher,
_hitTester = hitTester, _pointer = TestPointer(pointer, kind, device, buttons);
_pointer = TestPointer(pointer, kind, device, buttons),
_result = null;
/// Dispatch a pointer down event at the given `downLocation`, caching the /// Dispatch a pointer down event at the given `downLocation`, caching the
/// hit test result. /// hit test result.
Future<void> down(Offset downLocation, { Duration timeStamp = Duration.zero }) async { Future<void> down(Offset downLocation, { Duration timeStamp = Duration.zero }) async {
return TestAsyncUtils.guard<void>(() async { return TestAsyncUtils.guard<void>(() async {
_result = _hitTester(downLocation); return _dispatcher(_pointer.down(downLocation, timeStamp: timeStamp));
return _dispatcher(_pointer.down(downLocation, timeStamp: timeStamp), _result);
}); });
} }
...@@ -353,36 +348,33 @@ class TestGesture { ...@@ -353,36 +348,33 @@ class TestGesture {
Future<void> downWithCustomEvent(Offset downLocation, PointerDownEvent event) async { Future<void> downWithCustomEvent(Offset downLocation, PointerDownEvent event) async {
_pointer.setDownInfo(event, downLocation); _pointer.setDownInfo(event, downLocation);
return TestAsyncUtils.guard<void>(() async { return TestAsyncUtils.guard<void>(() async {
_result = _hitTester(downLocation); return _dispatcher(event);
return _dispatcher(event, _result);
}); });
} }
final EventDispatcher _dispatcher; final EventDispatcher _dispatcher;
final HitTester _hitTester;
final TestPointer _pointer; final TestPointer _pointer;
HitTestResult _result;
/// In a test, send a move event that moves the pointer by the given offset. /// In a test, send a move event that moves the pointer by the given offset.
@visibleForTesting @visibleForTesting
Future<void> updateWithCustomEvent(PointerEvent event, { Duration timeStamp = Duration.zero }) { Future<void> updateWithCustomEvent(PointerEvent event, { Duration timeStamp = Duration.zero }) {
_pointer.setDownInfo(event, event.position); _pointer.setDownInfo(event, event.position);
return TestAsyncUtils.guard<void>(() { return TestAsyncUtils.guard<void>(() {
return _dispatcher(event, _result); return _dispatcher(event);
}); });
} }
/// In a test, send a pointer add event for this pointer. /// In a test, send a pointer add event for this pointer.
Future<void> addPointer({ Duration timeStamp = Duration.zero, Offset location }) { Future<void> addPointer({ Duration timeStamp = Duration.zero, Offset location }) {
return TestAsyncUtils.guard<void>(() { return TestAsyncUtils.guard<void>(() {
return _dispatcher(_pointer.addPointer(timeStamp: timeStamp, location: location ?? _pointer.location), null); return _dispatcher(_pointer.addPointer(timeStamp: timeStamp, location: location ?? _pointer.location));
}); });
} }
/// In a test, send a pointer remove event for this pointer. /// In a test, send a pointer remove event for this pointer.
Future<void> removePointer({ Duration timeStamp = Duration.zero, Offset location }) { Future<void> removePointer({ Duration timeStamp = Duration.zero, Offset location }) {
return TestAsyncUtils.guard<void>(() { return TestAsyncUtils.guard<void>(() {
return _dispatcher(_pointer.removePointer(timeStamp: timeStamp, location: location ?? _pointer.location), null); return _dispatcher(_pointer.removePointer(timeStamp: timeStamp, location: location ?? _pointer.location));
}); });
} }
...@@ -403,14 +395,11 @@ class TestGesture { ...@@ -403,14 +395,11 @@ class TestGesture {
Future<void> moveTo(Offset location, { Duration timeStamp = Duration.zero }) { Future<void> moveTo(Offset location, { Duration timeStamp = Duration.zero }) {
return TestAsyncUtils.guard<void>(() { return TestAsyncUtils.guard<void>(() {
if (_pointer._isDown) { if (_pointer._isDown) {
assert(_result != null, return _dispatcher(_pointer.move(location, timeStamp: timeStamp));
'Move events with the pointer down must be preceded by a down '
'event that captures a hit test result.');
return _dispatcher(_pointer.move(location, timeStamp: timeStamp), _result);
} else { } else {
assert(_pointer.kind != PointerDeviceKind.touch, assert(_pointer.kind != PointerDeviceKind.touch,
'Touch device move events can only be sent if the pointer is down.'); 'Touch device move events can only be sent if the pointer is down.');
return _dispatcher(_pointer.hover(location, timeStamp: timeStamp), null); return _dispatcher(_pointer.hover(location, timeStamp: timeStamp));
} }
}); });
} }
...@@ -419,9 +408,8 @@ class TestGesture { ...@@ -419,9 +408,8 @@ class TestGesture {
Future<void> up({ Duration timeStamp = Duration.zero }) { Future<void> up({ Duration timeStamp = Duration.zero }) {
return TestAsyncUtils.guard<void>(() async { return TestAsyncUtils.guard<void>(() async {
assert(_pointer._isDown); assert(_pointer._isDown);
await _dispatcher(_pointer.up(timeStamp: timeStamp), _result); await _dispatcher(_pointer.up(timeStamp: timeStamp));
assert(!_pointer._isDown); assert(!_pointer._isDown);
_result = null;
}); });
} }
...@@ -431,9 +419,8 @@ class TestGesture { ...@@ -431,9 +419,8 @@ class TestGesture {
Future<void> cancel({ Duration timeStamp = Duration.zero }) { Future<void> cancel({ Duration timeStamp = Duration.zero }) {
return TestAsyncUtils.guard<void>(() async { return TestAsyncUtils.guard<void>(() async {
assert(_pointer._isDown); assert(_pointer._isDown);
await _dispatcher(_pointer.cancel(timeStamp: timeStamp), _result); await _dispatcher(_pointer.cancel(timeStamp: timeStamp));
assert(!_pointer._isDown); assert(!_pointer._isDown);
_result = null;
}); });
} }
} }
......
...@@ -543,7 +543,7 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker ...@@ -543,7 +543,7 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
// Flush all past events // Flush all past events
handleTimeStampDiff.add(-timeDiff); handleTimeStampDiff.add(-timeDiff);
for (final PointerEvent event in record.events) { for (final PointerEvent event in record.events) {
_handlePointerEvent(event, hitTestHistory); binding.handlePointerEvent(event, source: TestBindingEventSource.test);
} }
} else { } else {
// TODO(CareF): reconsider the pumping strategy after // TODO(CareF): reconsider the pumping strategy after
...@@ -554,7 +554,7 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker ...@@ -554,7 +554,7 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
binding.clock.now().difference(startTime) - record.timeDelay, binding.clock.now().difference(startTime) - record.timeDelay,
); );
for (final PointerEvent event in record.events) { for (final PointerEvent event in record.events) {
_handlePointerEvent(event, hitTestHistory); binding.handlePointerEvent(event, source: TestBindingEventSource.test);
} }
} }
} }
...@@ -566,48 +566,6 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker ...@@ -566,48 +566,6 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
}); });
} }
// This is a parallel implementation of [GestureBinding._handlePointerEvent]
// to make compatible with test bindings.
void _handlePointerEvent(
PointerEvent event,
Map<int, HitTestResult> _hitTests
) {
HitTestResult hitTestResult;
if (event is PointerDownEvent || event is PointerSignalEvent) {
assert(!_hitTests.containsKey(event.pointer));
hitTestResult = HitTestResult();
binding.hitTest(hitTestResult, event.position);
if (event is PointerDownEvent) {
_hitTests[event.pointer] = hitTestResult;
}
assert(() {
if (debugPrintHitTestResults)
debugPrint('$event: $hitTestResult');
return true;
}());
} else if (event is PointerUpEvent || event is PointerCancelEvent) {
hitTestResult = _hitTests.remove(event.pointer);
} else if (event.down) {
// Because events that occur with the pointer down (like
// PointerMoveEvents) should be dispatched to the same place that their
// initial PointerDownEvent was, we want to re-use the path we found when
// the pointer went down, rather than do hit detection each time we get
// such an event.
hitTestResult = _hitTests[event.pointer];
}
assert(() {
if (debugPrintMouseHoverEvents && event is PointerHoverEvent)
debugPrint('$event');
return true;
}());
if (hitTestResult != null ||
event is PointerHoverEvent ||
event is PointerAddedEvent ||
event is PointerRemovedEvent) {
binding.dispatchEvent(event, hitTestResult, source: TestBindingEventSource.test);
}
}
/// Triggers a frame after `duration` amount of time. /// Triggers a frame after `duration` amount of time.
/// ///
/// This makes the framework act as if the application had janked (missed /// This makes the framework act as if the application had janked (missed
...@@ -824,9 +782,9 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker ...@@ -824,9 +782,9 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
} }
@override @override
Future<void> sendEventToBinding(PointerEvent event, HitTestResult result) { Future<void> sendEventToBinding(PointerEvent event) {
return TestAsyncUtils.guard<void>(() async { return TestAsyncUtils.guard<void>(() async {
binding.dispatchEvent(event, result, source: TestBindingEventSource.test); binding.handlePointerEvent(event, source: TestBindingEventSource.test);
}); });
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment