Added 'barrierColor' and 'useSafeArea' parameters to showDialog. (#54110)

diff --git a/packages/flutter/lib/src/material/dialog.dart b/packages/flutter/lib/src/material/dialog.dart
index be0f638..dc1b79a 100644
--- a/packages/flutter/lib/src/material/dialog.dart
+++ b/packages/flutter/lib/src/material/dialog.dart
@@ -871,10 +871,23 @@
 /// the dialog. It is only used when the method is called. Its corresponding
 /// widget can be safely removed from the tree before the dialog is closed.
 ///
+/// The `barrierDismissible` argument is used to indicate whether tapping on the
+/// barrier will dismiss the dialog. It is `true` by default and can not be `null`.
+///
+/// The `barrierColor` argument is used to specify the color of the modal
+/// barrier that darkens everything the dialog. If `null` the default color
+/// `Colors.black54` is used.
+///
+/// The `useSafeArea` argument is used to indicate if the dialog should only
+/// display in 'safe' areas of the screen not used by the operating system
+/// (see [SafeArea] for more details). It is `true` by default which will mean
+/// the dialog will not overlap operating system areas. If it is set to `false`
+/// the dialog will only be constrained by the screen size. It can not be 'null`.
+//
 /// The `useRootNavigator` argument is used to determine whether to push the
 /// dialog to the [Navigator] furthest from or nearest to the given `context`.
 /// By default, `useRootNavigator` is `true` and the dialog route created by
-/// this method is pushed to the root navigator.
+/// this method is pushed to the root navigator. It can not be `null`.
 ///
 /// The `routeSettings` argument is passed to [showGeneralDialog],
 /// see [RouteSettings] for details.
@@ -897,7 +910,12 @@
 ///  * <https://material.io/design/components/dialogs.html>
 Future<T> showDialog<T>({
   @required BuildContext context,
+  WidgetBuilder builder,
   bool barrierDismissible = true,
+  Color barrierColor,
+  bool useSafeArea = true,
+  bool useRootNavigator = true,
+  RouteSettings routeSettings,
   @Deprecated(
     'Instead of using the "child" argument, return the child from a closure '
     'provided to the "builder" argument. This will ensure that the BuildContext '
@@ -905,11 +923,10 @@
     'This feature was deprecated after v0.2.3.'
   )
   Widget child,
-  WidgetBuilder builder,
-  bool useRootNavigator = true,
-  RouteSettings routeSettings,
 }) {
   assert(child == null || builder == null);
+  assert(barrierDismissible != null);
+  assert(useSafeArea != null);
   assert(useRootNavigator != null);
   assert(debugCheckHasMaterialLocalizations(context));
 
@@ -918,19 +935,21 @@
     context: context,
     pageBuilder: (BuildContext buildContext, Animation<double> animation, Animation<double> secondaryAnimation) {
       final Widget pageChild = child ?? Builder(builder: builder);
-      return SafeArea(
-        child: Builder(
-          builder: (BuildContext context) {
-            return theme != null
-                ? Theme(data: theme, child: pageChild)
-                : pageChild;
-          }
-        ),
+      Widget dialog = Builder(
+        builder: (BuildContext context) {
+          return theme != null
+            ? Theme(data: theme, child: pageChild)
+            : pageChild;
+        }
       );
+      if (useSafeArea) {
+        dialog = SafeArea(child: dialog);
+      }
+      return dialog;
     },
     barrierDismissible: barrierDismissible,
     barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
-    barrierColor: Colors.black54,
+    barrierColor: barrierColor ?? Colors.black54,
     transitionDuration: const Duration(milliseconds: 150),
     transitionBuilder: _buildMaterialDialogTransitions,
     useRootNavigator: useRootNavigator,
diff --git a/packages/flutter/test/material/dialog_test.dart b/packages/flutter/test/material/dialog_test.dart
index 2bd1161..9d579bb 100644
--- a/packages/flutter/test/material/dialog_test.dart
+++ b/packages/flutter/test/material/dialog_test.dart
@@ -344,6 +344,37 @@
 
   });
 
+  testWidgets('Barrier color', (WidgetTester tester) async {
+    await tester.pumpWidget(
+      const MaterialApp(
+        home: Center(child: Text('Test')),
+      ),
+    );
+    final BuildContext context = tester.element(find.text('Test'));
+
+    // Test default barrier color
+    showDialog<void>(
+      context: context,
+      builder: (BuildContext context) {
+        return const Text('Dialog');
+      },
+    );
+    await tester.pumpAndSettle();
+    expect(tester.widget<ModalBarrier>(find.byType(ModalBarrier).last).color, Colors.black54);
+
+    // Dismiss it and test a custom barrier color
+    await tester.tapAt(const Offset(10.0, 10.0));
+    showDialog<void>(
+      context: context,
+      builder: (BuildContext context) {
+        return const Text('Dialog');
+      },
+      barrierColor: Colors.pink,
+    );
+    await tester.pumpAndSettle();
+    expect(tester.widget<ModalBarrier>(find.byType(ModalBarrier).last).color, Colors.pink);
+  });
+
   testWidgets('Dialog hides underlying semantics tree', (WidgetTester tester) async {
     final SemanticsTester semantics = SemanticsTester(tester);
     const String buttonText = 'A button covered by dialog overlay';
@@ -1020,6 +1051,47 @@
     await tester.pump();
   });
 
+  testWidgets('showDialog safe area', (WidgetTester tester) async {
+    await tester.pumpWidget(
+      MaterialApp(
+        builder: (BuildContext context, Widget child) {
+          return MediaQuery(
+            // Set up the safe area to be 20 pixels in from each side
+            data: const MediaQueryData(padding: EdgeInsets.all(20.0)),
+            child: child,
+          );
+        },
+        home: const Center(child: Text('Test')),
+      ),
+    );
+    final BuildContext context = tester.element(find.text('Test'));
+
+    // By default it should honor the safe area
+    showDialog<void>(
+      context: context,
+      builder: (BuildContext context) {
+        return const Placeholder();
+      },
+    );
+    await tester.pumpAndSettle();
+    expect(tester.getTopLeft(find.byType(Placeholder)), const Offset(20.0, 20.0));
+    expect(tester.getBottomRight(find.byType(Placeholder)), const Offset(780.0, 580.0));
+
+    // Dismiss it and test with useSafeArea off
+    await tester.tapAt(const Offset(10.0, 10.0));
+    showDialog<void>(
+      context: context,
+      builder: (BuildContext context) {
+        return const Placeholder();
+      },
+      useSafeArea: false,
+    );
+    await tester.pumpAndSettle();
+    // Should take up the whole screen
+    expect(tester.getTopLeft(find.byType(Placeholder)), const Offset(0.0, 0.0));
+    expect(tester.getBottomRight(find.byType(Placeholder)), const Offset(800.0, 600.0));
+  });
+
   testWidgets('showDialog uses root navigator by default', (WidgetTester tester) async {
     final DialogObserver rootObserver = DialogObserver();
     final DialogObserver nestedObserver = DialogObserver();