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

[Material] Add ability to set shadow color and selected shadow color for chips...

[Material] Add ability to set shadow color and selected shadow color for chips and for chip themes (#28163)

* Add ability to set shadow color for chips and for chip themes

* Add selected shadow color as a property as well

* Update phrasing of documentation

* Address PR feedback
parent 0f218e42
......@@ -118,6 +118,11 @@ abstract class ChipAttributes {
///
/// Defaults to 0. The value is always non-negative.
double get elevation;
/// Color of the chip's shadow when the elevation is greater than 0.
///
/// The default is [Colors.black].
Color get shadowColor;
}
/// An interface for material design chips that can be deleted.
......@@ -309,6 +314,12 @@ abstract class SelectableChipAttributes {
/// The chip is selected when [selected] is true.
Color get selectedColor;
/// Color of the chip's shadow when the elevation is greater than 0 and the
/// chip is selected.
///
/// The default is [Colors.black].
Color get selectedShadowColor;
/// Tooltip string to be used for the body area (where the label and avatar
/// are) of the chip.
String get tooltip;
......@@ -486,6 +497,7 @@ class Chip extends StatelessWidget implements ChipAttributes, DeletableChipAttri
this.padding,
this.materialTapTargetSize,
this.elevation,
this.shadowColor,
}) : assert(label != null),
assert(clipBehavior != null),
assert(elevation == null || elevation >= 0.0),
......@@ -519,6 +531,8 @@ class Chip extends StatelessWidget implements ChipAttributes, DeletableChipAttri
final MaterialTapTargetSize materialTapTargetSize;
@override
final double elevation;
@override
final Color shadowColor;
@override
Widget build(BuildContext context) {
......@@ -539,6 +553,7 @@ class Chip extends StatelessWidget implements ChipAttributes, DeletableChipAttri
padding: padding,
materialTapTargetSize: materialTapTargetSize,
elevation: elevation,
shadowColor: shadowColor,
isEnabled: true,
);
}
......@@ -628,6 +643,8 @@ class InputChip extends StatelessWidget
this.padding,
this.materialTapTargetSize,
this.elevation,
this.shadowColor,
this.selectedShadowColor,
this.avatarBorder = const CircleBorder(),
}) : assert(selected != null),
assert(isEnabled != null),
......@@ -682,6 +699,10 @@ class InputChip extends StatelessWidget
@override
final double elevation;
@override
final Color shadowColor;
@override
final Color selectedShadowColor;
@override
final ShapeBorder avatarBorder;
@override
......@@ -710,6 +731,8 @@ class InputChip extends StatelessWidget
padding: padding,
materialTapTargetSize: materialTapTargetSize,
elevation: elevation,
shadowColor: shadowColor,
selectedShadowColor: selectedShadowColor,
isEnabled: isEnabled && (onSelected != null || onDeleted != null || onPressed != null),
avatarBorder: avatarBorder,
);
......@@ -798,6 +821,8 @@ class ChoiceChip extends StatelessWidget
this.padding,
this.materialTapTargetSize,
this.elevation,
this.shadowColor,
this.selectedShadowColor,
this.avatarBorder = const CircleBorder(),
}) : assert(selected != null),
assert(label != null),
......@@ -839,6 +864,10 @@ class ChoiceChip extends StatelessWidget
@override
final double elevation;
@override
final Color shadowColor;
@override
final Color selectedShadowColor;
@override
final ShapeBorder avatarBorder;
@override
......@@ -868,6 +897,8 @@ class ChoiceChip extends StatelessWidget
isEnabled: isEnabled,
materialTapTargetSize: materialTapTargetSize,
elevation: elevation,
shadowColor: shadowColor,
selectedShadowColor: selectedShadowColor,
avatarBorder: avatarBorder,
);
}
......@@ -987,6 +1018,8 @@ class FilterChip extends StatelessWidget
this.padding,
this.materialTapTargetSize,
this.elevation,
this.shadowColor,
this.selectedShadowColor,
this.avatarBorder = const CircleBorder(),
}) : assert(selected != null),
assert(label != null),
......@@ -1028,6 +1061,10 @@ class FilterChip extends StatelessWidget
@override
final double elevation;
@override
final Color shadowColor;
@override
final Color selectedShadowColor;
@override
final ShapeBorder avatarBorder;
@override
......@@ -1054,6 +1091,8 @@ class FilterChip extends StatelessWidget
isEnabled: isEnabled,
materialTapTargetSize: materialTapTargetSize,
elevation: elevation,
shadowColor: shadowColor,
selectedShadowColor: selectedShadowColor,
avatarBorder: avatarBorder,
);
}
......@@ -1127,6 +1166,7 @@ class ActionChip extends StatelessWidget implements ChipAttributes, TappableChip
this.padding,
this.materialTapTargetSize,
this.elevation,
this.shadowColor,
}) : assert(label != null),
assert(
onPressed != null,
......@@ -1163,6 +1203,8 @@ class ActionChip extends StatelessWidget implements ChipAttributes, TappableChip
final MaterialTapTargetSize materialTapTargetSize;
@override
final double elevation;
@override
final Color shadowColor;
@override
Widget build(BuildContext context) {
......@@ -1182,6 +1224,7 @@ class ActionChip extends StatelessWidget implements ChipAttributes, TappableChip
isEnabled: true,
materialTapTargetSize: materialTapTargetSize,
elevation: elevation,
shadowColor: shadowColor,
);
}
}
......@@ -1257,6 +1300,8 @@ class RawChip extends StatefulWidget
this.backgroundColor,
this.materialTapTargetSize,
this.elevation,
this.shadowColor,
this.selectedShadowColor,
this.avatarBorder = const CircleBorder(),
}) : assert(label != null),
assert(isEnabled != null),
......@@ -1311,6 +1356,10 @@ class RawChip extends StatefulWidget
@override
final double elevation;
@override
final Color shadowColor;
@override
final Color selectedShadowColor;
@override
final CircleBorder avatarBorder;
/// Whether or not to show a check mark when [selected] is true.
......@@ -1550,6 +1599,7 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip
static const double _defaultElevation = 0.0;
static const double _defaultPressElevation = 8.0;
static const Color _defaultShadowColor = Colors.black;
@override
Widget build(BuildContext context) {
......@@ -1564,9 +1614,13 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip
final ShapeBorder shape = widget.shape ?? chipTheme.shape;
final double elevation = widget.elevation ?? chipTheme.elevation ?? _defaultElevation;
final double pressElevation = widget.pressElevation ?? chipTheme.pressElevation ?? _defaultPressElevation;
final Color shadowColor = widget.shadowColor ?? chipTheme.shadowColor ?? _defaultShadowColor;
final Color selectedShadowColor = widget.selectedShadowColor ?? chipTheme.selectedShadowColor ?? _defaultShadowColor;
final bool selected = widget.selected ?? false;
Widget result = Material(
elevation: isTapping ? pressElevation : elevation,
shadowColor: selected ? selectedShadowColor : shadowColor,
animationDuration: pressedAnimationDuration,
shape: shape,
clipBehavior: widget.clipBehavior,
......
......@@ -164,8 +164,8 @@ class ChipTheme extends InheritedWidget {
/// * [ThemeData], which has a default [ChipThemeData].
class ChipThemeData extends Diagnosticable {
/// Create a [ChipThemeData] given a set of exact values. All the values
/// must be specified except for [elevation] and [pressElevation], which may
/// be null.
/// must be specified except for [shadowColor], [selectedShadowColor],
/// [elevation], and [pressElevation], which may be null.
///
/// This will rarely be used directly. It is used by [lerp] to
/// create intermediate themes based on two themes.
......@@ -175,6 +175,8 @@ class ChipThemeData extends Diagnosticable {
@required this.disabledColor,
@required this.selectedColor,
@required this.secondarySelectedColor,
this.shadowColor,
this.selectedShadowColor,
@required this.labelPadding,
@required this.padding,
@required this.shape,
......@@ -299,6 +301,25 @@ class ChipThemeData extends Diagnosticable {
/// The chip is selected when [selected] is true.
final Color secondarySelectedColor;
/// Color of the chip's shadow when the elevation is greater than 0.
///
/// If null, the chip defaults to [Colors.black].
///
/// See also:
///
/// * [selectedShadowColor]
final Color shadowColor;
/// Color of the chip's shadow when the elevation is greater than 0 and the
/// chip is selected.
///
/// If null, the chip defaults to [Colors.black].
///
/// See also:
///
/// * [shadowColor]
final Color selectedShadowColor;
/// The padding around the [label] widget.
///
/// By default, this is 4 logical pixels at the beginning and the end of the
......@@ -351,6 +372,8 @@ class ChipThemeData extends Diagnosticable {
Color disabledColor,
Color selectedColor,
Color secondarySelectedColor,
Color shadowColor,
Color selectedShadowColor,
EdgeInsetsGeometry labelPadding,
EdgeInsetsGeometry padding,
ShapeBorder shape,
......@@ -366,6 +389,8 @@ class ChipThemeData extends Diagnosticable {
disabledColor: disabledColor ?? this.disabledColor,
selectedColor: selectedColor ?? this.selectedColor,
secondarySelectedColor: secondarySelectedColor ?? this.secondarySelectedColor,
shadowColor: shadowColor ?? this.shadowColor,
selectedShadowColor: selectedShadowColor ?? this.selectedShadowColor,
labelPadding: labelPadding ?? this.labelPadding,
padding: padding ?? this.padding,
shape: shape ?? this.shape,
......@@ -392,6 +417,8 @@ class ChipThemeData extends Diagnosticable {
disabledColor: Color.lerp(a?.disabledColor, b?.disabledColor, t),
selectedColor: Color.lerp(a?.selectedColor, b?.selectedColor, t),
secondarySelectedColor: Color.lerp(a?.secondarySelectedColor, b?.secondarySelectedColor, t),
shadowColor: Color.lerp(a?.shadowColor, b?.shadowColor, t),
selectedShadowColor: Color.lerp(a?.selectedShadowColor, b?.selectedShadowColor, t),
labelPadding: EdgeInsetsGeometry.lerp(a?.labelPadding, b?.labelPadding, t),
padding: EdgeInsetsGeometry.lerp(a?.padding, b?.padding, t),
shape: ShapeBorder.lerp(a?.shape, b?.shape, t),
......@@ -411,6 +438,8 @@ class ChipThemeData extends Diagnosticable {
disabledColor,
selectedColor,
secondarySelectedColor,
shadowColor,
selectedShadowColor,
labelPadding,
padding,
shape,
......@@ -436,6 +465,8 @@ class ChipThemeData extends Diagnosticable {
&& otherData.disabledColor == disabledColor
&& otherData.selectedColor == selectedColor
&& otherData.secondarySelectedColor == secondarySelectedColor
&& otherData.shadowColor == shadowColor
&& otherData.selectedShadowColor == selectedShadowColor
&& otherData.labelPadding == labelPadding
&& otherData.padding == padding
&& otherData.shape == shape
......@@ -460,6 +491,8 @@ class ChipThemeData extends Diagnosticable {
properties.add(DiagnosticsProperty<Color>('disabledColor', disabledColor, defaultValue: defaultData.disabledColor));
properties.add(DiagnosticsProperty<Color>('selectedColor', selectedColor, defaultValue: defaultData.selectedColor));
properties.add(DiagnosticsProperty<Color>('secondarySelectedColor', secondarySelectedColor, defaultValue: defaultData.secondarySelectedColor));
properties.add(DiagnosticsProperty<Color>('shadowColor', shadowColor, defaultValue: defaultData.shadowColor));
properties.add(DiagnosticsProperty<Color>('selectedShadowColor', selectedShadowColor, defaultValue: defaultData.selectedShadowColor));
properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('labelPadding', labelPadding, defaultValue: defaultData.labelPadding));
properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding, defaultValue: defaultData.padding));
properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: defaultData.shape));
......
......@@ -1485,7 +1485,7 @@ void main() {
expect(tester.takeException(), null);
});
testWidgets('Chip elevation works correctly', (WidgetTester tester) async {
testWidgets('Chip elevation and shadow color work correctly', (WidgetTester tester) async {
final ThemeData theme = ThemeData(
platform: TargetPlatform.android,
primarySwatch: Colors.red,
......@@ -1508,16 +1508,32 @@ void main() {
await tester.pumpWidget(buildChip(chipTheme));
Material material = getMaterial(tester);
expect(material.elevation, 0.0);
expect(material.shadowColor, Colors.black);
inputChip = const InputChip(
label: Text('Label'),
elevation: 4.0,
shadowColor: Colors.green,
selectedShadowColor: Colors.blue,
);
await tester.pumpWidget(buildChip(chipTheme));
await tester.pumpAndSettle();
material = getMaterial(tester);
expect(material.elevation, 4.0);
expect(material.shadowColor, Colors.green);
inputChip = const InputChip(
label: Text('Label'),
selected: true,
shadowColor: Colors.green,
selectedShadowColor: Colors.blue,
);
await tester.pumpWidget(buildChip(chipTheme));
await tester.pumpAndSettle();
material = getMaterial(tester);
expect(material.shadowColor, Colors.blue);
});
testWidgets('can be tapped outside of chip body', (WidgetTester tester) async {
......
......@@ -120,6 +120,7 @@ void main() {
backgroundColor: Colors.purple,
deleteIconColor: Colors.purple.withAlpha(0x3d),
elevation: 3.0,
shadowColor: Colors.pink,
);
const bool value = false;
Widget buildChip(ChipThemeData data) {
......@@ -162,6 +163,7 @@ void main() {
expect(materialBox, paints..path(color: Color(customTheme.backgroundColor.value)));
expect(material.elevation, customTheme.elevation);
expect(material.shadowColor, customTheme.shadowColor);
});
testWidgets('ChipThemeData generates correct opacities for defaults', (WidgetTester tester) async {
......@@ -232,6 +234,8 @@ void main() {
).copyWith(
elevation: 1.0,
pressElevation: 4.0,
shadowColor: Colors.black,
selectedShadowColor: Colors.black,
);
final ChipThemeData chipThemeWhite = ChipThemeData.fromDefaults(
secondaryColor: Colors.white,
......@@ -242,6 +246,8 @@ void main() {
labelPadding: const EdgeInsets.only(top: 8.0, bottom: 8.0),
elevation: 5.0,
pressElevation: 10.0,
shadowColor: Colors.white,
selectedShadowColor: Colors.white,
);
final ChipThemeData lerp = ChipThemeData.lerp(chipThemeBlack, chipThemeWhite, 0.5);
......@@ -251,6 +257,8 @@ void main() {
expect(lerp.disabledColor, equals(middleGrey.withAlpha(0x0c)));
expect(lerp.selectedColor, equals(middleGrey.withAlpha(0x3d)));
expect(lerp.secondarySelectedColor, equals(middleGrey.withAlpha(0x3d)));
expect(lerp.shadowColor, equals(middleGrey));
expect(lerp.selectedShadowColor, equals(middleGrey));
expect(lerp.labelPadding, equals(const EdgeInsets.all(4.0)));
expect(lerp.padding, equals(const EdgeInsets.all(3.0)));
expect(lerp.shape, equals(isInstanceOf<StadiumBorder>()));
......@@ -268,6 +276,8 @@ void main() {
expect(lerpANull25.disabledColor, equals(Colors.black.withAlpha(0x03)));
expect(lerpANull25.selectedColor, equals(Colors.black.withAlpha(0x0f)));
expect(lerpANull25.secondarySelectedColor, equals(Colors.white.withAlpha(0x0f)));
expect(lerpANull25.shadowColor, equals(Colors.white.withAlpha(0x40)));
expect(lerpANull25.selectedShadowColor, equals(Colors.white.withAlpha(0x40)));
expect(lerpANull25.labelPadding, equals(const EdgeInsets.only(left: 0.0, top: 2.0, right: 0.0, bottom: 2.0)));
expect(lerpANull25.padding, equals(const EdgeInsets.all(0.5)));
expect(lerpANull25.shape, equals(isInstanceOf<StadiumBorder>()));
......@@ -283,6 +293,8 @@ void main() {
expect(lerpANull75.disabledColor, equals(Colors.black.withAlpha(0x09)));
expect(lerpANull75.selectedColor, equals(Colors.black.withAlpha(0x2e)));
expect(lerpANull75.secondarySelectedColor, equals(Colors.white.withAlpha(0x2e)));
expect(lerpANull75.shadowColor, equals(Colors.white.withAlpha(0xbf)));
expect(lerpANull75.selectedShadowColor, equals(Colors.white.withAlpha(0xbf)));
expect(lerpANull75.labelPadding, equals(const EdgeInsets.only(left: 0.0, top: 6.0, right: 0.0, bottom: 6.0)));
expect(lerpANull75.padding, equals(const EdgeInsets.all(1.5)));
expect(lerpANull75.shape, equals(isInstanceOf<StadiumBorder>()));
......@@ -298,6 +310,8 @@ void main() {
expect(lerpBNull25.disabledColor, equals(Colors.white.withAlpha(0x09)));
expect(lerpBNull25.selectedColor, equals(Colors.white.withAlpha(0x2e)));
expect(lerpBNull25.secondarySelectedColor, equals(Colors.black.withAlpha(0x2e)));
expect(lerpBNull25.shadowColor, equals(Colors.black.withAlpha(0xbf)));
expect(lerpBNull25.selectedShadowColor, equals(Colors.black.withAlpha(0xbf)));
expect(lerpBNull25.labelPadding, equals(const EdgeInsets.only(left: 6.0, top: 0.0, right: 6.0, bottom: 0.0)));
expect(lerpBNull25.padding, equals(const EdgeInsets.all(3.0)));
expect(lerpBNull25.shape, equals(isInstanceOf<StadiumBorder>()));
......@@ -313,6 +327,8 @@ void main() {
expect(lerpBNull75.disabledColor, equals(Colors.white.withAlpha(0x03)));
expect(lerpBNull75.selectedColor, equals(Colors.white.withAlpha(0x0f)));
expect(lerpBNull75.secondarySelectedColor, equals(Colors.black.withAlpha(0x0f)));
expect(lerpBNull75.shadowColor, equals(Colors.black.withAlpha(0x40)));
expect(lerpBNull75.selectedShadowColor, equals(Colors.black.withAlpha(0x40)));
expect(lerpBNull75.labelPadding, equals(const EdgeInsets.only(left: 2.0, top: 0.0, right: 2.0, bottom: 0.0)));
expect(lerpBNull75.padding, equals(const EdgeInsets.all(1.0)));
expect(lerpBNull75.shape, equals(isInstanceOf<StadiumBorder>()));
......
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