Fix tooltip crash when route has secondary animation (#172678)
Makes the tooltip interactive if the route is the current route and the
route is not animating out.
Added `_route` to cache `ModalRoute.of(context)`.
Consolidates [Fix the issue with
Tooltip](https://github.com/flutter/flutter/pull/168546) and [Delay
showing tooltip during page
transition](https://github.com/flutter/flutter/pull/167614)
Fix [[Desktop] [Web] [Regression] [3.32] AppBar back - RenderBox was not
laid out -
TooltipState._buildTooltipOverlay](https://github.com/flutter/flutter/issues/169741)
diff --git a/packages/flutter/lib/src/material/tooltip.dart b/packages/flutter/lib/src/material/tooltip.dart
index dcfef71..86e2e77 100644
--- a/packages/flutter/lib/src/material/tooltip.dart
+++ b/packages/flutter/lib/src/material/tooltip.dart
@@ -606,10 +606,6 @@
}
void _handlePointerDown(PointerDownEvent event) {
- assert(mounted);
- if (!(ModalRoute.isCurrentOf(context) ?? true)) {
- return;
- }
// PointerDeviceKinds that don't support hovering.
const Set<PointerDeviceKind> triggerModeDeviceKinds = <PointerDeviceKind>{
PointerDeviceKind.invertedStylus,
@@ -722,10 +718,6 @@
// (even these tooltips are still hovered),
// iii. The last hovering device leaves the tooltip.
void _handleMouseEnter(PointerEnterEvent event) {
- assert(mounted);
- if (!(ModalRoute.isCurrentOf(context) ?? true)) {
- return;
- }
// _handleMouseEnter is only called when the mouse starts to hover over this
// tooltip (including the actual tooltip it shows on the overlay), and this
// tooltip is the first to be hit in the widget tree's hit testing order.
@@ -747,10 +739,6 @@
}
void _handleMouseExit(PointerExitEvent event) {
- assert(mounted);
- if (!(ModalRoute.isCurrentOf(context) ?? true)) {
- return;
- }
if (_activeHoveringPointerDevices.isEmpty) {
return;
}
@@ -826,12 +814,14 @@
};
}
- Widget _buildTooltipOverlay(BuildContext context) {
- final OverlayState overlayState = Overlay.of(context, debugRequiredFor: widget);
- final RenderBox box = this.context.findRenderObject()! as RenderBox;
- final Offset target = box.localToGlobal(
- box.size.center(Offset.zero),
- ancestor: overlayState.context.findRenderObject(),
+ Widget _buildTooltipOverlay(BuildContext context, OverlayChildLayoutInfo layoutInfo) {
+ if (layoutInfo.childPaintTransform.determinant() == 0.0) {
+ // The child is not visible.
+ return const SizedBox.shrink();
+ }
+ final Offset target = MatrixUtils.transformPoint(
+ layoutInfo.childPaintTransform,
+ layoutInfo.childSize.center(Offset.zero),
);
final (TextStyle defaultTextStyle, BoxDecoration defaultDecoration) = switch (Theme.of(
@@ -947,7 +937,7 @@
),
);
}
- return OverlayPortal(
+ return OverlayPortal.overlayChildLayoutBuilder(
controller: _overlayController,
overlayChildBuilder: _buildTooltipOverlay,
child: result,
diff --git a/packages/flutter/test/material/tooltip_test.dart b/packages/flutter/test/material/tooltip_test.dart
index 5112c7c..65f80d7 100644
--- a/packages/flutter/test/material/tooltip_test.dart
+++ b/packages/flutter/test/material/tooltip_test.dart
@@ -3511,6 +3511,69 @@
expect(tester.takeException(), isNull);
});
+ // This is a regression test for https://github.com/flutter/flutter/issues/169741.
+ testWidgets(
+ 'Tooltip does not show while transitioning from another route with secondary animation',
+ (WidgetTester tester) async {
+ final TransitionDurationObserver observer = TransitionDurationObserver();
+
+ await tester.pumpWidget(
+ MaterialApp(
+ navigatorObservers: <NavigatorObserver>[observer],
+ home: Scaffold(
+ body: Builder(
+ builder: (BuildContext context) {
+ return TextButton(
+ onPressed: () => Navigator.push(
+ context,
+ CupertinoPageRoute<void>(
+ builder: (BuildContext context) => Scaffold(
+ appBar: AppBar(
+ leading: const Tooltip(message: 'Hello', child: Text('World')),
+ ),
+ body: TextButton(
+ onPressed: () {
+ Navigator.of(context).push(
+ MaterialPageRoute<void>(
+ builder: (BuildContext context) {
+ return Scaffold(appBar: AppBar(title: const Text('Third Page')));
+ },
+ ),
+ );
+ },
+ child: const Text('Go to Third Page'),
+ ),
+ ),
+ ),
+ ),
+ child: const Text('Go to Second Page'),
+ );
+ },
+ ),
+ ),
+ ),
+ );
+
+ expect(find.text('Go to Second Page'), findsOneWidget);
+ await tester.tap(find.text('Go to Second Page'));
+ await tester.pumpAndSettle();
+ expect(find.text('Go to Third Page'), findsOneWidget);
+
+ await tester.tap(find.text('Go to Third Page'));
+ await tester.pumpAndSettle();
+ expect(find.text('Third Page'), findsOneWidget);
+
+ final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
+ await gesture.addPointer();
+ await tester.tap(find.byType(BackButton));
+ await observer.pumpPastTransition(tester);
+ await gesture.moveTo(tester.getCenter(find.text('World')));
+ await tester.pumpAndSettle();
+
+ expect(tester.takeException(), isNull);
+ },
+ );
+
/// This is a regression test for https://github.com/flutter/flutter/issues/168545
testWidgets('The Tooltip on the ModalBottomSheet can still be displayed after showMenu.', (
WidgetTester tester,