From e1eb1b9ecf316e3be6677f8d31d9d9b352155d9d Mon Sep 17 00:00:00 2001 From: Chinmoy Date: Thu, 16 Mar 2023 22:32:57 +0530 Subject: [PATCH] Added onSecondaryTap in InkWell. (#119058) Exposes onSecondaryTap in InkWell. --- .../flutter/lib/src/material/ink_well.dart | 81 ++++++++++++++++++- .../flutter/test/material/ink_well_test.dart | 37 +++++++++ 2 files changed, 116 insertions(+), 2 deletions(-) diff --git a/packages/flutter/lib/src/material/ink_well.dart b/packages/flutter/lib/src/material/ink_well.dart index 5b65ed88f772..1effad65179d 100644 --- a/packages/flutter/lib/src/material/ink_well.dart +++ b/packages/flutter/lib/src/material/ink_well.dart @@ -294,6 +294,10 @@ class InkResponse extends StatelessWidget { this.onTapCancel, this.onDoubleTap, this.onLongPress, + this.onSecondaryTap, + this.onSecondaryTapUp, + this.onSecondaryTapDown, + this.onSecondaryTapCancel, this.onHighlightChanged, this.onHover, this.mouseCursor, @@ -342,6 +346,21 @@ class InkResponse extends StatelessWidget { /// Called when the user long-presses on this part of the material. final GestureLongPressCallback? onLongPress; + /// Called when the user taps this part of the material with a secondary button. + final GestureTapCallback? onSecondaryTap; + + /// Called when the user taps down on this part of the material with a + /// secondary button. + final GestureTapDownCallback? onSecondaryTapDown; + + /// Called when the user releases a secondary button tap that was started on + /// this part of the material. [onSecondaryTap] is called immediately after. + final GestureTapUpCallback? onSecondaryTapUp; + + /// Called when the user cancels a secondary button tap that was started on + /// this part of the material. + final GestureTapCallback? onSecondaryTapCancel; + /// Called when this part of the material either becomes highlighted or stops /// being highlighted. /// @@ -599,6 +618,10 @@ class InkResponse extends StatelessWidget { onTapCancel: onTapCancel, onDoubleTap: onDoubleTap, onLongPress: onLongPress, + onSecondaryTap: onSecondaryTap, + onSecondaryTapUp: onSecondaryTapUp, + onSecondaryTapDown: onSecondaryTapDown, + onSecondaryTapCancel: onSecondaryTapCancel, onHighlightChanged: onHighlightChanged, onHover: onHover, mouseCursor: mouseCursor, @@ -651,6 +674,10 @@ class _InkResponseStateWidget extends StatefulWidget { this.onTapCancel, this.onDoubleTap, this.onLongPress, + this.onSecondaryTap, + this.onSecondaryTapUp, + this.onSecondaryTapDown, + this.onSecondaryTapCancel, this.onHighlightChanged, this.onHover, this.mouseCursor, @@ -684,6 +711,10 @@ class _InkResponseStateWidget extends StatefulWidget { final GestureTapCallback? onTapCancel; final GestureTapCallback? onDoubleTap; final GestureLongPressCallback? onLongPress; + final GestureTapCallback? onSecondaryTap; + final GestureTapUpCallback? onSecondaryTapUp; + final GestureTapDownCallback? onSecondaryTapDown; + final GestureTapCallback? onSecondaryTapCancel; final ValueChanged? onHighlightChanged; final ValueChanged? onHover; final MouseCursor? mouseCursor; @@ -722,6 +753,10 @@ class _InkResponseStateWidget extends StatefulWidget { if (onTapDown != null) 'tap down', if (onTapUp != null) 'tap up', if (onTapCancel != null) 'tap cancel', + if (onSecondaryTap != null) 'secondary tap', + if (onSecondaryTapUp != null) 'secondary tap up', + if (onSecondaryTapDown != null) 'secondary tap down', + if (onSecondaryTapCancel != null) 'secondary tap cancel' ]; properties.add(IterableProperty('gestures', gestures, ifEmpty: '')); properties.add(DiagnosticsProperty('mouseCursor', mouseCursor)); @@ -1040,11 +1075,15 @@ class _InkResponseState extends State<_InkResponseStateWidget> widget.onFocusChange?.call(hasFocus); } - void handleTapDown(TapDownDetails details) { + void handleAnyTapDown(TapDownDetails details) { if (_anyChildInkResponsePressed) { return; } _startNewSplash(details: details); + } + + void handleTapDown(TapDownDetails details) { + handleAnyTapDown(details); widget.onTapDown?.call(details); } @@ -1052,6 +1091,15 @@ class _InkResponseState extends State<_InkResponseStateWidget> widget.onTapUp?.call(details); } + void handleSecondaryTapDown(TapDownDetails details) { + handleAnyTapDown(details); + widget.onSecondaryTapDown?.call(details); + } + + void handleSecondaryTapUp(TapUpDetails details) { + widget.onSecondaryTapUp?.call(details); + } + void _startNewSplash({TapDownDetails? details, BuildContext? context}) { assert(details != null || context != null); @@ -1110,6 +1158,20 @@ class _InkResponseState extends State<_InkResponseStateWidget> } } + void handleSecondaryTap() { + _currentSplash?.confirm(); + _currentSplash = null; + updateHighlight(_HighlightType.pressed, value: false); + widget.onSecondaryTap?.call(); + } + + void handleSecondaryTapCancel() { + _currentSplash?.cancel(); + _currentSplash = null; + widget.onSecondaryTapCancel?.call(); + updateHighlight(_HighlightType.pressed, value: false); + } + @override void deactivate() { if (_splashes != null) { @@ -1130,7 +1192,14 @@ class _InkResponseState extends State<_InkResponseStateWidget> } bool isWidgetEnabled(_InkResponseStateWidget widget) { - return widget.onTap != null || widget.onDoubleTap != null || widget.onLongPress != null || widget.onTapDown != null; + return widget.onTap != null + || widget.onDoubleTap != null + || widget.onLongPress != null + || widget.onTapUp != null + || widget.onTapDown != null + || widget.onSecondaryTap != null + || widget.onSecondaryTapUp != null + || widget.onSecondaryTapDown != null; } bool get enabled => isWidgetEnabled(widget); @@ -1220,6 +1289,10 @@ class _InkResponseState extends State<_InkResponseStateWidget> onTapCancel: enabled ? handleTapCancel : null, onDoubleTap: widget.onDoubleTap != null ? handleDoubleTap : null, onLongPress: widget.onLongPress != null ? handleLongPress : null, + onSecondaryTapDown: enabled ? handleSecondaryTapDown : null, + onSecondaryTapUp: enabled ? handleSecondaryTapUp: null, + onSecondaryTap: enabled ? handleSecondaryTap : null, + onSecondaryTapCancel: enabled ? handleSecondaryTapCancel : null, behavior: HitTestBehavior.opaque, excludeFromSemantics: true, child: widget.child, @@ -1326,6 +1399,10 @@ class InkWell extends InkResponse { super.onTapDown, super.onTapUp, super.onTapCancel, + super.onSecondaryTap, + super.onSecondaryTapUp, + super.onSecondaryTapDown, + super.onSecondaryTapCancel, super.onHighlightChanged, super.onHover, super.mouseCursor, diff --git a/packages/flutter/test/material/ink_well_test.dart b/packages/flutter/test/material/ink_well_test.dart index ffe018b85180..e309185eec98 100644 --- a/packages/flutter/test/material/ink_well_test.dart +++ b/packages/flutter/test/material/ink_well_test.dart @@ -2015,4 +2015,41 @@ testWidgets('InkResponse radius can be updated', (WidgetTester tester) async { await tester.pump(const Duration(milliseconds: 25)); expect(inkFeatures, paints..rect(rect: const Rect.fromLTRB(350.0, 250.0, 450.0, 350.0), color: const Color(0x8000ff00))); }); + + testWidgets('InkWell secondary tap test', (WidgetTester tester) async { + final List log = []; + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: Material( + child: Center( + child: InkWell( + onSecondaryTap: () { + log.add('secondary-tap'); + }, + onSecondaryTapDown: (TapDownDetails details) { + log.add('secondary-tap-down'); + }, + onSecondaryTapUp: (TapUpDetails details) { + log.add('secondary-tap-up'); + }, + onSecondaryTapCancel: () { + log.add('secondary-tap-cancel'); + }, + ), + ), + ), + )); + + await tester.tap(find.byType(InkWell), pointer: 1, buttons: kSecondaryButton); + + expect(log, equals(['secondary-tap-down', 'secondary-tap-up', 'secondary-tap'])); + log.clear(); + + final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byType(InkWell)), pointer: 2, buttons: kSecondaryButton); + await gesture.moveTo(const Offset(100, 100)); + await gesture.up(); + + expect(log, equals(['secondary-tap-down', 'secondary-tap-cancel'])); + }); }