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

Add elevation/pressElevation to ChipThemeData (#27399)

Allow the theme to provide these values to Chips
parent fc67f2ef
......@@ -467,8 +467,8 @@ abstract class TappableChipAttributes {
class Chip extends StatelessWidget implements ChipAttributes, DeletableChipAttributes {
/// Creates a material design chip.
///
/// The [label], [elevation], and [clipBehavior] arguments must not be null.
/// Additionally, the [elevation] must be non-negative.
/// The [label] and [clipBehavior] arguments must not be null.
/// The [elevation] must be null or non-negative.
const Chip({
Key key,
this.avatar,
......@@ -484,10 +484,10 @@ class Chip extends StatelessWidget implements ChipAttributes, DeletableChipAttri
this.backgroundColor,
this.padding,
this.materialTapTargetSize,
this.elevation = 0.0,
this.elevation,
}) : assert(label != null),
assert(clipBehavior != null),
assert(elevation != null && elevation >= 0.0),
assert(elevation == null || elevation >= 0.0),
super(key: key);
@override
......@@ -600,10 +600,9 @@ class InputChip extends StatelessWidget
/// The [onPressed] and [onSelected] callbacks must not both be specified at
/// the same time.
///
/// The [label], [isEnabled], [selected], [pressElevation], [elevation] and
/// [clipBehavior] arguments must not be null. Additionally, [pressElevation]
/// and [elevation] must be non-negative. Typically, [pressElevation] is
/// greater than [elevation].
/// The [label], [isEnabled], [selected], and [clipBehavior] arguments must
/// not be null. The [pressElevation] and [elevation] must be null or
/// non-negative. Typically, [pressElevation] is greater than [elevation].
const InputChip({
Key key,
this.avatar,
......@@ -618,7 +617,7 @@ class InputChip extends StatelessWidget
this.deleteIconColor,
this.deleteButtonTooltipMessage,
this.onPressed,
this.pressElevation = 8.0,
this.pressElevation,
this.disabledColor,
this.selectedColor,
this.tooltip,
......@@ -627,14 +626,14 @@ class InputChip extends StatelessWidget
this.backgroundColor,
this.padding,
this.materialTapTargetSize,
this.elevation = 0.0,
this.elevation,
this.avatarBorder = const CircleBorder(),
}) : assert(selected != null),
assert(isEnabled != null),
assert(label != null),
assert(clipBehavior != null),
assert(pressElevation != null && pressElevation >= 0.0),
assert(elevation != null && elevation >= 0.0),
assert(pressElevation == null || pressElevation >= 0.0),
assert(elevation == null || elevation >= 0.0),
super(key: key);
@override
......@@ -777,10 +776,9 @@ class ChoiceChip extends StatelessWidget
DisabledChipAttributes {
/// Create a chip that acts like a radio button.
///
/// The [label], [selected], [pressElevation], [elevation] and [clipBehavior]
/// arguments must not be null. Additionally, [pressElevation] and [elevation]
/// must be non-negative. Typically, [pressElevation] is greater than
/// [elevation].
/// The [label], [selected], and [clipBehavior] arguments must not be null.
/// The [pressElevation] and [elevation] must be null or non-negative.
/// Typically, [pressElevation] is greater than [elevation].
const ChoiceChip({
Key key,
this.avatar,
......@@ -788,7 +786,7 @@ class ChoiceChip extends StatelessWidget
this.labelStyle,
this.labelPadding,
this.onSelected,
this.pressElevation = 8.0,
this.pressElevation,
@required this.selected,
this.selectedColor,
this.disabledColor,
......@@ -798,13 +796,13 @@ class ChoiceChip extends StatelessWidget
this.backgroundColor,
this.padding,
this.materialTapTargetSize,
this.elevation = 0.0,
this.elevation,
this.avatarBorder = const CircleBorder(),
}) : assert(selected != null),
assert(label != null),
assert(clipBehavior != null),
assert(pressElevation != null && pressElevation >= 0.0),
assert(elevation != null && elevation >= 0.0),
assert(pressElevation == null || pressElevation >= 0.0),
assert(elevation == null || elevation >= 0.0),
super(key: key);
@override
......@@ -967,10 +965,9 @@ class FilterChip extends StatelessWidget
DisabledChipAttributes {
/// Create a chip that acts like a checkbox.
///
/// The [selected], [label], [pressElevation], [elevation] and [clipBehavior]
/// arguments must not be null. Additionally, [pressElevation] and [elevation]
/// must be non-negative. Typically, [pressElevation] is greater than
/// [elevation].
/// The [selected], [label], and [clipBehavior] arguments must not be null.
/// The [pressElevation] and [elevation] must be null or non-negative.
/// Typically, [pressElevation] is greater than [elevation].
const FilterChip({
Key key,
this.avatar,
......@@ -979,7 +976,7 @@ class FilterChip extends StatelessWidget
this.labelPadding,
this.selected = false,
@required this.onSelected,
this.pressElevation = 8.0,
this.pressElevation,
this.disabledColor,
this.selectedColor,
this.tooltip,
......@@ -988,13 +985,13 @@ class FilterChip extends StatelessWidget
this.backgroundColor,
this.padding,
this.materialTapTargetSize,
this.elevation = 0.0,
this.elevation,
this.avatarBorder = const CircleBorder(),
}) : assert(selected != null),
assert(label != null),
assert(clipBehavior != null),
assert(pressElevation != null && pressElevation >= 0.0),
assert(elevation != null && elevation >= 0.0),
assert(pressElevation == null || pressElevation >= 0.0),
assert(elevation == null || elevation >= 0.0),
super(key: key);
@override
......@@ -1111,10 +1108,9 @@ class FilterChip extends StatelessWidget
class ActionChip extends StatelessWidget implements ChipAttributes, TappableChipAttributes {
/// Create a chip that acts like a button.
///
/// The [label], [onPressed], [pressElevation], [elevation] and [clipBehavior]
/// arguments must not be null. Additionally, [pressElevation] and [elevation]
/// must be non-negative. Typically, [pressElevation] is greater than
/// [elevation].
/// The [label], [onPressed] and [clipBehavior] arguments must not be null.
/// The [pressElevation] and [elevation] must be null or non-negative.
/// Typically, [pressElevation] is greater than [elevation].
const ActionChip({
Key key,
this.avatar,
......@@ -1122,22 +1118,22 @@ class ActionChip extends StatelessWidget implements ChipAttributes, TappableChip
this.labelStyle,
this.labelPadding,
@required this.onPressed,
this.pressElevation = 8.0,
this.pressElevation,
this.tooltip,
this.shape,
this.clipBehavior = Clip.none,
this.backgroundColor,
this.padding,
this.materialTapTargetSize,
this.elevation = 0.0,
this.elevation,
}) : assert(label != null),
assert(
onPressed != null,
'Rather than disabling an ActionChip by setting onPressed to null, '
'remove it from the interface entirely.',
),
assert(pressElevation != null && pressElevation >= 0.0),
assert(elevation != null && elevation >= 0.0),
assert(pressElevation == null || pressElevation >= 0.0),
assert(elevation == null || elevation >= 0.0),
super(key: key);
@override
......@@ -1231,10 +1227,9 @@ class RawChip extends StatefulWidget
/// The [onPressed] and [onSelected] callbacks must not both be specified at
/// the same time.
///
/// The [label], [pressElevation], [elevation], [isEnabled], and
/// [clipBehavior] arguments must not be null. Additionally, [pressElevation]
/// and [elevation] must be non-negative. Typically, [pressElevation] is
/// greater than [elevation].
/// The [label], [isEnabled], and [clipBehavior] arguments must not be null.
/// The [pressElevation] and [elevation] must be null or non-negative.
/// Typically, [pressElevation] is greater than [elevation].
const RawChip({
Key key,
this.avatar,
......@@ -1248,7 +1243,7 @@ class RawChip extends StatefulWidget
this.deleteButtonTooltipMessage,
this.onPressed,
this.onSelected,
this.pressElevation = 8.0,
this.pressElevation,
this.tapEnabled = true,
this.selected,
this.showCheckmark = true,
......@@ -1260,13 +1255,13 @@ class RawChip extends StatefulWidget
this.clipBehavior = Clip.none,
this.backgroundColor,
this.materialTapTargetSize,
this.elevation = 0.0,
this.elevation,
this.avatarBorder = const CircleBorder(),
}) : assert(label != null),
assert(isEnabled != null),
assert(clipBehavior != null),
assert(pressElevation != null && pressElevation >= 0.0),
assert(elevation != null && elevation >= 0.0),
assert(pressElevation == null || pressElevation >= 0.0),
assert(elevation == null || elevation >= 0.0),
deleteIcon = deleteIcon ?? _kDefaultDeleteIcon,
super(key: key);
......@@ -1552,6 +1547,9 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip
);
}
static const double _defaultElevation = 0.0;
static const double _defaultPressElevation = 8.0;
@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context));
......@@ -1563,10 +1561,11 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip
final ChipThemeData chipTheme = ChipTheme.of(context);
final TextDirection textDirection = Directionality.of(context);
final ShapeBorder shape = widget.shape ?? chipTheme.shape;
final double elevation = widget.elevation ?? chipTheme.elevation ?? _defaultElevation;
final double pressElevation = widget.pressElevation ?? chipTheme.pressElevation ?? _defaultPressElevation;
Widget result = Material(
elevation: isTapping ? widget.pressElevation : widget.elevation,
elevation: isTapping ? pressElevation : elevation,
animationDuration: pressedAnimationDuration,
shape: shape,
clipBehavior: widget.clipBehavior,
......
......@@ -2,6 +2,8 @@
// 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';
import 'package:flutter/widgets.dart';
......@@ -162,7 +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.
/// must be specified except for [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.
......@@ -178,6 +181,8 @@ class ChipThemeData extends Diagnosticable {
@required this.labelStyle,
@required this.secondaryLabelStyle,
@required this.brightness,
this.elevation,
this.pressElevation,
}) : assert(backgroundColor != null),
assert(disabledColor != null),
assert(selectedColor != null),
......@@ -328,6 +333,16 @@ class ChipThemeData extends Diagnosticable {
/// This affects various base material color choices in the chip rendering.
final Brightness brightness;
/// The elevation to be applied to the chip.
///
/// If null, the chip defaults to 0.
final double elevation;
/// The elevation to be applied to the chip during the press motion.
///
/// If null, the chip defaults to 8.
final double pressElevation;
/// Creates a copy of this object but with the given fields replaced with the
/// new values.
ChipThemeData copyWith({
......@@ -342,6 +357,8 @@ class ChipThemeData extends Diagnosticable {
TextStyle labelStyle,
TextStyle secondaryLabelStyle,
Brightness brightness,
double elevation,
double pressElevation,
}) {
return ChipThemeData(
backgroundColor: backgroundColor ?? this.backgroundColor,
......@@ -355,6 +372,8 @@ class ChipThemeData extends Diagnosticable {
labelStyle: labelStyle ?? this.labelStyle,
secondaryLabelStyle: secondaryLabelStyle ?? this.secondaryLabelStyle,
brightness: brightness ?? this.brightness,
elevation: elevation ?? this.elevation,
pressElevation: pressElevation ?? this.pressElevation,
);
}
......@@ -379,6 +398,8 @@ class ChipThemeData extends Diagnosticable {
labelStyle: TextStyle.lerp(a?.labelStyle, b?.labelStyle, t),
secondaryLabelStyle: TextStyle.lerp(a?.secondaryLabelStyle, b?.secondaryLabelStyle, t),
brightness: t < 0.5 ? a?.brightness ?? Brightness.light : b?.brightness ?? Brightness.light,
elevation: lerpDouble(a?.elevation, b?.elevation, t),
pressElevation: lerpDouble(a?.pressElevation, b?.pressElevation, t),
);
}
......@@ -396,6 +417,8 @@ class ChipThemeData extends Diagnosticable {
labelStyle,
secondaryLabelStyle,
brightness,
elevation,
pressElevation,
);
}
......@@ -418,7 +441,9 @@ class ChipThemeData extends Diagnosticable {
&& otherData.shape == shape
&& otherData.labelStyle == labelStyle
&& otherData.secondaryLabelStyle == secondaryLabelStyle
&& otherData.brightness == brightness;
&& otherData.brightness == brightness
&& otherData.elevation == elevation
&& otherData.pressElevation == pressElevation;
}
@override
......@@ -441,5 +466,7 @@ class ChipThemeData extends Diagnosticable {
properties.add(DiagnosticsProperty<TextStyle>('labelStyle', labelStyle, defaultValue: defaultData.labelStyle));
properties.add(DiagnosticsProperty<TextStyle>('secondaryLabelStyle', secondaryLabelStyle, defaultValue: defaultData.secondaryLabelStyle));
properties.add(EnumProperty<Brightness>('brightness', brightness, defaultValue: defaultData.brightness));
properties.add(DoubleProperty('elevation', elevation, defaultValue: defaultData.elevation));
properties.add(DoubleProperty('pressElevation', pressElevation, defaultValue: defaultData.pressElevation));
}
}
......@@ -26,6 +26,15 @@ RenderBox getMaterialBox(WidgetTester tester) {
);
}
Material getMaterial(WidgetTester tester) {
return tester.widget<Material>(
find.descendant(
of: find.byType(RawChip),
matching: find.byType(Material),
),
);
}
IconThemeData getIconData(WidgetTester tester) {
final IconTheme iconTheme = tester.firstWidget(
find.descendant(
......@@ -1497,19 +1506,18 @@ void main() {
}
await tester.pumpWidget(buildChip(chipTheme));
expect(inputChip.pressElevation, 8.0);
expect(inputChip.elevation, 0.0);
Material material = getMaterial(tester);
expect(material.elevation, 0.0);
inputChip = const InputChip(
label: Text('Label'),
pressElevation: 12.0,
elevation: 4.0,
);
await tester.pumpWidget(buildChip(chipTheme));
await tester.pumpAndSettle();
expect(inputChip.pressElevation, 12.0);
expect(inputChip.elevation, 4.0);
material = getMaterial(tester);
expect(material.elevation, 4.0);
});
testWidgets('can be tapped outside of chip body', (WidgetTester tester) async {
......
......@@ -20,6 +20,15 @@ RenderBox getMaterialBox(WidgetTester tester) {
);
}
Material getMaterial(WidgetTester tester) {
return tester.widget<Material>(
find.descendant(
of: find.byType(RawChip),
matching: find.byType(Material),
),
);
}
IconThemeData getIconData(WidgetTester tester) {
final IconTheme iconTheme = tester.firstWidget(
find.descendant(
......@@ -110,6 +119,7 @@ void main() {
final ChipThemeData customTheme = chipTheme.copyWith(
backgroundColor: Colors.purple,
deleteIconColor: Colors.purple.withAlpha(0x3d),
elevation: 3.0,
);
const bool value = false;
Widget buildChip(ChipThemeData data) {
......@@ -148,8 +158,10 @@ void main() {
await tester.pumpAndSettle();
final RenderBox materialBox = getMaterialBox(tester);
final Material material = getMaterial(tester);
expect(materialBox, paints..path(color: Color(customTheme.backgroundColor.value)));
expect(material.elevation, customTheme.elevation);
});
testWidgets('ChipThemeData generates correct opacities for defaults', (WidgetTester tester) async {
......@@ -217,12 +229,21 @@ void main() {
secondaryColor: Colors.black,
brightness: Brightness.dark,
labelStyle: ThemeData.fallback().accentTextTheme.body2.copyWith(color: Colors.black),
).copyWith(
elevation: 1.0,
pressElevation: 4.0,
);
final ChipThemeData chipThemeWhite = ChipThemeData.fromDefaults(
secondaryColor: Colors.white,
brightness: Brightness.light,
labelStyle: ThemeData.fallback().accentTextTheme.body2.copyWith(color: Colors.white),
).copyWith(padding: const EdgeInsets.all(2.0), labelPadding: const EdgeInsets.only(top: 8.0, bottom: 8.0));
).copyWith(
padding: const EdgeInsets.all(2.0),
labelPadding: const EdgeInsets.only(top: 8.0, bottom: 8.0),
elevation: 5.0,
pressElevation: 10.0,
);
final ChipThemeData lerp = ChipThemeData.lerp(chipThemeBlack, chipThemeWhite, 0.5);
const Color middleGrey = Color(0xff7f7f7f);
expect(lerp.backgroundColor, equals(middleGrey.withAlpha(0x1f)));
......@@ -236,6 +257,8 @@ void main() {
expect(lerp.labelStyle.color, equals(middleGrey.withAlpha(0xde)));
expect(lerp.secondaryLabelStyle.color, equals(middleGrey.withAlpha(0xde)));
expect(lerp.brightness, equals(Brightness.light));
expect(lerp.elevation, 3.0);
expect(lerp.pressElevation, 7.0);
expect(ChipThemeData.lerp(null, null, 0.25), isNull);
......@@ -251,6 +274,8 @@ void main() {
expect(lerpANull25.labelStyle.color, equals(Colors.black.withAlpha(0x38)));
expect(lerpANull25.secondaryLabelStyle.color, equals(Colors.white.withAlpha(0x38)));
expect(lerpANull25.brightness, equals(Brightness.light));
expect(lerpANull25.elevation, 1.25);
expect(lerpANull25.pressElevation, 2.5);
final ChipThemeData lerpANull75 = ChipThemeData.lerp(null, chipThemeWhite, 0.75);
expect(lerpANull75.backgroundColor, equals(Colors.black.withAlpha(0x17)));
......@@ -264,6 +289,8 @@ void main() {
expect(lerpANull75.labelStyle.color, equals(Colors.black.withAlpha(0xa7)));
expect(lerpANull75.secondaryLabelStyle.color, equals(Colors.white.withAlpha(0xa7)));
expect(lerpANull75.brightness, equals(Brightness.light));
expect(lerpANull75.elevation, 3.75);
expect(lerpANull75.pressElevation, 7.5);
final ChipThemeData lerpBNull25 = ChipThemeData.lerp(chipThemeBlack, null, 0.25);
expect(lerpBNull25.backgroundColor, equals(Colors.white.withAlpha(0x17)));
......@@ -277,6 +304,8 @@ void main() {
expect(lerpBNull25.labelStyle.color, equals(Colors.white.withAlpha(0xa7)));
expect(lerpBNull25.secondaryLabelStyle.color, equals(Colors.black.withAlpha(0xa7)));
expect(lerpBNull25.brightness, equals(Brightness.dark));
expect(lerpBNull25.elevation, 0.75);
expect(lerpBNull25.pressElevation, 3.0);
final ChipThemeData lerpBNull75 = ChipThemeData.lerp(chipThemeBlack, null, 0.75);
expect(lerpBNull75.backgroundColor, equals(Colors.white.withAlpha(0x08)));
......@@ -290,5 +319,7 @@ void main() {
expect(lerpBNull75.labelStyle.color, equals(Colors.white.withAlpha(0x38)));
expect(lerpBNull75.secondaryLabelStyle.color, equals(Colors.black.withAlpha(0x38)));
expect(lerpBNull75.brightness, equals(Brightness.light));
expect(lerpBNull75.elevation, 0.25);
expect(lerpBNull75.pressElevation, 1.0);
});
}
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