[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
This commit is contained in:
parent
0f218e427c
commit
b73dd0e81e
@ -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>()));
|
||||
|
Loading…
x
Reference in New Issue
Block a user