diff --git a/packages/flutter/lib/src/material/tooltip.dart b/packages/flutter/lib/src/material/tooltip.dart index a7b2e830e1f..7bbb6c42d68 100644 --- a/packages/flutter/lib/src/material/tooltip.dart +++ b/packages/flutter/lib/src/material/tooltip.dart @@ -284,8 +284,16 @@ class _TooltipState extends State with SingleTickerProviderStateMixin { } void _createNewEntry() { + final OverlayState overlayState = Overlay.of( + context, + debugRequiredFor: widget, + ); + final RenderBox box = context.findRenderObject() as RenderBox; - final Offset target = box.localToGlobal(box.size.center(Offset.zero)); + final Offset target = box.localToGlobal( + box.size.center(Offset.zero), + ancestor: overlayState.context.findRenderObject(), + ); // We create this widget outside of the overlay entry's builder to prevent // updated values from happening to leak into the overlay when the overlay @@ -309,7 +317,7 @@ class _TooltipState extends State with SingleTickerProviderStateMixin { ), ); _entry = OverlayEntry(builder: (BuildContext context) => overlay); - Overlay.of(context, debugRequiredFor: widget).insert(_entry); + overlayState.insert(_entry); SemanticsService.tooltip(widget.message); } diff --git a/packages/flutter/test/material/tooltip_test.dart b/packages/flutter/test/material/tooltip_test.dart index e1a1d360650..90d45bae232 100644 --- a/packages/flutter/test/material/tooltip_test.dart +++ b/packages/flutter/test/material/tooltip_test.dart @@ -99,6 +99,68 @@ void main() { expect(tipInGlobal.dy, 20.0); }); + testWidgets('Does tooltip end up in the right place - center with padding outside overlay', (WidgetTester tester) async { + final GlobalKey key = GlobalKey(); + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: Padding( + padding: const EdgeInsets.all(20), + child: Overlay( + initialEntries: [ + OverlayEntry( + builder: (BuildContext context) { + return Stack( + children: [ + Positioned( + left: 300.0, + top: 0.0, + child: Tooltip( + key: key, + message: tooltipText, + height: 20.0, + padding: const EdgeInsets.all(5.0), + verticalOffset: 20.0, + preferBelow: false, + child: const SizedBox( + width: 0.0, + height: 0.0, + ), + ), + ), + ], + ); + }, + ), + ], + ), + ), + ), + ); + (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file. + await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0) + + /************************ 800x600 screen + * ________________ * }- 20.0 padding outside overlay + * | o | * y=0 + * | | | * }- 20.0 vertical offset, of which 10.0 is in the screen edge margin + * | +----+ | * \- (5.0 padding in height) + * | | | | * |- 20 height + * | +----+ | * /- (5.0 padding in height) + * |________________| * + * * } - 20.0 padding outside overlay + ************************/ + + final RenderBox tip = tester.renderObject( + _findTooltipContainer(tooltipText), + ); + final Offset tipInGlobal = tip.localToGlobal(tip.size.topCenter(Offset.zero)); + // The exact position of the left side depends on the font the test framework + // happens to pick, so we don't test that. + expect(tipInGlobal.dx, 320.0); + expect(tipInGlobal.dy, 40.0); + }); + testWidgets('Does tooltip end up in the right place - top left', (WidgetTester tester) async { final GlobalKey key = GlobalKey(); await tester.pumpWidget(