Add padding for Navigation Bar to account for safe area (#102419)
diff --git a/packages/flutter/lib/src/material/navigation_bar.dart b/packages/flutter/lib/src/material/navigation_bar.dart
index b9622da..22f7bcb 100644
--- a/packages/flutter/lib/src/material/navigation_bar.dart
+++ b/packages/flutter/lib/src/material/navigation_bar.dart
@@ -147,41 +147,35 @@
final NavigationDestinationLabelBehavior effectiveLabelBehavior = labelBehavior
?? navigationBarTheme.labelBehavior
?? defaults.labelBehavior!;
- final double additionalBottomPadding = MediaQuery.of(context).padding.bottom;
return Material(
color: backgroundColor
?? navigationBarTheme.backgroundColor
?? defaults.backgroundColor!,
elevation: elevation ?? navigationBarTheme.elevation ?? defaults.elevation!,
- child: Padding(
- padding: EdgeInsets.only(bottom: additionalBottomPadding),
- child: MediaQuery.removePadding(
- context: context,
- removeBottom: true,
- child: SizedBox(
- height: effectiveHeight,
- child: Row(
- children: <Widget>[
- for (int i = 0; i < destinations.length; i++)
- Expanded(
- child: _SelectableAnimatedBuilder(
- duration: animationDuration ?? const Duration(milliseconds: 500),
- isSelected: i == selectedIndex,
- builder: (BuildContext context, Animation<double> animation) {
- return _NavigationDestinationInfo(
- index: i,
- totalNumberOfDestinations: destinations.length,
- selectedAnimation: animation,
- labelBehavior: effectiveLabelBehavior,
- onTap: _handleTap(i),
- child: destinations[i],
- );
- },
- ),
+ child: SafeArea(
+ child: SizedBox(
+ height: effectiveHeight,
+ child: Row(
+ children: <Widget>[
+ for (int i = 0; i < destinations.length; i++)
+ Expanded(
+ child: _SelectableAnimatedBuilder(
+ duration: animationDuration ?? const Duration(milliseconds: 500),
+ isSelected: i == selectedIndex,
+ builder: (BuildContext context, Animation<double> animation) {
+ return _NavigationDestinationInfo(
+ index: i,
+ totalNumberOfDestinations: destinations.length,
+ selectedAnimation: animation,
+ labelBehavior: effectiveLabelBehavior,
+ onTap: _handleTap(i),
+ child: destinations[i],
+ );
+ },
),
- ],
- ),
+ ),
+ ],
),
),
),
diff --git a/packages/flutter/test/material/navigation_bar_test.dart b/packages/flutter/test/material/navigation_bar_test.dart
index 14276a2..43046c2 100644
--- a/packages/flutter/test/material/navigation_bar_test.dart
+++ b/packages/flutter/test/material/navigation_bar_test.dart
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
@@ -137,6 +138,104 @@
expect(tester.getSize(find.byType(NavigationBar)).height, expectedHeight);
});
+ testWidgets('NavigationBar respects the notch/system navigation bar in landscape mode', (WidgetTester tester) async {
+ const double safeAreaPadding = 40.0;
+ Widget navigationBar() {
+ return NavigationBar(
+ destinations: const <Widget>[
+ NavigationDestination(
+ icon: Icon(Icons.ac_unit),
+ label: 'AC',
+ ),
+ NavigationDestination(
+ key: Key('Center'),
+ icon: Icon(Icons.center_focus_strong),
+ label: 'Center',
+ ),
+ NavigationDestination(
+ icon: Icon(Icons.access_alarm),
+ label: 'Alarm',
+ ),
+ ],
+ onDestinationSelected: (int i) {},
+ );
+ }
+
+ await tester.pumpWidget(_buildWidget(navigationBar()));
+ final double defaultWidth = tester.getSize(find.byType(NavigationBar)).width;
+ final Finder defaultCenterItem = find.byKey(const Key('Center'));
+ final Offset center = tester.getCenter(defaultCenterItem);
+ expect(center.dx, defaultWidth / 2);
+
+ await tester.pumpWidget(
+ _buildWidget(
+ MediaQuery(
+ data: const MediaQueryData(
+ padding: EdgeInsets.only(left: safeAreaPadding),
+ ),
+ child: navigationBar(),
+ ),
+ ),
+ );
+
+ // The position of center item of navigation bar should indicate whether
+ // the safe area is sufficiently respected, when safe area is on the left side.
+ // e.g. Android device with system navigation bar in landscape mode.
+ final Finder leftPaddedCenterItem = find.byKey(const Key('Center'));
+ final Offset leftPaddedCenter = tester.getCenter(leftPaddedCenterItem);
+ expect(
+ leftPaddedCenter.dx,
+ closeTo((defaultWidth + safeAreaPadding) / 2.0, precisionErrorTolerance),
+ );
+
+ await tester.pumpWidget(
+ _buildWidget(
+ MediaQuery(
+ data: const MediaQueryData(
+ padding: EdgeInsets.only(right: safeAreaPadding)
+ ),
+ child: navigationBar(),
+ ),
+ ),
+ );
+
+ // The position of center item of navigation bar should indicate whether
+ // the safe area is sufficiently respected, when safe area is on the right side.
+ // e.g. Android device with system navigation bar in landscape mode.
+ final Finder rightPaddedCenterItem = find.byKey(const Key('Center'));
+ final Offset rightPaddedCenter = tester.getCenter(rightPaddedCenterItem);
+ expect(
+ rightPaddedCenter.dx,
+ closeTo((defaultWidth - safeAreaPadding) / 2, precisionErrorTolerance),
+ );
+
+ await tester.pumpWidget(
+ _buildWidget(
+ MediaQuery(
+ data: const MediaQueryData(
+ padding: EdgeInsets.fromLTRB(
+ safeAreaPadding,
+ 0,
+ safeAreaPadding,
+ safeAreaPadding
+ ),
+ ),
+ child: navigationBar(),
+ ),
+ ),
+ );
+
+ // The position of center item of navigation bar should indicate whether
+ // the safe area is sufficiently respected, when safe areas are on both sides.
+ // e.g. iOS device with both sides of round corner.
+ final Finder paddedCenterItem = find.byKey(const Key('Center'));
+ final Offset paddedCenter = tester.getCenter(paddedCenterItem);
+ expect(
+ paddedCenter.dx,
+ closeTo(defaultWidth / 2, precisionErrorTolerance),
+ );
+ });
+
testWidgets('NavigationBar uses proper defaults when no parameters are given', (WidgetTester tester) async {
// Pre-M3 settings that were hand coded.
await tester.pumpWidget(