Unverified Commit 6a1468db authored by rami-a's avatar rami-a Committed by GitHub

Add BottomSheetTheme to enable theming color, elevation, shape of BottomSheet (#31318)

* Introduce BottomSheetTheme and shape support for bottom sheet

* Add bottom sheet theme to ThemeData. Use theme in bottom sheet build

* Expose color, elevation, shape to showModalBottomSheet helper

* Expose color, elevation, shape to showBottomSheet helper

* Address PR feedback

* Address PR feedback

* Address additional PR feedback
parent 60a1b2b9
...@@ -27,6 +27,7 @@ export 'src/material/bottom_app_bar.dart'; ...@@ -27,6 +27,7 @@ export 'src/material/bottom_app_bar.dart';
export 'src/material/bottom_app_bar_theme.dart'; export 'src/material/bottom_app_bar_theme.dart';
export 'src/material/bottom_navigation_bar.dart'; export 'src/material/bottom_navigation_bar.dart';
export 'src/material/bottom_sheet.dart'; export 'src/material/bottom_sheet.dart';
export 'src/material/bottom_sheet_theme.dart';
export 'src/material/button.dart'; export 'src/material/button.dart';
export 'src/material/button_bar.dart'; export 'src/material/button_bar.dart';
export 'src/material/button_theme.dart'; export 'src/material/button_theme.dart';
......
...@@ -8,6 +8,7 @@ import 'package:flutter/foundation.dart'; ...@@ -8,6 +8,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/scheduler.dart'; import 'package:flutter/scheduler.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'bottom_sheet_theme.dart';
import 'colors.dart'; import 'colors.dart';
import 'debug.dart'; import 'debug.dart';
import 'material.dart'; import 'material.dart';
...@@ -56,14 +57,15 @@ class BottomSheet extends StatefulWidget { ...@@ -56,14 +57,15 @@ class BottomSheet extends StatefulWidget {
Key key, Key key,
this.animationController, this.animationController,
this.enableDrag = true, this.enableDrag = true,
this.elevation = 0.0,
this.backgroundColor, this.backgroundColor,
this.elevation,
this.shape,
@required this.onClosing, @required this.onClosing,
@required this.builder, @required this.builder,
}) : assert(enableDrag != null), }) : assert(enableDrag != null),
assert(onClosing != null), assert(onClosing != null),
assert(builder != null), assert(builder != null),
assert(elevation != null && elevation >= 0.0), assert(elevation == null || elevation >= 0.0),
super(key: key); super(key: key);
/// The animation controller that controls the bottom sheet's entrance and /// The animation controller that controls the bottom sheet's entrance and
...@@ -92,6 +94,13 @@ class BottomSheet extends StatefulWidget { ...@@ -92,6 +94,13 @@ class BottomSheet extends StatefulWidget {
/// Default is true. /// Default is true.
final bool enableDrag; final bool enableDrag;
/// The bottom sheet's background color.
///
/// Defines the bottom sheet's [Material.color].
///
/// Defaults to null and falls back to [Material]'s default.
final Color backgroundColor;
/// The z-coordinate at which to place this material relative to its parent. /// The z-coordinate at which to place this material relative to its parent.
/// ///
/// This controls the size of the shadow below the material. /// This controls the size of the shadow below the material.
...@@ -99,10 +108,12 @@ class BottomSheet extends StatefulWidget { ...@@ -99,10 +108,12 @@ class BottomSheet extends StatefulWidget {
/// Defaults to 0. The value is non-negative. /// Defaults to 0. The value is non-negative.
final double elevation; final double elevation;
/// The color for the [Material] of the bottom sheet. /// The shape of the bottom sheet.
/// ///
/// Defaults to [Colors.white]. The value must not be null. /// Defines the bottom sheet's [Material.shape].
final Color backgroundColor; ///
/// Defaults to null and falls back to [Material]'s default.
final ShapeBorder shape;
@override @override
_BottomSheetState createState() => _BottomSheetState(); _BottomSheetState createState() => _BottomSheetState();
...@@ -170,10 +181,16 @@ class _BottomSheetState extends State<BottomSheet> { ...@@ -170,10 +181,16 @@ class _BottomSheetState extends State<BottomSheet> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final BottomSheetThemeData bottomSheetTheme = Theme.of(context).bottomSheetTheme;
final Color color = widget.backgroundColor ?? bottomSheetTheme.backgroundColor;
final double elevation = widget.elevation ?? bottomSheetTheme.elevation ?? 0;
final ShapeBorder shape = widget.shape ?? bottomSheetTheme.shape;
final Widget bottomSheet = Material( final Widget bottomSheet = Material(
key: _childKey, key: _childKey,
color: widget.backgroundColor, color: color,
elevation: widget.elevation, elevation: elevation,
shape: shape,
child: NotificationListener<DraggableScrollableNotification>( child: NotificationListener<DraggableScrollableNotification>(
onNotification: extentChanged, onNotification: extentChanged,
child: widget.builder(context), child: widget.builder(context),
...@@ -227,12 +244,18 @@ class _ModalBottomSheet<T> extends StatefulWidget { ...@@ -227,12 +244,18 @@ class _ModalBottomSheet<T> extends StatefulWidget {
const _ModalBottomSheet({ const _ModalBottomSheet({
Key key, Key key,
this.route, this.route,
this.backgroundColor,
this.elevation,
this.shape,
this.isScrollControlled = false, this.isScrollControlled = false,
}) : assert(isScrollControlled != null), }) : assert(isScrollControlled != null),
super(key: key); super(key: key);
final _ModalBottomSheetRoute<T> route; final _ModalBottomSheetRoute<T> route;
final bool isScrollControlled; final bool isScrollControlled;
final Color backgroundColor;
final double elevation;
final ShapeBorder shape;
@override @override
_ModalBottomSheetState<T> createState() => _ModalBottomSheetState<T>(); _ModalBottomSheetState<T> createState() => _ModalBottomSheetState<T>();
...@@ -279,7 +302,6 @@ class _ModalBottomSheetState<T> extends State<_ModalBottomSheet<T>> { ...@@ -279,7 +302,6 @@ class _ModalBottomSheetState<T> extends State<_ModalBottomSheet<T>> {
child: CustomSingleChildLayout( child: CustomSingleChildLayout(
delegate: _ModalBottomSheetLayout(animationValue, widget.isScrollControlled), delegate: _ModalBottomSheetLayout(animationValue, widget.isScrollControlled),
child: BottomSheet( child: BottomSheet(
backgroundColor: widget.route.backgroundColor,
animationController: widget.route._animationController, animationController: widget.route._animationController,
onClosing: () { onClosing: () {
if (widget.route.isCurrent) { if (widget.route.isCurrent) {
...@@ -287,6 +309,9 @@ class _ModalBottomSheetState<T> extends State<_ModalBottomSheet<T>> { ...@@ -287,6 +309,9 @@ class _ModalBottomSheetState<T> extends State<_ModalBottomSheet<T>> {
} }
}, },
builder: widget.route.builder, builder: widget.route.builder,
backgroundColor: widget.backgroundColor,
elevation: widget.elevation,
shape: widget.shape,
), ),
), ),
), ),
...@@ -302,8 +327,10 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> { ...@@ -302,8 +327,10 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> {
this.builder, this.builder,
this.theme, this.theme,
this.barrierLabel, this.barrierLabel,
@required this.isScrollControlled,
this.backgroundColor, this.backgroundColor,
this.elevation,
this.shape,
@required this.isScrollControlled,
RouteSettings settings, RouteSettings settings,
}) : assert(isScrollControlled != null), }) : assert(isScrollControlled != null),
super(settings: settings); super(settings: settings);
...@@ -312,6 +339,8 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> { ...@@ -312,6 +339,8 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> {
final ThemeData theme; final ThemeData theme;
final bool isScrollControlled; final bool isScrollControlled;
final Color backgroundColor; final Color backgroundColor;
final double elevation;
final ShapeBorder shape;
@override @override
Duration get transitionDuration => _bottomSheetDuration; Duration get transitionDuration => _bottomSheetDuration;
...@@ -327,6 +356,7 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> { ...@@ -327,6 +356,7 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> {
AnimationController _animationController; AnimationController _animationController;
@override @override
AnimationController createAnimationController() { AnimationController createAnimationController() {
assert(_animationController == null); assert(_animationController == null);
...@@ -341,7 +371,13 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> { ...@@ -341,7 +371,13 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> {
Widget bottomSheet = MediaQuery.removePadding( Widget bottomSheet = MediaQuery.removePadding(
context: context, context: context,
removeTop: true, removeTop: true,
child: _ModalBottomSheet<T>(route: this, isScrollControlled: isScrollControlled), child: _ModalBottomSheet<T>(
route: this,
backgroundColor: backgroundColor,
elevation: elevation,
shape: shape,
isScrollControlled: isScrollControlled
),
); );
if (theme != null) if (theme != null)
bottomSheet = Theme(data: theme, child: bottomSheet); bottomSheet = Theme(data: theme, child: bottomSheet);
...@@ -384,8 +420,10 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> { ...@@ -384,8 +420,10 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> {
Future<T> showModalBottomSheet<T>({ Future<T> showModalBottomSheet<T>({
@required BuildContext context, @required BuildContext context,
@required WidgetBuilder builder, @required WidgetBuilder builder,
bool isScrollControlled = false,
Color backgroundColor, Color backgroundColor,
double elevation,
ShapeBorder shape,
bool isScrollControlled = false,
}) { }) {
assert(context != null); assert(context != null);
assert(builder != null); assert(builder != null);
...@@ -397,8 +435,10 @@ Future<T> showModalBottomSheet<T>({ ...@@ -397,8 +435,10 @@ Future<T> showModalBottomSheet<T>({
builder: builder, builder: builder,
theme: Theme.of(context, shadowThemeOnly: true), theme: Theme.of(context, shadowThemeOnly: true),
isScrollControlled: isScrollControlled, isScrollControlled: isScrollControlled,
backgroundColor: backgroundColor,
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel, barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
backgroundColor: backgroundColor,
elevation: elevation,
shape: shape,
)); ));
} }
...@@ -440,6 +480,8 @@ PersistentBottomSheetController<T> showBottomSheet<T>({ ...@@ -440,6 +480,8 @@ PersistentBottomSheetController<T> showBottomSheet<T>({
@required BuildContext context, @required BuildContext context,
@required WidgetBuilder builder, @required WidgetBuilder builder,
Color backgroundColor, Color backgroundColor,
double elevation,
ShapeBorder shape,
}) { }) {
assert(context != null); assert(context != null);
assert(builder != null); assert(builder != null);
...@@ -448,5 +490,7 @@ PersistentBottomSheetController<T> showBottomSheet<T>({ ...@@ -448,5 +490,7 @@ PersistentBottomSheetController<T> showBottomSheet<T>({
return Scaffold.of(context).showBottomSheet<T>( return Scaffold.of(context).showBottomSheet<T>(
builder, builder,
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
elevation: elevation,
shape: shape,
); );
} }
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui' show lerpDouble;
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
/// Defines default property values for [BottomSheet]'s [Material].
///
/// Descendant widgets obtain the current [BottomSheetThemeData] object
/// using `Theme.of(context).bottomSheetTheme`. Instances of
/// [BottomSheetThemeData] can be customized with
/// [BottomSheetThemeData.copyWith].
///
/// Typically a [BottomSheetThemeData] is specified as part of the
/// overall [Theme] with [ThemeData.bottomSheetTheme].
///
/// All [BottomSheetThemeData] properties are `null` by default.
/// When null, the [BottomSheet] will provide its own defaults.
///
/// See also:
///
/// * [ThemeData], which describes the overall theme information for the
/// application.
class BottomSheetThemeData extends Diagnosticable {
/// Creates a theme that can be used for [ThemeData.bottomSheetTheme].
const BottomSheetThemeData({
this.backgroundColor,
this.elevation,
this.shape,
});
/// Default value for [BottomSheet.backgroundColor].
///
/// If null, [BottomSheet] defaults to [Material]'s default.
final Color backgroundColor;
/// Default value for [BottomSheet.elevation].
///
/// {@macro flutter.material.material.elevation}
///
/// If null, [BottomSheet] defaults to 0.0.
final double elevation;
/// Default value for [BottomSheet.shape].
///
/// If null, no overriding shape is specified for [BottomSheet], so the
/// [BottomSheet] is rectangular.
final ShapeBorder shape;
/// Creates a copy of this object with the given fields replaced with the
/// new values.
BottomSheetThemeData copyWith({
Color backgroundColor,
double elevation,
ShapeBorder shape,
}) {
return BottomSheetThemeData(
backgroundColor: backgroundColor ?? this.backgroundColor,
elevation: elevation ?? this.elevation,
shape: shape ?? this.shape,
);
}
/// Linearly interpolate between two bottom sheet themes.
///
/// If both arguments are null then null is returned.
///
/// {@macro dart.ui.shadow.lerp}
static BottomSheetThemeData lerp(BottomSheetThemeData a, BottomSheetThemeData b, double t) {
assert(t != null);
if (a == null && b == null)
return null;
return BottomSheetThemeData(
backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t),
elevation: lerpDouble(a?.elevation, b?.elevation, t),
shape: ShapeBorder.lerp(a?.shape, b?.shape, t),
);
}
@override
int get hashCode {
return hashValues(
backgroundColor,
elevation,
shape,
);
}
@override
bool operator ==(Object other) {
if (identical(this, other))
return true;
if (other.runtimeType != runtimeType)
return false;
final BottomSheetThemeData typedOther = other;
return typedOther.backgroundColor == backgroundColor
&& typedOther.elevation == elevation
&& typedOther.shape == shape;
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<Color>('backgroundColor', backgroundColor, defaultValue: null));
properties.add(DiagnosticsProperty<double>('elevation', elevation, defaultValue: null));
properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: null));
}
}
...@@ -1541,6 +1541,8 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin { ...@@ -1541,6 +1541,8 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
bool isPersistent, { bool isPersistent, {
AnimationController animationController, AnimationController animationController,
Color backgroundColor, Color backgroundColor,
double elevation,
ShapeBorder shape,
}) { }) {
assert(() { assert(() {
if (widget.bottomSheet != null && isPersistent && _currentBottomSheet != null) { if (widget.bottomSheet != null && isPersistent && _currentBottomSheet != null) {
...@@ -1619,6 +1621,8 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin { ...@@ -1619,6 +1621,8 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
builder: builder, builder: builder,
isPersistent: isPersistent, isPersistent: isPersistent,
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
elevation: elevation,
shape: shape,
); );
if (!isPersistent) if (!isPersistent)
...@@ -1673,6 +1677,8 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin { ...@@ -1673,6 +1677,8 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
PersistentBottomSheetController<T> showBottomSheet<T>( PersistentBottomSheetController<T> showBottomSheet<T>(
WidgetBuilder builder, { WidgetBuilder builder, {
Color backgroundColor, Color backgroundColor,
double elevation,
ShapeBorder shape,
}) { }) {
assert(() { assert(() {
if (widget.bottomSheet != null) { if (widget.bottomSheet != null) {
...@@ -1694,6 +1700,8 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin { ...@@ -1694,6 +1700,8 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
false, false,
animationController: controller, animationController: controller,
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
elevation: elevation,
shape: shape,
); );
}); });
return _currentBottomSheet; return _currentBottomSheet;
...@@ -2216,6 +2224,8 @@ class _StandardBottomSheet extends StatefulWidget { ...@@ -2216,6 +2224,8 @@ class _StandardBottomSheet extends StatefulWidget {
this.builder, this.builder,
this.isPersistent = false, this.isPersistent = false,
this.backgroundColor, this.backgroundColor,
this.elevation,
this.shape,
}) : super(key: key); }) : super(key: key);
final AnimationController animationController; // we control it, but it must be disposed by whoever created it. final AnimationController animationController; // we control it, but it must be disposed by whoever created it.
...@@ -2225,6 +2235,8 @@ class _StandardBottomSheet extends StatefulWidget { ...@@ -2225,6 +2235,8 @@ class _StandardBottomSheet extends StatefulWidget {
final WidgetBuilder builder; final WidgetBuilder builder;
final bool isPersistent; final bool isPersistent;
final Color backgroundColor; final Color backgroundColor;
final double elevation;
final ShapeBorder shape;
@override @override
_StandardBottomSheetState createState() => _StandardBottomSheetState(); _StandardBottomSheetState createState() => _StandardBottomSheetState();
...@@ -2311,6 +2323,8 @@ class _StandardBottomSheetState extends State<_StandardBottomSheet> { ...@@ -2311,6 +2323,8 @@ class _StandardBottomSheetState extends State<_StandardBottomSheet> {
onClosing: widget.onClosing, onClosing: widget.onClosing,
builder: widget.builder, builder: widget.builder,
backgroundColor: widget.backgroundColor, backgroundColor: widget.backgroundColor,
elevation: widget.elevation,
shape: widget.shape,
), ),
), ),
); );
......
...@@ -11,6 +11,7 @@ import 'package:flutter/widgets.dart'; ...@@ -11,6 +11,7 @@ import 'package:flutter/widgets.dart';
import 'app_bar_theme.dart'; import 'app_bar_theme.dart';
import 'bottom_app_bar_theme.dart'; import 'bottom_app_bar_theme.dart';
import 'bottom_sheet_theme.dart';
import 'button_theme.dart'; import 'button_theme.dart';
import 'card_theme.dart'; import 'card_theme.dart';
import 'chip_theme.dart'; import 'chip_theme.dart';
...@@ -165,6 +166,7 @@ class ThemeData extends Diagnosticable { ...@@ -165,6 +166,7 @@ class ThemeData extends Diagnosticable {
Typography typography, Typography typography,
CupertinoThemeData cupertinoOverrideTheme, CupertinoThemeData cupertinoOverrideTheme,
SnackBarThemeData snackBarTheme, SnackBarThemeData snackBarTheme,
BottomSheetThemeData bottomSheetTheme,
}) { }) {
brightness ??= Brightness.light; brightness ??= Brightness.light;
final bool isDark = brightness == Brightness.dark; final bool isDark = brightness == Brightness.dark;
...@@ -259,6 +261,7 @@ class ThemeData extends Diagnosticable { ...@@ -259,6 +261,7 @@ class ThemeData extends Diagnosticable {
floatingActionButtonTheme ??= const FloatingActionButtonThemeData(); floatingActionButtonTheme ??= const FloatingActionButtonThemeData();
cupertinoOverrideTheme = cupertinoOverrideTheme?.noDefault(); cupertinoOverrideTheme = cupertinoOverrideTheme?.noDefault();
snackBarTheme ??= const SnackBarThemeData(); snackBarTheme ??= const SnackBarThemeData();
bottomSheetTheme ??= const BottomSheetThemeData();
return ThemeData.raw( return ThemeData.raw(
brightness: brightness, brightness: brightness,
...@@ -313,6 +316,7 @@ class ThemeData extends Diagnosticable { ...@@ -313,6 +316,7 @@ class ThemeData extends Diagnosticable {
typography: typography, typography: typography,
cupertinoOverrideTheme: cupertinoOverrideTheme, cupertinoOverrideTheme: cupertinoOverrideTheme,
snackBarTheme: snackBarTheme, snackBarTheme: snackBarTheme,
bottomSheetTheme: bottomSheetTheme,
); );
} }
...@@ -379,6 +383,7 @@ class ThemeData extends Diagnosticable { ...@@ -379,6 +383,7 @@ class ThemeData extends Diagnosticable {
@required this.typography, @required this.typography,
@required this.cupertinoOverrideTheme, @required this.cupertinoOverrideTheme,
@required this.snackBarTheme, @required this.snackBarTheme,
@required this.bottomSheetTheme,
}) : assert(brightness != null), }) : assert(brightness != null),
assert(primaryColor != null), assert(primaryColor != null),
assert(primaryColorBrightness != null), assert(primaryColorBrightness != null),
...@@ -428,7 +433,8 @@ class ThemeData extends Diagnosticable { ...@@ -428,7 +433,8 @@ class ThemeData extends Diagnosticable {
assert(dialogTheme != null), assert(dialogTheme != null),
assert(floatingActionButtonTheme != null), assert(floatingActionButtonTheme != null),
assert(typography != null), assert(typography != null),
assert(snackBarTheme != null); assert(snackBarTheme != null),
assert(bottomSheetTheme != null);
// Warning: make sure these properties are in the exact same order as in // Warning: make sure these properties are in the exact same order as in
// hashValues() and in the raw constructor and in the order of fields in // hashValues() and in the raw constructor and in the order of fields in
...@@ -699,6 +705,9 @@ class ThemeData extends Diagnosticable { ...@@ -699,6 +705,9 @@ class ThemeData extends Diagnosticable {
/// can be overridden using attributes of this [cupertinoOverrideTheme]. /// can be overridden using attributes of this [cupertinoOverrideTheme].
final CupertinoThemeData cupertinoOverrideTheme; final CupertinoThemeData cupertinoOverrideTheme;
/// A theme for customizing the color, elevation, and shape of a bottom sheet.
final BottomSheetThemeData bottomSheetTheme;
/// Creates a copy of this theme but with the given fields replaced with the new values. /// Creates a copy of this theme but with the given fields replaced with the new values.
ThemeData copyWith({ ThemeData copyWith({
Brightness brightness, Brightness brightness,
...@@ -753,6 +762,7 @@ class ThemeData extends Diagnosticable { ...@@ -753,6 +762,7 @@ class ThemeData extends Diagnosticable {
Typography typography, Typography typography,
CupertinoThemeData cupertinoOverrideTheme, CupertinoThemeData cupertinoOverrideTheme,
SnackBarThemeData snackBarTheme, SnackBarThemeData snackBarTheme,
BottomSheetThemeData bottomSheetTheme,
}) { }) {
cupertinoOverrideTheme = cupertinoOverrideTheme?.noDefault(); cupertinoOverrideTheme = cupertinoOverrideTheme?.noDefault();
return ThemeData.raw( return ThemeData.raw(
...@@ -808,6 +818,7 @@ class ThemeData extends Diagnosticable { ...@@ -808,6 +818,7 @@ class ThemeData extends Diagnosticable {
typography: typography ?? this.typography, typography: typography ?? this.typography,
cupertinoOverrideTheme: cupertinoOverrideTheme ?? this.cupertinoOverrideTheme, cupertinoOverrideTheme: cupertinoOverrideTheme ?? this.cupertinoOverrideTheme,
snackBarTheme: snackBarTheme ?? this.snackBarTheme, snackBarTheme: snackBarTheme ?? this.snackBarTheme,
bottomSheetTheme: bottomSheetTheme ?? this.bottomSheetTheme,
); );
} }
...@@ -941,6 +952,7 @@ class ThemeData extends Diagnosticable { ...@@ -941,6 +952,7 @@ class ThemeData extends Diagnosticable {
typography: Typography.lerp(a.typography, b.typography, t), typography: Typography.lerp(a.typography, b.typography, t),
cupertinoOverrideTheme: t < 0.5 ? a.cupertinoOverrideTheme : b.cupertinoOverrideTheme, cupertinoOverrideTheme: t < 0.5 ? a.cupertinoOverrideTheme : b.cupertinoOverrideTheme,
snackBarTheme: SnackBarThemeData.lerp(a.snackBarTheme, b.snackBarTheme, t), snackBarTheme: SnackBarThemeData.lerp(a.snackBarTheme, b.snackBarTheme, t),
bottomSheetTheme: BottomSheetThemeData.lerp(a.bottomSheetTheme, b.bottomSheetTheme, t),
); );
} }
...@@ -1003,7 +1015,8 @@ class ThemeData extends Diagnosticable { ...@@ -1003,7 +1015,8 @@ class ThemeData extends Diagnosticable {
(otherData.floatingActionButtonTheme == floatingActionButtonTheme) && (otherData.floatingActionButtonTheme == floatingActionButtonTheme) &&
(otherData.typography == typography) && (otherData.typography == typography) &&
(otherData.cupertinoOverrideTheme == cupertinoOverrideTheme) && (otherData.cupertinoOverrideTheme == cupertinoOverrideTheme) &&
(otherData.snackBarTheme == snackBarTheme); (otherData.snackBarTheme == snackBarTheme) &&
(otherData.bottomSheetTheme == bottomSheetTheme);
} }
@override @override
...@@ -1067,6 +1080,7 @@ class ThemeData extends Diagnosticable { ...@@ -1067,6 +1080,7 @@ class ThemeData extends Diagnosticable {
typography, typography,
cupertinoOverrideTheme, cupertinoOverrideTheme,
snackBarTheme, snackBarTheme,
bottomSheetTheme,
), ),
), ),
); );
...@@ -1125,6 +1139,7 @@ class ThemeData extends Diagnosticable { ...@@ -1125,6 +1139,7 @@ class ThemeData extends Diagnosticable {
properties.add(DiagnosticsProperty<Typography>('typography', typography, defaultValue: defaultData.typography)); properties.add(DiagnosticsProperty<Typography>('typography', typography, defaultValue: defaultData.typography));
properties.add(DiagnosticsProperty<CupertinoThemeData>('cupertinoOverrideTheme', cupertinoOverrideTheme, defaultValue: defaultData.cupertinoOverrideTheme)); properties.add(DiagnosticsProperty<CupertinoThemeData>('cupertinoOverrideTheme', cupertinoOverrideTheme, defaultValue: defaultData.cupertinoOverrideTheme));
properties.add(DiagnosticsProperty<SnackBarThemeData>('snackBarTheme', snackBarTheme, defaultValue: defaultData.snackBarTheme)); properties.add(DiagnosticsProperty<SnackBarThemeData>('snackBarTheme', snackBarTheme, defaultValue: defaultData.snackBarTheme));
properties.add(DiagnosticsProperty<BottomSheetThemeData>('bottomSheetTheme', bottomSheetTheme, defaultValue: defaultData.bottomSheetTheme));
} }
} }
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
test('BottomSheetThemeData copyWith, ==, hashCode basics', () {
expect(const BottomSheetThemeData(), const BottomSheetThemeData().copyWith());
expect(const BottomSheetThemeData().hashCode, const BottomSheetThemeData().copyWith().hashCode);
});
test('BottomSheetThemeData null fields by default', () {
const BottomSheetThemeData bottomSheetTheme = BottomSheetThemeData();
expect(bottomSheetTheme.backgroundColor, null);
expect(bottomSheetTheme.elevation, null);
expect(bottomSheetTheme.shape, null);
});
testWidgets('Default BottomSheetThemeData debugFillProperties', (WidgetTester tester) async {
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
const BottomSheetThemeData().debugFillProperties(builder);
final List<String> description = builder.properties
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
.map((DiagnosticsNode node) => node.toString())
.toList();
expect(description, <String>[]);
});
testWidgets('BottomSheetThemeData implements debugFillProperties', (WidgetTester tester) async {
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
BottomSheetThemeData(
backgroundColor: const Color(0xFFFFFFFF),
elevation: 2.0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(2.0)),
).debugFillProperties(builder);
final List<String> description = builder.properties
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
.map((DiagnosticsNode node) => node.toString())
.toList();
expect(description, <String>[
'backgroundColor: Color(0xffffffff)',
'elevation: 2.0',
'shape: RoundedRectangleBorder(BorderSide(Color(0xff000000), 0.0, BorderStyle.none), BorderRadius.circular(2.0))',
]);
});
testWidgets('Passing no BottomSheetThemeData returns defaults', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: BottomSheet(
onClosing: () {},
builder: (BuildContext context) {
return Container();
},
),
),
));
final Material material = tester.widget<Material>(
find.descendant(
of: find.byType(BottomSheet),
matching: find.byType(Material),
).first,
);
expect(material.color, null);
expect(material.elevation, 0.0);
expect(material.shape, null);
});
testWidgets('BottomSheet uses values from BottomSheetThemeData', (WidgetTester tester) async {
final BottomSheetThemeData bottomSheetTheme = _bottomSheetTheme();
await tester.pumpWidget(MaterialApp(
theme: ThemeData(bottomSheetTheme: bottomSheetTheme),
home: Scaffold(
body: BottomSheet(
onClosing: () {},
builder: (BuildContext context) {
return Container();
},
),
),
));
final Material material = tester.widget<Material>(
find.descendant(
of: find.byType(BottomSheet),
matching: find.byType(Material),
).first,
);
expect(material.color, bottomSheetTheme.backgroundColor);
expect(material.elevation, bottomSheetTheme.elevation);
expect(material.shape, bottomSheetTheme.shape);
});
testWidgets('BottomSheet widget properties take priority over theme', (WidgetTester tester) async {
const Color backgroundColor = Colors.purple;
const double elevation = 7.0;
const ShapeBorder shape = RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(9.0)),
);
await tester.pumpWidget(MaterialApp(
theme: ThemeData(bottomSheetTheme: _bottomSheetTheme()),
home: Scaffold(
body: BottomSheet(
backgroundColor: backgroundColor,
elevation: elevation,
shape: shape,
onClosing: () {},
builder: (BuildContext context) {
return Container();
},
),
),
));
final Material material = tester.widget<Material>(
find.descendant(
of: find.byType(BottomSheet),
matching: find.byType(Material),
).first,
);
expect(material.color, backgroundColor);
expect(material.elevation, elevation);
expect(material.shape, shape);
});
}
BottomSheetThemeData _bottomSheetTheme() {
return BottomSheetThemeData(
backgroundColor: Colors.orange,
elevation: 12.0,
shape: BeveledRectangleBorder(borderRadius: BorderRadius.circular(12)),
);
}
...@@ -251,6 +251,40 @@ void main() { ...@@ -251,6 +251,40 @@ void main() {
semantics.dispose(); semantics.dispose();
}); });
testWidgets('Verify that visual properties are passed through', (WidgetTester tester) async {
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
const Color color = Colors.pink;
const double elevation = 9.0;
final ShapeBorder shape = BeveledRectangleBorder(borderRadius: BorderRadius.circular(12));
await tester.pumpWidget(MaterialApp(
home: Scaffold(
key: scaffoldKey,
body: const Center(child: Text('body')),
),
));
showModalBottomSheet<void>(
context: scaffoldKey.currentContext,
backgroundColor: color,
elevation: elevation,
shape: shape,
builder: (BuildContext context) {
return Container(
child: const Text('BottomSheet'),
);
},
);
await tester.pump();
await tester.pump(const Duration(seconds: 1));
final BottomSheet bottomSheet = tester.widget(find.byType(BottomSheet));
expect(bottomSheet.backgroundColor, color);
expect(bottomSheet.elevation, elevation);
expect(bottomSheet.shape, shape);
});
testWidgets('modal BottomSheet with scrollController has semantics', (WidgetTester tester) async { testWidgets('modal BottomSheet with scrollController has semantics', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester); final SemanticsTester semantics = SemanticsTester(tester);
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>(); final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
......
...@@ -426,6 +426,39 @@ void main() { ...@@ -426,6 +426,39 @@ void main() {
expect(find.byKey(bottomSheetKey), findsNothing); expect(find.byKey(bottomSheetKey), findsNothing);
}); });
testWidgets('Verify that visual properties are passed through', (WidgetTester tester) async {
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
const Color color = Colors.pink;
const double elevation = 9.0;
final ShapeBorder shape = BeveledRectangleBorder(borderRadius: BorderRadius.circular(12));
await tester.pumpWidget(MaterialApp(
home: Scaffold(
key: scaffoldKey,
body: const Center(child: Text('body')),
),
));
scaffoldKey.currentState.showBottomSheet<void>((BuildContext context) {
return ListView(
shrinkWrap: true,
primary: false,
children: <Widget>[
Container(height: 100.0, child: const Text('One')),
Container(height: 100.0, child: const Text('Two')),
Container(height: 100.0, child: const Text('Three')),
],
);
}, backgroundColor: color, elevation: elevation, shape: shape);
await tester.pumpAndSettle();
final BottomSheet bottomSheet = tester.widget(find.byType(BottomSheet));
expect(bottomSheet.backgroundColor, color);
expect(bottomSheet.elevation, elevation);
expect(bottomSheet.shape, shape);
});
testWidgets('PersistentBottomSheetController.close dismisses the bottom sheet', (WidgetTester tester) async { testWidgets('PersistentBottomSheetController.close dismisses the bottom sheet', (WidgetTester tester) async {
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey(); final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey();
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment