Fix unresponsive mouse tooltip (#142282)

Fixes https://github.com/flutter/flutter/issues/142045

The intent of using `??=` was that if the tooltip is already scheduled for showing, rescheduling another show does nothing. But if the tooltip is already scheduled for dismissing, the `??=` won't cancel the dismiss timer and as a result the tooltip won't show. So the `??=` is now replaced by `=` to keep it consistent with the `_scheduleDismissTooltip` implementation.
This commit is contained in:
LongCatIsLooong 2024-01-31 09:17:53 -08:00 committed by GitHub
parent 347403f2ea
commit 43aee92e61
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 85 additions and 1 deletions

View file

@ -508,7 +508,8 @@ class TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
);
switch (_controller.status) {
case AnimationStatus.dismissed when withDelay.inMicroseconds > 0:
_timer ??= Timer(withDelay, show);
_timer?.cancel();
_timer = Timer(withDelay, show);
// If the tooltip is already fading in or fully visible, skip the
// animation and show the tooltip immediately.
case AnimationStatus.dismissed:

View file

@ -1562,6 +1562,89 @@ void main() {
expect(find.text('last tooltip'), findsOneWidget);
});
// Regression test for https://github.com/flutter/flutter/issues/142045.
testWidgets('Tooltip shows/hides when the mouse hovers, and then exits and re-enters in quick succession', (WidgetTester tester) async {
const Duration waitDuration = Durations.extralong1;
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
addTearDown(() async {
return gesture.removePointer();
});
await gesture.addPointer();
await gesture.moveTo(const Offset(1.0, 1.0));
await tester.pumpWidget(
const MaterialApp(
home: Center(
child: Tooltip(
message: tooltipText,
waitDuration: waitDuration,
exitDuration: waitDuration,
child: SizedBox(
width: 100.0,
height: 100.0,
),
),
),
),
);
Future<void> mouseEnterAndWaitUntilVisible() async {
await gesture.moveTo(tester.getCenter(find.byType(Tooltip)));
await tester.pump();
await tester.pump(waitDuration);
await tester.pumpAndSettle();
expect(find.text(tooltipText), findsOne);
}
Future<void> mouseExit() async {
await gesture.moveTo(Offset.zero);
await tester.pump();
}
Future<void> performSequence(Iterable<Future<void> Function()> actions) async {
for (final Future<void> Function() action in actions) {
await action();
}
}
await performSequence(<Future<void> Function()>[mouseEnterAndWaitUntilVisible]);
expect(find.text(tooltipText), findsOne);
// Wait for reset.
await mouseExit();
await tester.pump(const Duration(hours: 1));
await tester.pumpAndSettle();
expect(find.text(tooltipText), findsNothing);
await performSequence(<Future<void> Function()>[
mouseEnterAndWaitUntilVisible,
mouseExit,
mouseEnterAndWaitUntilVisible,
]);
expect(find.text(tooltipText), findsOne);
// Wait for reset.
await mouseExit();
await tester.pump(const Duration(hours: 1));
await tester.pumpAndSettle();
expect(find.text(tooltipText), findsNothing);
await performSequence(<Future<void> Function()>[
mouseEnterAndWaitUntilVisible,
mouseExit,
mouseEnterAndWaitUntilVisible,
mouseExit,
mouseEnterAndWaitUntilVisible,
]);
expect(find.text(tooltipText), findsOne);
// Wait for reset.
await mouseExit();
await tester.pump(const Duration(hours: 1));
await tester.pumpAndSettle();
expect(find.text(tooltipText), findsNothing);
});
testWidgets('Tooltip text is also hoverable', (WidgetTester tester) async {
const Duration waitDuration = Duration.zero;
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);