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
This commit is contained in:
parent
60a1b2b9ea
commit
6a1468db16
@ -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';
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
/// 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 {
|
|||||||
/// 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> {
|
|||||||
|
|
||||||
@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 {
|
|||||||
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>> {
|
|||||||
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>> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
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> {
|
|||||||
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> {
|
|||||||
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> {
|
|||||||
|
|
||||||
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> {
|
|||||||
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> {
|
|||||||
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>({
|
|||||||
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>({
|
|||||||
@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>({
|
|||||||
return Scaffold.of(context).showBottomSheet<T>(
|
return Scaffold.of(context).showBottomSheet<T>(
|
||||||
builder,
|
builder,
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
|
elevation: elevation,
|
||||||
|
shape: shape,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
111
packages/flutter/lib/src/material/bottom_sheet_theme.dart
Normal file
111
packages/flutter/lib/src/material/bottom_sheet_theme.dart
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
// 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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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> {
|
|||||||
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';
|
|||||||
|
|
||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
typography: typography,
|
typography: typography,
|
||||||
cupertinoOverrideTheme: cupertinoOverrideTheme,
|
cupertinoOverrideTheme: cupertinoOverrideTheme,
|
||||||
snackBarTheme: snackBarTheme,
|
snackBarTheme: snackBarTheme,
|
||||||
|
bottomSheetTheme: bottomSheetTheme,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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 {
|
|||||||
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 {
|
|||||||
/// 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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
(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 {
|
|||||||
typography,
|
typography,
|
||||||
cupertinoOverrideTheme,
|
cupertinoOverrideTheme,
|
||||||
snackBarTheme,
|
snackBarTheme,
|
||||||
|
bottomSheetTheme,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
143
packages/flutter/test/material/bottom_sheet_theme_test.dart
Normal file
143
packages/flutter/test/material/bottom_sheet_theme_test.dart
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
// 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() {
|
|||||||
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() {
|
|||||||
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(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user