Add flag to use root navigator for showModalBottomSheet (#35878)
* Add flag to use root navigator for showModalBottomSheet
* Add documentation
* Add tests
* Address feedback and fix analyzer issues
* Address feedback
* Update comments
diff --git a/packages/flutter/lib/src/material/bottom_sheet.dart b/packages/flutter/lib/src/material/bottom_sheet.dart
index 8f9ca2a..7ede1d9 100644
--- a/packages/flutter/lib/src/material/bottom_sheet.dart
+++ b/packages/flutter/lib/src/material/bottom_sheet.dart
@@ -400,6 +400,11 @@
/// a [GridView] and have the bottom sheet be draggable, you should set this
/// parameter to true.
///
+/// The `useRootNavigator` parameter ensures that the root navigator is used to
+/// display the [BottomSheet] when set to `true`. This is useful in the case
+/// that a modal [BottomSheet] needs to be displayed above all other content
+/// but the caller is inside another [Navigator].
+///
/// Returns a `Future` that resolves to the value (if any) that was passed to
/// [Navigator.pop] when the modal bottom sheet was closed.
///
@@ -417,14 +422,16 @@
double elevation,
ShapeBorder shape,
bool isScrollControlled = false,
+ bool useRootNavigator = false,
}) {
assert(context != null);
assert(builder != null);
assert(isScrollControlled != null);
+ assert(useRootNavigator != null);
assert(debugCheckHasMediaQuery(context));
assert(debugCheckHasMaterialLocalizations(context));
- return Navigator.push(context, _ModalBottomSheetRoute<T>(
+ return Navigator.of(context, rootNavigator: useRootNavigator).push(_ModalBottomSheetRoute<T>(
builder: builder,
theme: Theme.of(context, shadowThemeOnly: true),
isScrollControlled: isScrollControlled,
diff --git a/packages/flutter/test/material/bottom_sheet_test.dart b/packages/flutter/test/material/bottom_sheet_test.dart
index 15cca8e..f0cf7e6 100644
--- a/packages/flutter/test/material/bottom_sheet_test.dart
+++ b/packages/flutter/test/material/bottom_sheet_test.dart
@@ -361,4 +361,91 @@
), ignoreTransform: true, ignoreRect: true, ignoreId: true));
semantics.dispose();
});
+
+ testWidgets('showModalBottomSheet does not use root Navigator by default', (WidgetTester tester) async {
+ await tester.pumpWidget(MaterialApp(
+ home: Scaffold(
+ body: Navigator(onGenerateRoute: (RouteSettings settings) => MaterialPageRoute<void>(builder: (_) {
+ return const _TestPage();
+ })),
+ bottomNavigationBar: BottomNavigationBar(
+ items: const <BottomNavigationBarItem>[
+ BottomNavigationBarItem(
+ icon: Icon(Icons.ac_unit),
+ title: Text('Item 1'),
+ ),
+ BottomNavigationBarItem(
+ icon: Icon(Icons.style),
+ title: Text('Item 2'),
+ )
+ ],
+ ),
+ ),
+ ));
+
+ await tester.tap(find.text('Show bottom sheet'));
+ await tester.pumpAndSettle();
+
+ // Bottom sheet is displayed in correct position within the inner navigator
+ // and above the BottomNavigationBar.
+ expect(tester.getBottomLeft(find.byType(BottomSheet)).dy, 544.0);
+ });
+
+ testWidgets('showModalBottomSheet uses root Navigator when specified', (WidgetTester tester) async {
+ await tester.pumpWidget(MaterialApp(
+ home: Scaffold(
+ body: Navigator(onGenerateRoute: (RouteSettings settings) => MaterialPageRoute<void>(builder: (_) {
+ return const _TestPage(useRootNavigator: true);
+ })),
+ bottomNavigationBar: BottomNavigationBar(
+ items: const <BottomNavigationBarItem>[
+ BottomNavigationBarItem(
+ icon: Icon(Icons.ac_unit),
+ title: Text('Item 1'),
+ ),
+ BottomNavigationBarItem(
+ icon: Icon(Icons.style),
+ title: Text('Item 2'),
+ )
+ ],
+ ),
+ ),
+ ));
+
+ await tester.tap(find.text('Show bottom sheet'));
+ await tester.pumpAndSettle();
+
+ // Bottom sheet is displayed in correct position above all content including
+ // the BottomNavigationBar.
+ expect(tester.getBottomLeft(find.byType(BottomSheet)).dy, 600.0);
+ });
+}
+
+class _TestPage extends StatelessWidget {
+ const _TestPage({Key key, this.useRootNavigator}) : super(key: key);
+
+ final bool useRootNavigator;
+
+ @override
+ Widget build(BuildContext context) {
+ return Center(
+ child: FlatButton(
+ child: const Text('Show bottom sheet'),
+ onPressed: () {
+ if (useRootNavigator != null) {
+ showModalBottomSheet<void>(
+ useRootNavigator: useRootNavigator,
+ context: context,
+ builder: (_) => const Text('Modal bottom sheet'),
+ );
+ } else {
+ showModalBottomSheet<void>(
+ context: context,
+ builder: (_) => const Text('Modal bottom sheet'),
+ );
+ }
+ }
+ ),
+ );
+ }
}