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

[Material] Add clip property to bottom sheet and theme (#38831)

parent 36e8b93d
...@@ -60,6 +60,7 @@ class BottomSheet extends StatefulWidget { ...@@ -60,6 +60,7 @@ class BottomSheet extends StatefulWidget {
this.backgroundColor, this.backgroundColor,
this.elevation, this.elevation,
this.shape, this.shape,
this.clipBehavior,
@required this.onClosing, @required this.onClosing,
@required this.builder, @required this.builder,
}) : assert(enableDrag != null), }) : assert(enableDrag != null),
...@@ -115,6 +116,19 @@ class BottomSheet extends StatefulWidget { ...@@ -115,6 +116,19 @@ class BottomSheet extends StatefulWidget {
/// Defaults to null and falls back to [Material]'s default. /// Defaults to null and falls back to [Material]'s default.
final ShapeBorder shape; final ShapeBorder shape;
/// {@macro flutter.widgets.Clip}
///
/// Defines the bottom sheet's [Material.clipBehavior].
///
/// Use this property to enable clipping of content when the bottom sheet has
/// a custom [shape] and the content can extend past this shape. For example,
/// a bottom sheet with rounded corners and an edge-to-edge [Image] at the
/// top.
///
/// If this property is null then [ThemeData.bottomSheetTheme.clipBehavior] is
/// used. If that's null then the behavior will be [Clip.none].
final Clip clipBehavior;
@override @override
_BottomSheetState createState() => _BottomSheetState(); _BottomSheetState createState() => _BottomSheetState();
...@@ -185,12 +199,14 @@ class _BottomSheetState extends State<BottomSheet> { ...@@ -185,12 +199,14 @@ class _BottomSheetState extends State<BottomSheet> {
final Color color = widget.backgroundColor ?? bottomSheetTheme.backgroundColor; final Color color = widget.backgroundColor ?? bottomSheetTheme.backgroundColor;
final double elevation = widget.elevation ?? bottomSheetTheme.elevation ?? 0; final double elevation = widget.elevation ?? bottomSheetTheme.elevation ?? 0;
final ShapeBorder shape = widget.shape ?? bottomSheetTheme.shape; final ShapeBorder shape = widget.shape ?? bottomSheetTheme.shape;
final Clip clipBehavior = widget.clipBehavior ?? bottomSheetTheme.clipBehavior ?? Clip.none;
final Widget bottomSheet = Material( final Widget bottomSheet = Material(
key: _childKey, key: _childKey,
color: color, color: color,
elevation: elevation, elevation: elevation,
shape: shape, shape: shape,
clipBehavior: clipBehavior,
child: NotificationListener<DraggableScrollableNotification>( child: NotificationListener<DraggableScrollableNotification>(
onNotification: extentChanged, onNotification: extentChanged,
child: widget.builder(context), child: widget.builder(context),
...@@ -247,6 +263,7 @@ class _ModalBottomSheet<T> extends StatefulWidget { ...@@ -247,6 +263,7 @@ class _ModalBottomSheet<T> extends StatefulWidget {
this.backgroundColor, this.backgroundColor,
this.elevation, this.elevation,
this.shape, this.shape,
this.clipBehavior,
this.isScrollControlled = false, this.isScrollControlled = false,
}) : assert(isScrollControlled != null), }) : assert(isScrollControlled != null),
super(key: key); super(key: key);
...@@ -256,6 +273,7 @@ class _ModalBottomSheet<T> extends StatefulWidget { ...@@ -256,6 +273,7 @@ class _ModalBottomSheet<T> extends StatefulWidget {
final Color backgroundColor; final Color backgroundColor;
final double elevation; final double elevation;
final ShapeBorder shape; final ShapeBorder shape;
final Clip clipBehavior;
@override @override
_ModalBottomSheetState<T> createState() => _ModalBottomSheetState<T>(); _ModalBottomSheetState<T> createState() => _ModalBottomSheetState<T>();
...@@ -306,6 +324,7 @@ class _ModalBottomSheetState<T> extends State<_ModalBottomSheet<T>> { ...@@ -306,6 +324,7 @@ class _ModalBottomSheetState<T> extends State<_ModalBottomSheet<T>> {
backgroundColor: widget.backgroundColor, backgroundColor: widget.backgroundColor,
elevation: widget.elevation, elevation: widget.elevation,
shape: widget.shape, shape: widget.shape,
clipBehavior: widget.clipBehavior,
), ),
), ),
), ),
...@@ -323,6 +342,7 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> { ...@@ -323,6 +342,7 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> {
this.backgroundColor, this.backgroundColor,
this.elevation, this.elevation,
this.shape, this.shape,
this.clipBehavior,
@required this.isScrollControlled, @required this.isScrollControlled,
RouteSettings settings, RouteSettings settings,
}) : assert(isScrollControlled != null), }) : assert(isScrollControlled != null),
...@@ -334,6 +354,7 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> { ...@@ -334,6 +354,7 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> {
final Color backgroundColor; final Color backgroundColor;
final double elevation; final double elevation;
final ShapeBorder shape; final ShapeBorder shape;
final Clip clipBehavior;
@override @override
Duration get transitionDuration => _bottomSheetDuration; Duration get transitionDuration => _bottomSheetDuration;
...@@ -369,6 +390,7 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> { ...@@ -369,6 +390,7 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> {
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
elevation: elevation, elevation: elevation,
shape: shape, shape: shape,
clipBehavior: clipBehavior,
isScrollControlled: isScrollControlled isScrollControlled: isScrollControlled
), ),
); );
...@@ -423,6 +445,7 @@ Future<T> showModalBottomSheet<T>({ ...@@ -423,6 +445,7 @@ Future<T> showModalBottomSheet<T>({
Color backgroundColor, Color backgroundColor,
double elevation, double elevation,
ShapeBorder shape, ShapeBorder shape,
Clip clipBehavior,
bool isScrollControlled = false, bool isScrollControlled = false,
bool useRootNavigator = false, bool useRootNavigator = false,
}) { }) {
...@@ -441,6 +464,7 @@ Future<T> showModalBottomSheet<T>({ ...@@ -441,6 +464,7 @@ Future<T> showModalBottomSheet<T>({
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
elevation: elevation, elevation: elevation,
shape: shape, shape: shape,
clipBehavior: clipBehavior,
)); ));
} }
...@@ -485,6 +509,7 @@ PersistentBottomSheetController<T> showBottomSheet<T>({ ...@@ -485,6 +509,7 @@ PersistentBottomSheetController<T> showBottomSheet<T>({
Color backgroundColor, Color backgroundColor,
double elevation, double elevation,
ShapeBorder shape, ShapeBorder shape,
Clip clipBehavior,
}) { }) {
assert(context != null); assert(context != null);
assert(builder != null); assert(builder != null);
...@@ -495,5 +520,6 @@ PersistentBottomSheetController<T> showBottomSheet<T>({ ...@@ -495,5 +520,6 @@ PersistentBottomSheetController<T> showBottomSheet<T>({
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
elevation: elevation, elevation: elevation,
shape: shape, shape: shape,
clipBehavior: clipBehavior,
); );
} }
...@@ -30,6 +30,7 @@ class BottomSheetThemeData extends Diagnosticable { ...@@ -30,6 +30,7 @@ class BottomSheetThemeData extends Diagnosticable {
this.backgroundColor, this.backgroundColor,
this.elevation, this.elevation,
this.shape, this.shape,
this.clipBehavior,
}); });
/// Default value for [BottomSheet.backgroundColor]. /// Default value for [BottomSheet.backgroundColor].
...@@ -50,17 +51,24 @@ class BottomSheetThemeData extends Diagnosticable { ...@@ -50,17 +51,24 @@ class BottomSheetThemeData extends Diagnosticable {
/// [BottomSheet] is rectangular. /// [BottomSheet] is rectangular.
final ShapeBorder shape; final ShapeBorder shape;
/// Default value for [BottomSheet.clipBehavior].
///
/// If null, [BottomSheet] uses [Clip.none].
final Clip clipBehavior;
/// Creates a copy of this object with the given fields replaced with the /// Creates a copy of this object with the given fields replaced with the
/// new values. /// new values.
BottomSheetThemeData copyWith({ BottomSheetThemeData copyWith({
Color backgroundColor, Color backgroundColor,
double elevation, double elevation,
ShapeBorder shape, ShapeBorder shape,
Clip clipBehavior,
}) { }) {
return BottomSheetThemeData( return BottomSheetThemeData(
backgroundColor: backgroundColor ?? this.backgroundColor, backgroundColor: backgroundColor ?? this.backgroundColor,
elevation: elevation ?? this.elevation, elevation: elevation ?? this.elevation,
shape: shape ?? this.shape, shape: shape ?? this.shape,
clipBehavior: clipBehavior ?? this.clipBehavior,
); );
} }
...@@ -77,6 +85,7 @@ class BottomSheetThemeData extends Diagnosticable { ...@@ -77,6 +85,7 @@ class BottomSheetThemeData extends Diagnosticable {
backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t), backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t),
elevation: lerpDouble(a?.elevation, b?.elevation, t), elevation: lerpDouble(a?.elevation, b?.elevation, t),
shape: ShapeBorder.lerp(a?.shape, b?.shape, t), shape: ShapeBorder.lerp(a?.shape, b?.shape, t),
clipBehavior: t < 0.5 ? a?.clipBehavior : b?.clipBehavior,
); );
} }
...@@ -86,6 +95,7 @@ class BottomSheetThemeData extends Diagnosticable { ...@@ -86,6 +95,7 @@ class BottomSheetThemeData extends Diagnosticable {
backgroundColor, backgroundColor,
elevation, elevation,
shape, shape,
clipBehavior,
); );
} }
...@@ -98,7 +108,8 @@ class BottomSheetThemeData extends Diagnosticable { ...@@ -98,7 +108,8 @@ class BottomSheetThemeData extends Diagnosticable {
final BottomSheetThemeData typedOther = other; final BottomSheetThemeData typedOther = other;
return typedOther.backgroundColor == backgroundColor return typedOther.backgroundColor == backgroundColor
&& typedOther.elevation == elevation && typedOther.elevation == elevation
&& typedOther.shape == shape; && typedOther.shape == shape
&& typedOther.clipBehavior == clipBehavior;
} }
@override @override
...@@ -107,5 +118,6 @@ class BottomSheetThemeData extends Diagnosticable { ...@@ -107,5 +118,6 @@ class BottomSheetThemeData extends Diagnosticable {
properties.add(ColorProperty('backgroundColor', backgroundColor, defaultValue: null)); properties.add(ColorProperty('backgroundColor', backgroundColor, defaultValue: null));
properties.add(DoubleProperty('elevation', elevation, defaultValue: null)); properties.add(DoubleProperty('elevation', elevation, defaultValue: null));
properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: null)); properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: null));
properties.add(DiagnosticsProperty<Clip>('clipBehavior', clipBehavior, defaultValue: null));
} }
} }
...@@ -1622,6 +1622,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin { ...@@ -1622,6 +1622,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
Color backgroundColor, Color backgroundColor,
double elevation, double elevation,
ShapeBorder shape, ShapeBorder shape,
Clip clipBehavior,
}) { }) {
assert(() { assert(() {
if (widget.bottomSheet != null && isPersistent && _currentBottomSheet != null) { if (widget.bottomSheet != null && isPersistent && _currentBottomSheet != null) {
...@@ -1702,6 +1703,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin { ...@@ -1702,6 +1703,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
elevation: elevation, elevation: elevation,
shape: shape, shape: shape,
clipBehavior: clipBehavior,
); );
if (!isPersistent) if (!isPersistent)
...@@ -1759,6 +1761,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin { ...@@ -1759,6 +1761,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
Color backgroundColor, Color backgroundColor,
double elevation, double elevation,
ShapeBorder shape, ShapeBorder shape,
Clip clipBehavior,
}) { }) {
assert(() { assert(() {
if (widget.bottomSheet != null) { if (widget.bottomSheet != null) {
...@@ -1782,6 +1785,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin { ...@@ -1782,6 +1785,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
elevation: elevation, elevation: elevation,
shape: shape, shape: shape,
clipBehavior: clipBehavior,
); );
}); });
return _currentBottomSheet; return _currentBottomSheet;
...@@ -2313,6 +2317,7 @@ class _StandardBottomSheet extends StatefulWidget { ...@@ -2313,6 +2317,7 @@ class _StandardBottomSheet extends StatefulWidget {
this.backgroundColor, this.backgroundColor,
this.elevation, this.elevation,
this.shape, this.shape,
this.clipBehavior,
}) : 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.
...@@ -2324,6 +2329,7 @@ class _StandardBottomSheet extends StatefulWidget { ...@@ -2324,6 +2329,7 @@ class _StandardBottomSheet extends StatefulWidget {
final Color backgroundColor; final Color backgroundColor;
final double elevation; final double elevation;
final ShapeBorder shape; final ShapeBorder shape;
final Clip clipBehavior;
@override @override
_StandardBottomSheetState createState() => _StandardBottomSheetState(); _StandardBottomSheetState createState() => _StandardBottomSheetState();
...@@ -2412,6 +2418,7 @@ class _StandardBottomSheetState extends State<_StandardBottomSheet> { ...@@ -2412,6 +2418,7 @@ class _StandardBottomSheetState extends State<_StandardBottomSheet> {
backgroundColor: widget.backgroundColor, backgroundColor: widget.backgroundColor,
elevation: widget.elevation, elevation: widget.elevation,
shape: widget.shape, shape: widget.shape,
clipBehavior: widget.clipBehavior,
), ),
), ),
); );
......
...@@ -270,6 +270,7 @@ void main() { ...@@ -270,6 +270,7 @@ void main() {
const Color color = Colors.pink; const Color color = Colors.pink;
const double elevation = 9.0; const double elevation = 9.0;
final ShapeBorder shape = BeveledRectangleBorder(borderRadius: BorderRadius.circular(12)); final ShapeBorder shape = BeveledRectangleBorder(borderRadius: BorderRadius.circular(12));
const Clip clipBehavior = Clip.antiAlias;
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
home: Scaffold( home: Scaffold(
...@@ -283,6 +284,7 @@ void main() { ...@@ -283,6 +284,7 @@ void main() {
backgroundColor: color, backgroundColor: color,
elevation: elevation, elevation: elevation,
shape: shape, shape: shape,
clipBehavior: clipBehavior,
builder: (BuildContext context) { builder: (BuildContext context) {
return Container( return Container(
child: const Text('BottomSheet'), child: const Text('BottomSheet'),
...@@ -297,6 +299,7 @@ void main() { ...@@ -297,6 +299,7 @@ void main() {
expect(bottomSheet.backgroundColor, color); expect(bottomSheet.backgroundColor, color);
expect(bottomSheet.elevation, elevation); expect(bottomSheet.elevation, elevation);
expect(bottomSheet.shape, shape); expect(bottomSheet.shape, shape);
expect(bottomSheet.clipBehavior, clipBehavior);
}); });
testWidgets('modal BottomSheet with scrollController has semantics', (WidgetTester tester) async { testWidgets('modal BottomSheet with scrollController has semantics', (WidgetTester tester) async {
......
...@@ -17,6 +17,7 @@ void main() { ...@@ -17,6 +17,7 @@ void main() {
expect(bottomSheetTheme.backgroundColor, null); expect(bottomSheetTheme.backgroundColor, null);
expect(bottomSheetTheme.elevation, null); expect(bottomSheetTheme.elevation, null);
expect(bottomSheetTheme.shape, null); expect(bottomSheetTheme.shape, null);
expect(bottomSheetTheme.clipBehavior, null);
}); });
testWidgets('Default BottomSheetThemeData debugFillProperties', (WidgetTester tester) async { testWidgets('Default BottomSheetThemeData debugFillProperties', (WidgetTester tester) async {
...@@ -37,6 +38,7 @@ void main() { ...@@ -37,6 +38,7 @@ void main() {
backgroundColor: const Color(0xFFFFFFFF), backgroundColor: const Color(0xFFFFFFFF),
elevation: 2.0, elevation: 2.0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(2.0)), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(2.0)),
clipBehavior: Clip.antiAlias,
).debugFillProperties(builder); ).debugFillProperties(builder);
final List<String> description = builder.properties final List<String> description = builder.properties
...@@ -48,6 +50,7 @@ void main() { ...@@ -48,6 +50,7 @@ void main() {
'backgroundColor: Color(0xffffffff)', 'backgroundColor: Color(0xffffffff)',
'elevation: 2.0', 'elevation: 2.0',
'shape: RoundedRectangleBorder(BorderSide(Color(0xff000000), 0.0, BorderStyle.none), BorderRadius.circular(2.0))', 'shape: RoundedRectangleBorder(BorderSide(Color(0xff000000), 0.0, BorderStyle.none), BorderRadius.circular(2.0))',
'clipBehavior: Clip.antiAlias'
]); ]);
}); });
...@@ -72,6 +75,7 @@ void main() { ...@@ -72,6 +75,7 @@ void main() {
expect(material.color, null); expect(material.color, null);
expect(material.elevation, 0.0); expect(material.elevation, 0.0);
expect(material.shape, null); expect(material.shape, null);
expect(material.clipBehavior, Clip.none);
}); });
testWidgets('BottomSheet uses values from BottomSheetThemeData', (WidgetTester tester) async { testWidgets('BottomSheet uses values from BottomSheetThemeData', (WidgetTester tester) async {
...@@ -98,6 +102,7 @@ void main() { ...@@ -98,6 +102,7 @@ void main() {
expect(material.color, bottomSheetTheme.backgroundColor); expect(material.color, bottomSheetTheme.backgroundColor);
expect(material.elevation, bottomSheetTheme.elevation); expect(material.elevation, bottomSheetTheme.elevation);
expect(material.shape, bottomSheetTheme.shape); expect(material.shape, bottomSheetTheme.shape);
expect(material.clipBehavior, bottomSheetTheme.clipBehavior);
}); });
testWidgets('BottomSheet widget properties take priority over theme', (WidgetTester tester) async { testWidgets('BottomSheet widget properties take priority over theme', (WidgetTester tester) async {
...@@ -106,6 +111,7 @@ void main() { ...@@ -106,6 +111,7 @@ void main() {
const ShapeBorder shape = RoundedRectangleBorder( const ShapeBorder shape = RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(9.0)), borderRadius: BorderRadius.all(Radius.circular(9.0)),
); );
const Clip clipBehavior = Clip.hardEdge;
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
theme: ThemeData(bottomSheetTheme: _bottomSheetTheme()), theme: ThemeData(bottomSheetTheme: _bottomSheetTheme()),
...@@ -114,6 +120,7 @@ void main() { ...@@ -114,6 +120,7 @@ void main() {
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
elevation: elevation, elevation: elevation,
shape: shape, shape: shape,
clipBehavior: Clip.hardEdge,
onClosing: () {}, onClosing: () {},
builder: (BuildContext context) { builder: (BuildContext context) {
return Container(); return Container();
...@@ -131,6 +138,7 @@ void main() { ...@@ -131,6 +138,7 @@ void main() {
expect(material.color, backgroundColor); expect(material.color, backgroundColor);
expect(material.elevation, elevation); expect(material.elevation, elevation);
expect(material.shape, shape); expect(material.shape, shape);
expect(material.clipBehavior, clipBehavior);
}); });
} }
...@@ -139,5 +147,6 @@ BottomSheetThemeData _bottomSheetTheme() { ...@@ -139,5 +147,6 @@ BottomSheetThemeData _bottomSheetTheme() {
backgroundColor: Colors.orange, backgroundColor: Colors.orange,
elevation: 12.0, elevation: 12.0,
shape: BeveledRectangleBorder(borderRadius: BorderRadius.circular(12)), shape: BeveledRectangleBorder(borderRadius: BorderRadius.circular(12)),
clipBehavior: Clip.antiAlias,
); );
} }
...@@ -431,6 +431,7 @@ void main() { ...@@ -431,6 +431,7 @@ void main() {
const Color color = Colors.pink; const Color color = Colors.pink;
const double elevation = 9.0; const double elevation = 9.0;
final ShapeBorder shape = BeveledRectangleBorder(borderRadius: BorderRadius.circular(12)); final ShapeBorder shape = BeveledRectangleBorder(borderRadius: BorderRadius.circular(12));
const Clip clipBehavior = Clip.antiAlias;
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
home: Scaffold( home: Scaffold(
...@@ -449,7 +450,7 @@ void main() { ...@@ -449,7 +450,7 @@ void main() {
Container(height: 100.0, child: const Text('Three')), Container(height: 100.0, child: const Text('Three')),
], ],
); );
}, backgroundColor: color, elevation: elevation, shape: shape); }, backgroundColor: color, elevation: elevation, shape: shape, clipBehavior: clipBehavior);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
...@@ -457,6 +458,7 @@ void main() { ...@@ -457,6 +458,7 @@ void main() {
expect(bottomSheet.backgroundColor, color); expect(bottomSheet.backgroundColor, color);
expect(bottomSheet.elevation, elevation); expect(bottomSheet.elevation, elevation);
expect(bottomSheet.shape, shape); expect(bottomSheet.shape, shape);
expect(bottomSheet.clipBehavior, clipBehavior);
}); });
testWidgets('PersistentBottomSheetController.close dismisses the bottom sheet', (WidgetTester tester) async { testWidgets('PersistentBottomSheetController.close dismisses the bottom sheet', (WidgetTester tester) async {
......
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