Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions packages/material_ui/lib/src/bottom_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -971,7 +971,9 @@ class ModalBottomSheetRoute<T> extends PopupRoute<T> {
/// Specifies the color of the modal barrier that darkens everything below the
/// bottom sheet.
///
/// Defaults to `Colors.black54` if not provided.
/// If this is null, then [BottomSheetThemeData.modalBarrierColor] is used.
/// If that is also null, then [ColorScheme.scrim] is used with an opacity
/// matching [Colors.black54].
final Color? modalBarrierColor;

/// Specifies whether the bottom sheet will be dismissed
Expand Down Expand Up @@ -1103,7 +1105,17 @@ class ModalBottomSheetRoute<T> extends PopupRoute<T> {
final String? barrierLabel;

@override
Color get barrierColor => modalBarrierColor ?? Colors.black54;
Color get barrierColor {
if (modalBarrierColor != null) {
return modalBarrierColor!;
}
if (navigator != null) {
final ThemeData theme = Theme.of(navigator!.context);
return theme.bottomSheetTheme.modalBarrierColor ??
theme.colorScheme.scrim.withValues(alpha: Colors.black54.a);
}
return Colors.black54;
}

AnimationController? _animationController;

Expand Down Expand Up @@ -1343,7 +1355,10 @@ Future<T?> showModalBottomSheet<T>({
clipBehavior: clipBehavior,
constraints: constraints,
isDismissible: isDismissible,
modalBarrierColor: barrierColor ?? Theme.of(context).bottomSheetTheme.modalBarrierColor,
modalBarrierColor:
barrierColor ??
Theme.of(context).bottomSheetTheme.modalBarrierColor ??
Theme.of(context).colorScheme.scrim.withValues(alpha: Colors.black54.a),
enableDrag: enableDrag,
showDragHandle: showDragHandle,
settings: routeSettings,
Expand Down
23 changes: 15 additions & 8 deletions packages/material_ui/lib/src/dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1569,10 +1569,11 @@ class _DialogContentPage extends Page<void> {
///
/// The `barrierColor` argument is used to specify the color of the modal
/// barrier that darkens everything below the dialog. If `null` the `barrierColor`
/// field from `DialogThemeData` is used. If that is `null` the default color
/// `Colors.black54` is used. If windowing is enabled via `flutter config
/// --enable-windowing`, then this argument is ignored as dialogs are displayed
/// in their own windows which do not have a modal barrier.
/// field from `DialogThemeData` is used. If that is also `null`,
/// [ColorScheme.scrim] is used with an opacity matching [Colors.black54].
/// If windowing is enabled via `flutter config --enable-windowing`, then this
/// argument is ignored as dialogs are displayed in their own windows which do
/// not have a modal barrier.
///
/// 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
Expand Down Expand Up @@ -1706,7 +1707,7 @@ Future<T?> showDialog<T>({
barrierColor ??
DialogTheme.of(context).barrierColor ??
Theme.of(context).dialogTheme.barrierColor ??
Colors.black54,
Theme.of(context).colorScheme.scrim.withValues(alpha: Colors.black54.a),
barrierDismissible: barrierDismissible,
barrierLabel: barrierLabel,
useSafeArea: useSafeArea,
Expand Down Expand Up @@ -1843,8 +1844,9 @@ bool _debugIsActive(BuildContext context) {
/// barrier will dismiss the dialog. It is `true` by default and cannot be `null`.
///
/// The `barrierColor` argument is used to specify the color of the modal
/// barrier that darkens everything below the dialog. If `null`, the default
/// color `Colors.black54` is used.
/// barrier that darkens everything below the dialog. If `null`, the
/// `barrierColor` field from [DialogThemeData] is used. If that is also `null`,
/// [ColorScheme.scrim] is used with an opacity matching [Colors.black54].
///
/// 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
Expand Down Expand Up @@ -1872,7 +1874,7 @@ class DialogRoute<T> extends RawDialogRoute<T> {
required BuildContext context,
required WidgetBuilder builder,
CapturedThemes? themes,
super.barrierColor = Colors.black54,
Color? barrierColor,
super.barrierDismissible,
String? barrierLabel,
bool useSafeArea = true,
Expand All @@ -1884,6 +1886,11 @@ class DialogRoute<T> extends RawDialogRoute<T> {
AnimationStyle? animationStyle,
}) : _animationStyle = animationStyle,
super(
barrierColor:
barrierColor ??
DialogTheme.of(context).barrierColor ??
Theme.of(context).dialogTheme.barrierColor ??
Theme.of(context).colorScheme.scrim.withValues(alpha: Colors.black54.a),
pageBuilder:
(
BuildContext buildContext,
Expand Down
7 changes: 5 additions & 2 deletions packages/material_ui/lib/src/drawer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,8 @@ class DrawerController extends StatefulWidget {
/// a drawer is open.
///
/// If this is null, then [DrawerThemeData.scrimColor] is used. If that
/// is also null, then it defaults to [Colors.black54].
/// is also null, then [ColorScheme.scrim] is used with an opacity matching
/// [Colors.black54].
final Color? scrimColor;

/// Determines if the [Drawer] can be opened with a drag gesture.
Expand Down Expand Up @@ -713,7 +714,9 @@ class DrawerControllerState extends State<DrawerController> with SingleTickerPro
};

final Color scrimColor =
widget.scrimColor ?? DrawerTheme.of(context).scrimColor ?? Colors.black54;
widget.scrimColor ??
DrawerTheme.of(context).scrimColor ??
Theme.of(context).colorScheme.scrim.withValues(alpha: Colors.black54.a);
final Color effectiveScrimColor = scrimColor.withValues(
alpha: scrimColor.a * _controller.value,
);
Expand Down
4 changes: 3 additions & 1 deletion packages/material_ui/lib/src/scaffold.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
/// @docImport 'bottom_app_bar.dart';
/// @docImport 'bottom_navigation_bar.dart';
/// @docImport 'bottom_sheet_theme.dart';
/// @docImport 'color_scheme.dart';
/// @docImport 'drawer_theme.dart';
/// @docImport 'icon_button.dart';
/// @docImport 'tab_controller.dart';
Expand Down Expand Up @@ -1955,7 +1956,8 @@ class Scaffold extends StatefulWidget {
/// The color to use for the scrim that obscures primary content while a drawer is open.
///
/// If this is null, then [DrawerThemeData.scrimColor] is used. If that
/// is also null, then it defaults to [Colors.black54].
/// is also null, then [ColorScheme.scrim] is used with an opacity matching
/// [Colors.black54].
final Color? drawerScrimColor;

/// A builder for the widget that obscures primary content while a bottom sheet is open.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
changelog: |
- Uses `ColorScheme.scrim` as the default fallback for dialog, modal bottom sheet, and drawer barrier/scrim colors.
version: patch
216 changes: 216 additions & 0 deletions packages/material_ui/test/bottom_sheet_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,222 @@ void main() {
expect(modalBarrier.color, barrierColor);
});

testWidgets('Modal bottom sheet barrier color uses ColorScheme.scrim', (
WidgetTester tester,
) async {
const Color scrim = Colors.red;
late BuildContext savedContext;

await tester.pumpWidget(
MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple, scrim: scrim),
),
home: Builder(
builder: (BuildContext context) {
savedContext = context;
return const SizedBox.expand();
},
),
),
);

unawaited(
showModalBottomSheet<void>(
context: savedContext,
builder: (BuildContext context) {
return const SizedBox(height: 200, child: Text('BottomSheet'));
},
),
);
await tester.pumpAndSettle();

final ModalBarrier modalBarrier = tester.widget(find.byType(ModalBarrier).last);
expect(modalBarrier.color, scrim.withValues(alpha: Colors.black54.a));
});

testWidgets('BottomSheetTheme.modalBarrierColor takes precedence over ColorScheme.scrim', (
WidgetTester tester,
) async {
const Color scrim = Colors.red;
const Color themeBarrierColor = Colors.blue;
late BuildContext savedContext;

await tester.pumpWidget(
MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple, scrim: scrim),
bottomSheetTheme: const BottomSheetThemeData(modalBarrierColor: themeBarrierColor),
),
home: Builder(
builder: (BuildContext context) {
savedContext = context;
return const SizedBox.expand();
},
),
),
);

unawaited(
showModalBottomSheet<void>(
context: savedContext,
builder: (BuildContext context) {
return const SizedBox(height: 200, child: Text('BottomSheet'));
},
),
);
await tester.pumpAndSettle();

final ModalBarrier modalBarrier = tester.widget(find.byType(ModalBarrier).last);
expect(modalBarrier.color, themeBarrierColor);
});

testWidgets('Explicit barrierColor takes precedence over ColorScheme.scrim for bottom sheets', (
WidgetTester tester,
) async {
const Color scrim = Colors.red;
const Color explicitBarrierColor = Colors.green;
late BuildContext savedContext;

await tester.pumpWidget(
MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple, scrim: scrim),
bottomSheetTheme: const BottomSheetThemeData(modalBarrierColor: Colors.blue),
),
home: Builder(
builder: (BuildContext context) {
savedContext = context;
return const SizedBox.expand();
},
),
),
);

unawaited(
showModalBottomSheet<void>(
context: savedContext,
barrierColor: explicitBarrierColor,
builder: (BuildContext context) {
return const SizedBox(height: 200, child: Text('BottomSheet'));
},
),
);
await tester.pumpAndSettle();

final ModalBarrier modalBarrier = tester.widget(find.byType(ModalBarrier).last);
expect(modalBarrier.color, explicitBarrierColor);
});

testWidgets('Bottom sheet ColorScheme.scrim alpha is normalized to Colors.black54 opacity', (
WidgetTester tester,
) async {
final Color scrim = Colors.red.withValues(alpha: 0.2);
late BuildContext savedContext;

await tester.pumpWidget(
MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple, scrim: scrim),
),
home: Builder(
builder: (BuildContext context) {
savedContext = context;
return const SizedBox.expand();
},
),
),
);

unawaited(
showModalBottomSheet<void>(
context: savedContext,
builder: (BuildContext context) {
return const SizedBox(height: 200, child: Text('BottomSheet'));
},
),
);
await tester.pumpAndSettle();

final ModalBarrier modalBarrier = tester.widget(find.byType(ModalBarrier).last);
expect(modalBarrier.color, scrim.withValues(alpha: Colors.black54.a));
});

testWidgets('ModalBottomSheetRoute barrier color uses ColorScheme.scrim', (
WidgetTester tester,
) async {
const Color scrim = Colors.red;
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple, scrim: scrim),
),
home: Builder(
builder: (BuildContext context) {
return TextButton(
onPressed: () {
Navigator.of(context).push<void>(
ModalBottomSheetRoute<void>(
isScrollControlled: false,
builder: (BuildContext context) {
return const SizedBox(height: 200, child: Text('BottomSheet'));
},
),
);
},
child: const Text('Open'),
);
},
),
),
);

await tester.tap(find.text('Open'));
await tester.pumpAndSettle();

final ModalBarrier modalBarrier = tester.widget(find.byType(ModalBarrier).last);
expect(modalBarrier.color, scrim.withValues(alpha: Colors.black54.a));
});

testWidgets(
'ModalBottomSheetRoute uses BottomSheetTheme.modalBarrierColor over ColorScheme.scrim',
(WidgetTester tester) async {
const Color scrim = Colors.red;
const Color themeBarrierColor = Colors.blue;
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple, scrim: scrim),
bottomSheetTheme: const BottomSheetThemeData(modalBarrierColor: themeBarrierColor),
),
home: Builder(
builder: (BuildContext context) {
return TextButton(
onPressed: () {
Navigator.of(context).push<void>(
ModalBottomSheetRoute<void>(
isScrollControlled: false,
builder: (BuildContext context) {
return const SizedBox(height: 200, child: Text('BottomSheet'));
},
),
);
},
child: const Text('Open'),
);
},
),
),
);

await tester.tap(find.text('Open'));
await tester.pumpAndSettle();

final ModalBarrier modalBarrier = tester.widget(find.byType(ModalBarrier).last);
expect(modalBarrier.color, themeBarrierColor);
},
);

testWidgets('Material3 - BottomSheet uses fallback values', (WidgetTester tester) async {
const Color surfaceColor = Colors.pink;
const Color surfaceTintColor = Colors.blue;
Expand Down
Loading