Skip to content

Commit ff136cb

Browse files
authored
Fix Tooltip obscured by keyboard (#103339)
1 parent 0ff0aff commit ff136cb

File tree

2 files changed

+56
-0
lines changed

2 files changed

+56
-0
lines changed

packages/flutter/lib/src/material/tooltip.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,7 @@ class _TooltipOverlay extends StatelessWidget {
841841
);
842842
}
843843
return Positioned.fill(
844+
bottom: MediaQuery.maybeOf(context)?.viewInsets.bottom ?? 0.0,
844845
child: CustomSingleChildLayout(
845846
delegate: _TooltipPositionDelegate(
846847
target: target,

packages/flutter/test/material/tooltip_test.dart

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,61 @@ void main() {
489489
expect(tip.localToGlobal(tip.size.bottomRight(Offset.zero)).dy, equals(324.0));
490490
});
491491

492+
testWidgets('Tooltip should be fully visible when MediaQuery.viewInsets > 0', (WidgetTester tester) async {
493+
// Regression test for https://github.com/flutter/flutter/issues/23666
494+
Widget materialAppWithViewInsets(double viewInsetsHeight) {
495+
final Widget scaffold = Scaffold(
496+
body: const TextField(),
497+
floatingActionButton: FloatingActionButton(
498+
tooltip: tooltipText,
499+
onPressed: () { /* do nothing */ },
500+
child: const Icon(Icons.add),
501+
),
502+
);
503+
return MediaQuery.fromWindow(
504+
child: MediaQuery(
505+
data: MediaQueryData(
506+
viewInsets: EdgeInsets.only(bottom: viewInsetsHeight),
507+
),
508+
child: MaterialApp(
509+
useInheritedMediaQuery: true,
510+
home: scaffold,
511+
),
512+
),
513+
);
514+
}
515+
516+
// Start with MediaQuery.viewInsets.bottom = 0
517+
await tester.pumpWidget(materialAppWithViewInsets(0));
518+
519+
// Show FAB tooltip
520+
final Finder fabFinder = find.byType(FloatingActionButton);
521+
await tester.longPress(fabFinder);
522+
await tester.pump(const Duration(milliseconds: 500));
523+
expect(find.byType(Tooltip), findsOneWidget);
524+
525+
// FAB tooltip should be above FAB
526+
RenderBox tip = tester.renderObject(_findTooltipContainer(tooltipText));
527+
Offset fabTopRight = tester.getTopRight(fabFinder);
528+
Offset tooltipTopRight = tip.localToGlobal(tip.size.topRight(Offset.zero));
529+
expect(tooltipTopRight.dy < fabTopRight.dy, true);
530+
531+
// Simulate Keyboard opening (MediaQuery.viewInsets.bottom = 300))
532+
await tester.pumpWidget(materialAppWithViewInsets(300));
533+
await tester.pumpAndSettle();
534+
535+
// Show FAB tooltip
536+
await tester.longPress(fabFinder);
537+
await tester.pump(const Duration(milliseconds: 500));
538+
expect(find.byType(Tooltip), findsOneWidget);
539+
540+
// FAB tooltip should still be above FAB
541+
tip = tester.renderObject(_findTooltipContainer(tooltipText));
542+
fabTopRight = tester.getTopRight(fabFinder);
543+
tooltipTopRight = tip.localToGlobal(tip.size.topRight(Offset.zero));
544+
expect(tooltipTopRight.dy < fabTopRight.dy, true);
545+
});
546+
492547
testWidgets('Custom tooltip margin', (WidgetTester tester) async {
493548
const double customMarginValue = 10.0;
494549
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();

0 commit comments

Comments
 (0)