Fix InkWell highlight and splash sometimes persists (#100880)
diff --git a/packages/flutter/lib/src/material/ink_well.dart b/packages/flutter/lib/src/material/ink_well.dart
index e90fb23..6192ec5 100644
--- a/packages/flutter/lib/src/material/ink_well.dart
+++ b/packages/flutter/lib/src/material/ink_well.dart
@@ -769,12 +769,12 @@
bool get _anyChildInkResponsePressed => _activeChildren.isNotEmpty;
void _simulateTap([Intent? intent]) {
- _startSplash(context: context);
+ _startNewSplash(context: context);
_handleTap();
}
void _simulateLongPress() {
- _startSplash(context: context);
+ _startNewSplash(context: context);
_handleLongPress();
}
@@ -966,7 +966,7 @@
void _handleTapDown(TapDownDetails details) {
if (_anyChildInkResponsePressed)
return;
- _startSplash(details: details);
+ _startNewSplash(details: details);
widget.onTapDown?.call(details);
}
@@ -974,7 +974,7 @@
widget.onTapUp?.call(details);
}
- void _startSplash({TapDownDetails? details, BuildContext? context}) {
+ void _startNewSplash({TapDownDetails? details, BuildContext? context}) {
assert(details != null || context != null);
final Offset globalPosition;
@@ -988,6 +988,7 @@
final InteractiveInkFeature splash = _createInkFeature(globalPosition);
_splashes ??= HashSet<InteractiveInkFeature>();
_splashes!.add(splash);
+ _currentSplash?.cancel();
_currentSplash = splash;
updateKeepAlive();
updateHighlight(_HighlightType.pressed, value: true);
@@ -1014,6 +1015,7 @@
void _handleDoubleTap() {
_currentSplash?.confirm();
_currentSplash = null;
+ updateHighlight(_HighlightType.pressed, value: false);
widget.onDoubleTap?.call();
}
diff --git a/packages/flutter/test/material/ink_well_test.dart b/packages/flutter/test/material/ink_well_test.dart
index 0d2146f..7957916 100644
--- a/packages/flutter/test/material/ink_well_test.dart
+++ b/packages/flutter/test/material/ink_well_test.dart
@@ -1424,4 +1424,90 @@
textDirection: TextDirection.ltr,
));
});
+
+ testWidgets('InkWell highlight should not survive after [onTapDown, onDoubleTap] sequence', (WidgetTester tester) async {
+ final List<String> log = <String>[];
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: Material(
+ child: Center(
+ child: InkWell(
+ onTap: () {
+ log.add('tap');
+ },
+ onDoubleTap: () {
+ log.add('double-tap');
+ },
+ onTapDown: (TapDownDetails details) {
+ log.add('tap-down');
+ },
+ onTapCancel: () {
+ log.add('tap-cancel');
+ },
+ ),
+ ),
+ ),
+ ));
+
+ final Offset taplocation = tester.getRect(find.byType(InkWell)).center;
+
+ final TestGesture gesture = await tester.startGesture(taplocation);
+ await tester.pump(const Duration(milliseconds: 100));
+ expect(log, equals(<String>['tap-down']));
+ await gesture.up();
+ await tester.pump(const Duration(milliseconds: 100));
+ await tester.tap(find.byType(InkWell));
+ await tester.pump(const Duration(milliseconds: 100));
+ expect(log, equals(<String>['tap-down', 'double-tap']));
+
+ await tester.pumpAndSettle();
+ final RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
+ expect(inkFeatures, paintsExactlyCountTimes(#drawRect, 0));
+ });
+
+ testWidgets('InkWell splash should not survive after [onTapDown, onTapDown, onTapCancel, onDoubleTap] sequence', (WidgetTester tester) async {
+ final List<String> log = <String>[];
+
+ await tester.pumpWidget(Directionality(
+ textDirection: TextDirection.ltr,
+ child: Material(
+ child: Center(
+ child: InkWell(
+ onTap: () {
+ log.add('tap');
+ },
+ onDoubleTap: () {
+ log.add('double-tap');
+ },
+ onTapDown: (TapDownDetails details) {
+ log.add('tap-down');
+ },
+ onTapCancel: () {
+ log.add('tap-cancel');
+ },
+ ),
+ ),
+ ),
+ ));
+
+ final Offset tapLocation = tester.getRect(find.byType(InkWell)).center;
+
+ final TestGesture gesture1 = await tester.startGesture(tapLocation);
+ await tester.pump(const Duration(milliseconds: 100));
+ expect(log, equals(<String>['tap-down']));
+ await gesture1.up();
+ await tester.pump(const Duration(milliseconds: 100));
+
+ final TestGesture gesture2 = await tester.startGesture(tapLocation);
+ await tester.pump(const Duration(milliseconds: 100));
+ expect(log, equals(<String>['tap-down', 'tap-down']));
+ await gesture2.up();
+ await tester.pump(const Duration(milliseconds: 100));
+ expect(log, equals(<String>['tap-down', 'tap-down', 'tap-cancel', 'double-tap']));
+
+ await tester.pumpAndSettle();
+ final RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
+ expect(inkFeatures, paintsExactlyCountTimes(#drawCircle, 0));
+ });
}