Skip to content

Commit 2e419ff

Browse files
authored
Wrap PopupMenu with SafeArea to respect status bar (flutter#64678)
1 parent 553577a commit 2e419ff

File tree

2 files changed

+114
-6
lines changed

2 files changed

+114
-6
lines changed

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

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -756,12 +756,7 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
756756
menu = Theme(data: theme!, child: menu);
757757
}
758758

759-
return MediaQuery.removePadding(
760-
context: context,
761-
removeTop: true,
762-
removeBottom: true,
763-
removeLeft: true,
764-
removeRight: true,
759+
return SafeArea(
765760
child: Builder(
766761
builder: (BuildContext context) {
767762
return CustomSingleChildLayout(

packages/flutter/test/material/popup_menu_test.dart

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1583,6 +1583,119 @@ void main() {
15831583

15841584
expect(RendererBinding.instance!.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.basic);
15851585
});
1586+
1587+
testWidgets('PopupMenu in AppBar does not overlap with the status bar', (WidgetTester tester) async {
1588+
const List<PopupMenuItem<int>> choices = <PopupMenuItem<int>>[
1589+
PopupMenuItem<int>(value: 1, child: Text('Item 1')),
1590+
PopupMenuItem<int>(value: 2, child: Text('Item 2')),
1591+
PopupMenuItem<int>(value: 3, child: Text('Item 3')),
1592+
];
1593+
1594+
const double statusBarHeight = 24.0;
1595+
final PopupMenuItem<int> firstItem = choices[0];
1596+
int _selectedValue = choices[0].value;
1597+
1598+
await tester.pumpWidget(
1599+
MaterialApp(
1600+
builder: (BuildContext context, Widget child) {
1601+
return MediaQuery(
1602+
data: const MediaQueryData(padding: EdgeInsets.only(top: statusBarHeight)), // status bar
1603+
child: child,
1604+
);
1605+
},
1606+
home: StatefulBuilder(
1607+
builder: (BuildContext context, StateSetter setState) {
1608+
return Scaffold(
1609+
appBar: AppBar(
1610+
title: const Text('PopupMenu Test'),
1611+
actions: <Widget>[
1612+
PopupMenuButton<int>(
1613+
onSelected: (int result) {
1614+
setState(() {
1615+
_selectedValue = result;
1616+
});
1617+
},
1618+
initialValue: _selectedValue,
1619+
itemBuilder: (BuildContext context) {
1620+
return choices;
1621+
},
1622+
),
1623+
],
1624+
),
1625+
);
1626+
},
1627+
),
1628+
),
1629+
);
1630+
1631+
await tester.tap(find.byIcon(Icons.more_vert));
1632+
await tester.pumpAndSettle();
1633+
1634+
// Tap third item.
1635+
await tester.tap(find.text('Item 3'));
1636+
await tester.pumpAndSettle();
1637+
1638+
// Open popupMenu again.
1639+
await tester.tap(find.byIcon(Icons.more_vert));
1640+
await tester.pumpAndSettle();
1641+
1642+
// Check whether the first item is not overlapping with status bar.
1643+
expect(tester.getTopLeft(find.byWidget(firstItem)).dy, greaterThan(statusBarHeight));
1644+
});
1645+
1646+
testWidgets('Vertically long PopupMenu does not overlap with the status bar and bottom notch', (WidgetTester tester) async {
1647+
const double windowPaddingTop = 44;
1648+
const double windowPaddingBottom = 34;
1649+
final GlobalKey _firstKey = GlobalKey();
1650+
final GlobalKey _lastKey = GlobalKey();
1651+
1652+
await tester.pumpWidget(
1653+
MaterialApp(
1654+
builder: (BuildContext context, Widget child) {
1655+
return MediaQuery(
1656+
data: const MediaQueryData(
1657+
padding: EdgeInsets.only(
1658+
top: windowPaddingTop,
1659+
bottom: windowPaddingBottom,
1660+
),
1661+
),
1662+
child: child,
1663+
);
1664+
},
1665+
home: Scaffold(
1666+
appBar: AppBar(
1667+
title: const Text('PopupMenu Test'),
1668+
),
1669+
body: PopupMenuButton<int>(
1670+
child: const Text('Show Menu'),
1671+
itemBuilder: (BuildContext context) => Iterable<PopupMenuItem<int>>.generate(
1672+
20, (int i) => PopupMenuItem<int>(
1673+
// Set globalKey to the first and last item.
1674+
key: i == 0 ? _firstKey : i == 19 ? _lastKey : null,
1675+
value: i,
1676+
child: Text('Item $i'),
1677+
),
1678+
).toList(),
1679+
),
1680+
),
1681+
),
1682+
);
1683+
1684+
await tester.tap(find.text('Show Menu'));
1685+
await tester.pumpAndSettle();
1686+
1687+
// Check whether the first item is not overlapping with status bar.
1688+
expect(tester.getTopLeft(find.byKey(_firstKey)).dy, greaterThan(windowPaddingTop));
1689+
1690+
await tester.ensureVisible(find.byKey(_lastKey, skipOffstage: false));
1691+
await tester.pumpAndSettle();
1692+
1693+
// Check whether the last item is not overlapping with bottom notch.
1694+
expect(
1695+
tester.getBottomLeft(find.byKey(_lastKey)).dy,
1696+
lessThan(600 - windowPaddingBottom), // Device height is 600.
1697+
);
1698+
});
15861699
}
15871700

15881701
class TestApp extends StatefulWidget {

0 commit comments

Comments
 (0)