Unverified Commit d696b051 authored by Eilidh Southren's avatar Eilidh Southren Committed by GitHub

Bottom appbar/sheet shadow property (#121406)

* add shadowColor property

* add to bottom app bar

* add test

* update m2/m3 diffs

* reorder debug test

* finalize

* remove crswap

* update doc comments

* add m2 shadow back

* add const

* update docs

* update docs

* comment replies

* make param non-null

* indentation fix

* doc fix
parent 7a280785
......@@ -24,6 +24,9 @@ class _${blockName}DefaultsM3 extends BottomAppBarTheme {
@override
Color? get surfaceTintColor => ${componentColor('md.comp.bottom-app-bar.container.surface-tint-layer')};
@override
Color? get shadowColor => Colors.transparent;
}
''';
}
......@@ -24,6 +24,9 @@ class _${blockName}DefaultsM3 extends BottomSheetThemeData {
@override
Color? get surfaceTintColor => ${componentColor("md.comp.sheet.bottom.docked.container.surface-tint-layer")};
@override
Color? get shadowColor => Colors.transparent;
}
''';
}
......@@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'bottom_app_bar_theme.dart';
import 'colors.dart';
import 'elevation_overlay.dart';
import 'material.dart';
import 'scaffold.dart';
......@@ -71,6 +72,7 @@ class BottomAppBar extends StatefulWidget {
this.child,
this.padding,
this.surfaceTintColor,
this.shadowColor,
this.height,
}) : assert(elevation == null || elevation >= 0.0);
......@@ -135,6 +137,18 @@ class BottomAppBar extends StatefulWidget {
/// See [Material.surfaceTintColor] for more details on how this overlay is applied.
final Color? surfaceTintColor;
/// The color of the shadow below the app bar.
///
/// If this property is null, then [BottomAppBarTheme.shadowColor] of
/// [ThemeData.bottomAppBarTheme] is used. If that is also null, the default value
/// is fully opaque black for Material 2, and transparent for Material 3.
///
/// See also:
///
/// * [elevation], which defines the size of the shadow below the app bar.
/// * [shape], which defines the shape of the app bar and its shadow.
final Color? shadowColor;
/// The double value used to indicate the height of the [BottomAppBar].
///
/// If this is null, the default value is the minimum in relation to the content,
......@@ -177,29 +191,33 @@ class _BottomAppBarState extends State<BottomAppBar> {
final Color color = widget.color ?? babTheme.color ?? defaults.color!;
final Color surfaceTintColor = widget.surfaceTintColor ?? babTheme.surfaceTintColor ?? defaults.surfaceTintColor!;
final Color effectiveColor = isMaterial3 ? color : ElevationOverlay.applyOverlay(context, color, elevation);
final Color shadowColor = widget.shadowColor ?? babTheme.shadowColor ?? defaults.shadowColor!;
final Widget child = Padding(
padding: widget.padding ?? babTheme.padding ?? (isMaterial3 ? const EdgeInsets.symmetric(vertical: 12.0, horizontal: 16.0) : EdgeInsets.zero),
child: widget.child,
);
return SizedBox(
height: height,
child: PhysicalShape(
clipper: clipper,
elevation: elevation,
color: effectiveColor,
clipBehavior: widget.clipBehavior,
child: Material(
final Material material = Material(
key: materialKey,
type: isMaterial3 ? MaterialType.canvas : MaterialType.transparency,
elevation: elevation,
color: isMaterial3 ? effectiveColor : null,
surfaceTintColor: surfaceTintColor,
shadowColor: shadowColor,
child: SafeArea(child: child),
),
),
);
final PhysicalShape physicalShape = PhysicalShape(
clipper: clipper,
elevation: elevation,
shadowColor: shadowColor,
color: effectiveColor,
clipBehavior: widget.clipBehavior,
child: material,
);
return SizedBox(height: height, child: physicalShape);
}
}
......@@ -260,6 +278,9 @@ class _BottomAppBarDefaultsM2 extends BottomAppBarTheme {
@override
Color? get surfaceTintColor => Theme.of(context).colorScheme.surfaceTint;
@override
Color get shadowColor => const Color(0xFF000000);
}
// BEGIN GENERATED TOKEN PROPERTIES - BottomAppBar
......@@ -286,6 +307,9 @@ class _BottomAppBarDefaultsM3 extends BottomAppBarTheme {
@override
Color? get surfaceTintColor => Theme.of(context).colorScheme.surfaceTint;
@override
Color get shadowColor => Colors.transparent;
}
// END GENERATED TOKEN PROPERTIES - BottomAppBar
......@@ -34,6 +34,7 @@ class BottomAppBarTheme with Diagnosticable {
this.shape,
this.height,
this.surfaceTintColor,
this.shadowColor,
this.padding,
});
......@@ -49,8 +50,6 @@ class BottomAppBarTheme with Diagnosticable {
final NotchedShape? shape;
/// Overrides the default value for [BottomAppBar.height].
///
/// If null, [BottomAppBar] height will be the minimum on the non material 3.
final double? height;
/// Overrides the default value for [BottomAppBar.surfaceTintColor].
......@@ -60,6 +59,9 @@ class BottomAppBarTheme with Diagnosticable {
/// See [Material.surfaceTintColor] for more details.
final Color? surfaceTintColor;
/// Overrides the default value for [BottomAppBar.shadowColor].
final Color? shadowColor;
/// Overrides the default value for [BottomAppBar.padding].
final EdgeInsetsGeometry? padding;
......@@ -71,6 +73,7 @@ class BottomAppBarTheme with Diagnosticable {
NotchedShape? shape,
double? height,
Color? surfaceTintColor,
Color? shadowColor,
EdgeInsetsGeometry? padding,
}) {
return BottomAppBarTheme(
......@@ -79,6 +82,7 @@ class BottomAppBarTheme with Diagnosticable {
shape: shape ?? this.shape,
height: height ?? this.height,
surfaceTintColor: surfaceTintColor ?? this.surfaceTintColor,
shadowColor: shadowColor ?? this.shadowColor,
padding: padding ?? this.padding,
);
}
......@@ -103,6 +107,7 @@ class BottomAppBarTheme with Diagnosticable {
shape: t < 0.5 ? a?.shape : b?.shape,
height: lerpDouble(a?.height, b?.height, t),
surfaceTintColor: Color.lerp(a?.surfaceTintColor, b?.surfaceTintColor, t),
shadowColor: Color.lerp(a?.shadowColor, b?.shadowColor, t),
padding: EdgeInsetsGeometry.lerp(a?.padding, b?.padding, t),
);
}
......@@ -114,6 +119,7 @@ class BottomAppBarTheme with Diagnosticable {
shape,
height,
surfaceTintColor,
shadowColor,
padding,
);
......@@ -131,6 +137,7 @@ class BottomAppBarTheme with Diagnosticable {
&& other.shape == shape
&& other.height == height
&& other.surfaceTintColor == surfaceTintColor
&& other.shadowColor == shadowColor
&& other.padding == padding;
}
......@@ -142,6 +149,7 @@ class BottomAppBarTheme with Diagnosticable {
properties.add(DiagnosticsProperty<NotchedShape>('shape', shape, defaultValue: null));
properties.add(DiagnosticsProperty<double>('height', height, defaultValue: null));
properties.add(ColorProperty('surfaceTintColor', surfaceTintColor, defaultValue: null));
properties.add(ColorProperty('shadowColor', shadowColor, defaultValue: null));
properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding, defaultValue: null));
}
}
......@@ -78,6 +78,7 @@ class BottomSheet extends StatefulWidget {
this.onDragStart,
this.onDragEnd,
this.backgroundColor,
this.shadowColor,
this.elevation,
this.shape,
this.clipBehavior,
......@@ -134,6 +135,18 @@ class BottomSheet extends StatefulWidget {
/// Defaults to null and falls back to [Material]'s default.
final Color? backgroundColor;
/// The color of the shadow below the sheet.
///
/// If this property is null, then [BottomSheetThemeData.shadowColor] of
/// [ThemeData.bottomSheetTheme] is used. If that is also null, the default value
/// is transparent.
///
/// See also:
///
/// * [elevation], which defines the size of the shadow below the sheet.
/// * [shape], which defines the shape of the sheet and its shadow.
final Color? shadowColor;
/// The z-coordinate at which to place this material relative to its parent.
///
/// This controls the size of the shadow below the material.
......@@ -275,6 +288,7 @@ class _BottomSheetState extends State<BottomSheet> {
final BoxConstraints? constraints = widget.constraints ?? bottomSheetTheme.constraints;
final Color? color = widget.backgroundColor ?? bottomSheetTheme.backgroundColor ?? defaults.backgroundColor;
final Color? surfaceTintColor = bottomSheetTheme.surfaceTintColor ?? defaults.surfaceTintColor;
final Color? shadowColor = widget.shadowColor ?? bottomSheetTheme.shadowColor ?? defaults.shadowColor;
final double elevation = widget.elevation ?? bottomSheetTheme.elevation ?? defaults.elevation ?? 0;
final ShapeBorder? shape = widget.shape ?? bottomSheetTheme.shape ?? defaults.shape;
final Clip clipBehavior = widget.clipBehavior ?? bottomSheetTheme.clipBehavior ?? Clip.none;
......@@ -284,6 +298,7 @@ class _BottomSheetState extends State<BottomSheet> {
color: color,
elevation: elevation,
surfaceTintColor: surfaceTintColor,
shadowColor: shadowColor,
shape: shape,
clipBehavior: clipBehavior,
child: NotificationListener<DraggableScrollableNotification>(
......@@ -1142,6 +1157,9 @@ class _BottomSheetDefaultsM3 extends BottomSheetThemeData {
@override
Color? get surfaceTintColor => Theme.of(context).colorScheme.surfaceTint;
@override
Color? get shadowColor => Colors.transparent;
}
// END GENERATED TOKEN PROPERTIES - BottomSheet
......@@ -33,6 +33,7 @@ class BottomSheetThemeData with Diagnosticable {
this.elevation,
this.modalBackgroundColor,
this.modalBarrierColor,
this.shadowColor,
this.modalElevation,
this.shape,
this.clipBehavior,
......@@ -66,6 +67,9 @@ class BottomSheetThemeData with Diagnosticable {
/// a modal bottom sheet.
final Color? modalBarrierColor;
/// Overrides the default value for [BottomSheet.shadowColor].
final Color? shadowColor;
/// Value for [BottomSheet.elevation] when the Bottom sheet is presented as a
/// modal bottom sheet.
final double? modalElevation;
......@@ -94,6 +98,7 @@ class BottomSheetThemeData with Diagnosticable {
double? elevation,
Color? modalBackgroundColor,
Color? modalBarrierColor,
Color? shadowColor,
double? modalElevation,
ShapeBorder? shape,
Clip? clipBehavior,
......@@ -105,6 +110,7 @@ class BottomSheetThemeData with Diagnosticable {
elevation: elevation ?? this.elevation,
modalBackgroundColor: modalBackgroundColor ?? this.modalBackgroundColor,
modalBarrierColor: modalBarrierColor ?? this.modalBarrierColor,
shadowColor: shadowColor ?? this.shadowColor,
modalElevation: modalElevation ?? this.modalElevation,
shape: shape ?? this.shape,
clipBehavior: clipBehavior ?? this.clipBehavior,
......@@ -127,6 +133,7 @@ class BottomSheetThemeData with Diagnosticable {
elevation: lerpDouble(a?.elevation, b?.elevation, t),
modalBackgroundColor: Color.lerp(a?.modalBackgroundColor, b?.modalBackgroundColor, t),
modalBarrierColor: Color.lerp(a?.modalBarrierColor, b?.modalBarrierColor, t),
shadowColor: Color.lerp(a?.shadowColor, b?.shadowColor, t),
modalElevation: lerpDouble(a?.modalElevation, b?.modalElevation, t),
shape: ShapeBorder.lerp(a?.shape, b?.shape, t),
clipBehavior: t < 0.5 ? a?.clipBehavior : b?.clipBehavior,
......@@ -141,6 +148,7 @@ class BottomSheetThemeData with Diagnosticable {
elevation,
modalBackgroundColor,
modalBarrierColor,
shadowColor,
modalElevation,
shape,
clipBehavior,
......@@ -160,6 +168,7 @@ class BottomSheetThemeData with Diagnosticable {
&& other.surfaceTintColor == surfaceTintColor
&& other.elevation == elevation
&& other.modalBackgroundColor == modalBackgroundColor
&& other.shadowColor == shadowColor
&& other.modalBarrierColor == modalBarrierColor
&& other.modalElevation == modalElevation
&& other.shape == shape
......@@ -174,6 +183,7 @@ class BottomSheetThemeData with Diagnosticable {
properties.add(ColorProperty('surfaceTintColor', surfaceTintColor, defaultValue: null));
properties.add(DoubleProperty('elevation', elevation, defaultValue: null));
properties.add(ColorProperty('modalBackgroundColor', modalBackgroundColor, defaultValue: null));
properties.add(ColorProperty('shadowColor', shadowColor, defaultValue: null));
properties.add(ColorProperty('modalBarrierColor', modalBarrierColor, defaultValue: null));
properties.add(DoubleProperty('modalElevation', modalElevation, defaultValue: null));
properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: null));
......
......@@ -233,6 +233,27 @@ void main() {
expect(material.color, const Color(0xff0000ff));
});
testWidgets('Shadow color is transparent in Material 3', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(useMaterial3: true,
),
home: const Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: null,
),
bottomNavigationBar: BottomAppBar(
color: Color(0xff0000ff),
),
),
)
);
final Material material = tester.widget(find.byType(Material).at(1));
expect(material.shadowColor, Colors.transparent);
});
testWidgets('dark theme applies an elevation overlay color', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
......
......@@ -196,6 +196,17 @@ void main() {
expect(widget.surfaceTintColor, babThemeSurfaceTintColor);
});
testWidgets('BAB theme overrides shadowColor - M3', (WidgetTester tester) async {
const Color babThemeShadowColor = Colors.yellow;
const BottomAppBarTheme theme = BottomAppBarTheme(
shadowColor: babThemeShadowColor, elevation: 0
);
await tester.pumpWidget(_withTheme(theme, true));
final Material widget = getBabRenderObject(tester);
expect(widget.shadowColor, babThemeShadowColor);
});
testWidgets('BAB surfaceTintColor - Widget - M3', (WidgetTester tester) async {
const Color themeSurfaceTintColor = Colors.white10;
const Color babThemeSurfaceTintColor = Colors.black87;
......
......@@ -903,6 +903,30 @@ void main() {
expect(material.shape, defaultShape);
});
testWidgets('BottomSheet has transparent shadow in material3', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
theme: ThemeData(
useMaterial3: true,
),
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),
),
);
expect(material.shadowColor, Colors.transparent);
});
testWidgets('modal BottomSheet with scrollController has semantics', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
......
......@@ -27,6 +27,7 @@ void main() {
test('BottomSheetThemeData null fields by default', () {
const BottomSheetThemeData bottomSheetTheme = BottomSheetThemeData();
expect(bottomSheetTheme.backgroundColor, null);
expect(bottomSheetTheme.shadowColor, null);
expect(bottomSheetTheme.elevation, null);
expect(bottomSheetTheme.shape, null);
expect(bottomSheetTheme.clipBehavior, null);
......@@ -50,6 +51,7 @@ void main() {
const BottomSheetThemeData(
backgroundColor: Color(0xFFFFFFFF),
elevation: 2.0,
shadowColor: Color(0xFF00FFFF),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(2.0))),
clipBehavior: Clip.antiAlias,
constraints: BoxConstraints(minWidth: 200, maxWidth: 640),
......@@ -63,6 +65,7 @@ void main() {
expect(description, <String>[
'backgroundColor: Color(0xffffffff)',
'elevation: 2.0',
'shadowColor: Color(0xff00ffff)',
'shape: RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.circular(2.0))',
'clipBehavior: Clip.antiAlias',
'constraints: BoxConstraints(200.0<=w<=640.0, 0.0<=h<=Infinity)',
......@@ -122,6 +125,7 @@ void main() {
testWidgets('BottomSheet widget properties take priority over theme', (WidgetTester tester) async {
const Color backgroundColor = Colors.purple;
const Color shadowColor = Colors.blue;
const double elevation = 7.0;
const ShapeBorder shape = RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(9.0)),
......@@ -133,6 +137,7 @@ void main() {
home: Scaffold(
body: BottomSheet(
backgroundColor: backgroundColor,
shadowColor: shadowColor,
elevation: elevation,
shape: shape,
clipBehavior: Clip.hardEdge,
......@@ -151,6 +156,7 @@ void main() {
),
);
expect(material.color, backgroundColor);
expect(material.shadowColor, shadowColor);
expect(material.elevation, elevation);
expect(material.shape, shape);
expect(material.clipBehavior, clipBehavior);
......@@ -240,18 +246,22 @@ void main() {
const double darkElevation = 3.0;
const Color lightBackgroundColor = Colors.green;
const Color darkBackgroundColor = Colors.grey;
const Color lightShadowColor = Colors.blue;
const Color darkShadowColor = Colors.purple;
await tester.pumpWidget(MaterialApp(
theme: ThemeData.light().copyWith(
bottomSheetTheme: const BottomSheetThemeData(
elevation: lightElevation,
backgroundColor: lightBackgroundColor,
shadowColor: lightShadowColor,
),
),
darkTheme: ThemeData.dark().copyWith(
bottomSheetTheme: const BottomSheetThemeData(
elevation: darkElevation,
backgroundColor: darkBackgroundColor,
shadowColor: darkShadowColor,
),
),
home: Scaffold(
......@@ -287,6 +297,7 @@ void main() {
);
expect(lightMaterial.elevation, lightElevation);
expect(lightMaterial.color, lightBackgroundColor);
expect(lightMaterial.shadowColor, lightShadowColor);
// Simulate the user changing to dark theme
tester.binding.platformDispatcher.platformBrightnessTestValue = Brightness.dark;
......@@ -300,6 +311,7 @@ void main() {
);
expect(darkMaterial.elevation, darkElevation);
expect(darkMaterial.color, darkBackgroundColor);
expect(darkMaterial.shadowColor, darkShadowColor);
});
}
......
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