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_navigation_bar.dart';
|
||||
export 'src/material/bottom_sheet.dart';
|
||||
export 'src/material/bottom_sheet_theme.dart';
|
||||
export 'src/material/button.dart';
|
||||
export 'src/material/button_bar.dart';
|
||||
export 'src/material/button_theme.dart';
|
||||
|
@ -8,6 +8,7 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'bottom_sheet_theme.dart';
|
||||
import 'colors.dart';
|
||||
import 'debug.dart';
|
||||
import 'material.dart';
|
||||
@ -56,14 +57,15 @@ class BottomSheet extends StatefulWidget {
|
||||
Key key,
|
||||
this.animationController,
|
||||
this.enableDrag = true,
|
||||
this.elevation = 0.0,
|
||||
this.backgroundColor,
|
||||
this.elevation,
|
||||
this.shape,
|
||||
@required this.onClosing,
|
||||
@required this.builder,
|
||||
}) : assert(enableDrag != null),
|
||||
assert(onClosing != null),
|
||||
assert(builder != null),
|
||||
assert(elevation != null && elevation >= 0.0),
|
||||
assert(elevation == null || elevation >= 0.0),
|
||||
super(key: key);
|
||||
|
||||
/// The animation controller that controls the bottom sheet's entrance and
|
||||
@ -92,6 +94,13 @@ class BottomSheet extends StatefulWidget {
|
||||
/// Default is true.
|
||||
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.
|
||||
///
|
||||
/// 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.
|
||||
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.
|
||||
final Color backgroundColor;
|
||||
/// Defines the bottom sheet's [Material.shape].
|
||||
///
|
||||
/// Defaults to null and falls back to [Material]'s default.
|
||||
final ShapeBorder shape;
|
||||
|
||||
@override
|
||||
_BottomSheetState createState() => _BottomSheetState();
|
||||
@ -170,10 +181,16 @@ class _BottomSheetState extends State<BottomSheet> {
|
||||
|
||||
@override
|
||||
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(
|
||||
key: _childKey,
|
||||
color: widget.backgroundColor,
|
||||
elevation: widget.elevation,
|
||||
color: color,
|
||||
elevation: elevation,
|
||||
shape: shape,
|
||||
child: NotificationListener<DraggableScrollableNotification>(
|
||||
onNotification: extentChanged,
|
||||
child: widget.builder(context),
|
||||
@ -227,12 +244,18 @@ class _ModalBottomSheet<T> extends StatefulWidget {
|
||||
const _ModalBottomSheet({
|
||||
Key key,
|
||||
this.route,
|
||||
this.backgroundColor,
|
||||
this.elevation,
|
||||
this.shape,
|
||||
this.isScrollControlled = false,
|
||||
}) : assert(isScrollControlled != null),
|
||||
super(key: key);
|
||||
|
||||
final _ModalBottomSheetRoute<T> route;
|
||||
final bool isScrollControlled;
|
||||
final Color backgroundColor;
|
||||
final double elevation;
|
||||
final ShapeBorder shape;
|
||||
|
||||
@override
|
||||
_ModalBottomSheetState<T> createState() => _ModalBottomSheetState<T>();
|
||||
@ -279,7 +302,6 @@ class _ModalBottomSheetState<T> extends State<_ModalBottomSheet<T>> {
|
||||
child: CustomSingleChildLayout(
|
||||
delegate: _ModalBottomSheetLayout(animationValue, widget.isScrollControlled),
|
||||
child: BottomSheet(
|
||||
backgroundColor: widget.route.backgroundColor,
|
||||
animationController: widget.route._animationController,
|
||||
onClosing: () {
|
||||
if (widget.route.isCurrent) {
|
||||
@ -287,6 +309,9 @@ class _ModalBottomSheetState<T> extends State<_ModalBottomSheet<T>> {
|
||||
}
|
||||
},
|
||||
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.theme,
|
||||
this.barrierLabel,
|
||||
@required this.isScrollControlled,
|
||||
this.backgroundColor,
|
||||
this.elevation,
|
||||
this.shape,
|
||||
@required this.isScrollControlled,
|
||||
RouteSettings settings,
|
||||
}) : assert(isScrollControlled != null),
|
||||
super(settings: settings);
|
||||
@ -312,6 +339,8 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> {
|
||||
final ThemeData theme;
|
||||
final bool isScrollControlled;
|
||||
final Color backgroundColor;
|
||||
final double elevation;
|
||||
final ShapeBorder shape;
|
||||
|
||||
@override
|
||||
Duration get transitionDuration => _bottomSheetDuration;
|
||||
@ -327,6 +356,7 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> {
|
||||
|
||||
AnimationController _animationController;
|
||||
|
||||
|
||||
@override
|
||||
AnimationController createAnimationController() {
|
||||
assert(_animationController == null);
|
||||
@ -341,7 +371,13 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> {
|
||||
Widget bottomSheet = MediaQuery.removePadding(
|
||||
context: context,
|
||||
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)
|
||||
bottomSheet = Theme(data: theme, child: bottomSheet);
|
||||
@ -384,8 +420,10 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> {
|
||||
Future<T> showModalBottomSheet<T>({
|
||||
@required BuildContext context,
|
||||
@required WidgetBuilder builder,
|
||||
bool isScrollControlled = false,
|
||||
Color backgroundColor,
|
||||
double elevation,
|
||||
ShapeBorder shape,
|
||||
bool isScrollControlled = false,
|
||||
}) {
|
||||
assert(context != null);
|
||||
assert(builder != null);
|
||||
@ -397,8 +435,10 @@ Future<T> showModalBottomSheet<T>({
|
||||
builder: builder,
|
||||
theme: Theme.of(context, shadowThemeOnly: true),
|
||||
isScrollControlled: isScrollControlled,
|
||||
backgroundColor: backgroundColor,
|
||||
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
|
||||
backgroundColor: backgroundColor,
|
||||
elevation: elevation,
|
||||
shape: shape,
|
||||
));
|
||||
}
|
||||
|
||||
@ -440,6 +480,8 @@ PersistentBottomSheetController<T> showBottomSheet<T>({
|
||||
@required BuildContext context,
|
||||
@required WidgetBuilder builder,
|
||||
Color backgroundColor,
|
||||
double elevation,
|
||||
ShapeBorder shape,
|
||||
}) {
|
||||
assert(context != null);
|
||||
assert(builder != null);
|
||||
@ -448,5 +490,7 @@ PersistentBottomSheetController<T> showBottomSheet<T>({
|
||||
return Scaffold.of(context).showBottomSheet<T>(
|
||||
builder,
|
||||
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, {
|
||||
AnimationController animationController,
|
||||
Color backgroundColor,
|
||||
double elevation,
|
||||
ShapeBorder shape,
|
||||
}) {
|
||||
assert(() {
|
||||
if (widget.bottomSheet != null && isPersistent && _currentBottomSheet != null) {
|
||||
@ -1619,6 +1621,8 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
|
||||
builder: builder,
|
||||
isPersistent: isPersistent,
|
||||
backgroundColor: backgroundColor,
|
||||
elevation: elevation,
|
||||
shape: shape,
|
||||
);
|
||||
|
||||
if (!isPersistent)
|
||||
@ -1673,6 +1677,8 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
|
||||
PersistentBottomSheetController<T> showBottomSheet<T>(
|
||||
WidgetBuilder builder, {
|
||||
Color backgroundColor,
|
||||
double elevation,
|
||||
ShapeBorder shape,
|
||||
}) {
|
||||
assert(() {
|
||||
if (widget.bottomSheet != null) {
|
||||
@ -1694,6 +1700,8 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
|
||||
false,
|
||||
animationController: controller,
|
||||
backgroundColor: backgroundColor,
|
||||
elevation: elevation,
|
||||
shape: shape,
|
||||
);
|
||||
});
|
||||
return _currentBottomSheet;
|
||||
@ -2216,6 +2224,8 @@ class _StandardBottomSheet extends StatefulWidget {
|
||||
this.builder,
|
||||
this.isPersistent = false,
|
||||
this.backgroundColor,
|
||||
this.elevation,
|
||||
this.shape,
|
||||
}) : super(key: key);
|
||||
|
||||
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 bool isPersistent;
|
||||
final Color backgroundColor;
|
||||
final double elevation;
|
||||
final ShapeBorder shape;
|
||||
|
||||
@override
|
||||
_StandardBottomSheetState createState() => _StandardBottomSheetState();
|
||||
@ -2311,6 +2323,8 @@ class _StandardBottomSheetState extends State<_StandardBottomSheet> {
|
||||
onClosing: widget.onClosing,
|
||||
builder: widget.builder,
|
||||
backgroundColor: widget.backgroundColor,
|
||||
elevation: widget.elevation,
|
||||
shape: widget.shape,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -11,6 +11,7 @@ import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'app_bar_theme.dart';
|
||||
import 'bottom_app_bar_theme.dart';
|
||||
import 'bottom_sheet_theme.dart';
|
||||
import 'button_theme.dart';
|
||||
import 'card_theme.dart';
|
||||
import 'chip_theme.dart';
|
||||
@ -165,6 +166,7 @@ class ThemeData extends Diagnosticable {
|
||||
Typography typography,
|
||||
CupertinoThemeData cupertinoOverrideTheme,
|
||||
SnackBarThemeData snackBarTheme,
|
||||
BottomSheetThemeData bottomSheetTheme,
|
||||
}) {
|
||||
brightness ??= Brightness.light;
|
||||
final bool isDark = brightness == Brightness.dark;
|
||||
@ -259,6 +261,7 @@ class ThemeData extends Diagnosticable {
|
||||
floatingActionButtonTheme ??= const FloatingActionButtonThemeData();
|
||||
cupertinoOverrideTheme = cupertinoOverrideTheme?.noDefault();
|
||||
snackBarTheme ??= const SnackBarThemeData();
|
||||
bottomSheetTheme ??= const BottomSheetThemeData();
|
||||
|
||||
return ThemeData.raw(
|
||||
brightness: brightness,
|
||||
@ -313,6 +316,7 @@ class ThemeData extends Diagnosticable {
|
||||
typography: typography,
|
||||
cupertinoOverrideTheme: cupertinoOverrideTheme,
|
||||
snackBarTheme: snackBarTheme,
|
||||
bottomSheetTheme: bottomSheetTheme,
|
||||
);
|
||||
}
|
||||
|
||||
@ -379,6 +383,7 @@ class ThemeData extends Diagnosticable {
|
||||
@required this.typography,
|
||||
@required this.cupertinoOverrideTheme,
|
||||
@required this.snackBarTheme,
|
||||
@required this.bottomSheetTheme,
|
||||
}) : assert(brightness != null),
|
||||
assert(primaryColor != null),
|
||||
assert(primaryColorBrightness != null),
|
||||
@ -428,7 +433,8 @@ class ThemeData extends Diagnosticable {
|
||||
assert(dialogTheme != null),
|
||||
assert(floatingActionButtonTheme != 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
|
||||
// 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].
|
||||
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.
|
||||
ThemeData copyWith({
|
||||
Brightness brightness,
|
||||
@ -753,6 +762,7 @@ class ThemeData extends Diagnosticable {
|
||||
Typography typography,
|
||||
CupertinoThemeData cupertinoOverrideTheme,
|
||||
SnackBarThemeData snackBarTheme,
|
||||
BottomSheetThemeData bottomSheetTheme,
|
||||
}) {
|
||||
cupertinoOverrideTheme = cupertinoOverrideTheme?.noDefault();
|
||||
return ThemeData.raw(
|
||||
@ -808,6 +818,7 @@ class ThemeData extends Diagnosticable {
|
||||
typography: typography ?? this.typography,
|
||||
cupertinoOverrideTheme: cupertinoOverrideTheme ?? this.cupertinoOverrideTheme,
|
||||
snackBarTheme: snackBarTheme ?? this.snackBarTheme,
|
||||
bottomSheetTheme: bottomSheetTheme ?? this.bottomSheetTheme,
|
||||
);
|
||||
}
|
||||
|
||||
@ -941,6 +952,7 @@ class ThemeData extends Diagnosticable {
|
||||
typography: Typography.lerp(a.typography, b.typography, t),
|
||||
cupertinoOverrideTheme: t < 0.5 ? a.cupertinoOverrideTheme : b.cupertinoOverrideTheme,
|
||||
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.typography == typography) &&
|
||||
(otherData.cupertinoOverrideTheme == cupertinoOverrideTheme) &&
|
||||
(otherData.snackBarTheme == snackBarTheme);
|
||||
(otherData.snackBarTheme == snackBarTheme) &&
|
||||
(otherData.bottomSheetTheme == bottomSheetTheme);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -1067,6 +1080,7 @@ class ThemeData extends Diagnosticable {
|
||||
typography,
|
||||
cupertinoOverrideTheme,
|
||||
snackBarTheme,
|
||||
bottomSheetTheme,
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -1125,6 +1139,7 @@ class ThemeData extends Diagnosticable {
|
||||
properties.add(DiagnosticsProperty<Typography>('typography', typography, defaultValue: defaultData.typography));
|
||||
properties.add(DiagnosticsProperty<CupertinoThemeData>('cupertinoOverrideTheme', cupertinoOverrideTheme, defaultValue: defaultData.cupertinoOverrideTheme));
|
||||
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();
|
||||
});
|
||||
|
||||
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 {
|
||||
final SemanticsTester semantics = SemanticsTester(tester);
|
||||
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
|
@ -426,6 +426,39 @@ void main() {
|
||||
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 {
|
||||
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey();
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
|
Loading…
x
Reference in New Issue
Block a user