Unverified Commit 4dbbf678 authored by Greg Spencer's avatar Greg Spencer Committed by GitHub

Adding ChipTheme, ChipThemeData, and some more tests. (#16447)

This converts the chips to use a ChipThemeData to get most of their customization values from (if not overridden by specific arguments to the chip constructors), and to have the base ThemeData contain one of these. It also adds the ChipTheme widget that will allow overriding the theme for a particular subtree in the widget hierarchy.

Added tests for both, and just more tests in general for the Chips.
parent c31706f9
...@@ -30,6 +30,7 @@ export 'src/material/card.dart'; ...@@ -30,6 +30,7 @@ export 'src/material/card.dart';
export 'src/material/checkbox.dart'; export 'src/material/checkbox.dart';
export 'src/material/checkbox_list_tile.dart'; export 'src/material/checkbox_list_tile.dart';
export 'src/material/chip.dart'; export 'src/material/chip.dart';
export 'src/material/chip_theme.dart';
export 'src/material/circle_avatar.dart'; export 'src/material/circle_avatar.dart';
export 'src/material/colors.dart'; export 'src/material/colors.dart';
export 'src/material/constants.dart'; export 'src/material/constants.dart';
......
...@@ -10,6 +10,7 @@ import 'package:flutter/painting.dart'; ...@@ -10,6 +10,7 @@ import 'package:flutter/painting.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'chip_theme.dart';
import 'colors.dart'; import 'colors.dart';
import 'debug.dart'; import 'debug.dart';
import 'icons.dart'; import 'icons.dart';
...@@ -22,25 +23,20 @@ import 'tooltip.dart'; ...@@ -22,25 +23,20 @@ import 'tooltip.dart';
// Some design constants // Some design constants
const double _kChipHeight = 32.0; const double _kChipHeight = 32.0;
const double _kDeleteIconSize = 18.0; const double _kDeleteIconSize = 18.0;
const int _kTextLabelAlpha = 0xde; // 87%
const double _kDeleteIconOpacity = 0.87; const int _kCheckmarkAlpha = 0xde; // 87%
const EdgeInsetsGeometry _kDefaultPadding = const EdgeInsets.all(4.0); const int _kDisabledAlpha = 0x61; // 38%
const EdgeInsetsGeometry _kDefaultLabelPadding = const EdgeInsets.symmetric(horizontal: 8.0);
const EdgeInsetsGeometry _kDefaultAvatarPadding = EdgeInsets.zero;
const EdgeInsetsGeometry _kDefaultDeleteIconPadding = EdgeInsets.zero;
const int _kDisabledAlpha = 0x5e; // 36%
const double _kCheckmarkStrokeWidth = 2.0; const double _kCheckmarkStrokeWidth = 2.0;
const double _kPressElevation = 8.0; const double _kPressElevation = 8.0;
const Duration _kSelectDuration = const Duration(milliseconds: 195); const Duration _kSelectDuration = const Duration(milliseconds: 195);
const Duration _kCheckmarkDuration = const Duration(milliseconds: 150); const Duration _kCheckmarkDuration = const Duration(milliseconds: 150);
const Duration _kCheckmarkReverseDuration = const Duration(milliseconds: 50); const Duration _kCheckmarkReverseDuration = const Duration(milliseconds: 50);
const Duration _kDrawerDuration = const Duration(milliseconds: 150); const Duration _kDrawerDuration = const Duration(milliseconds: 150);
const Duration _kReverseDrawerDuration = const Duration(milliseconds: 100); const Duration _kReverseDrawerDuration = const Duration(milliseconds: 100);
const Duration _kDisableDuration = const Duration(milliseconds: 75); const Duration _kDisableDuration = const Duration(milliseconds: 75);
const Color _kSelectScrimColor = const Color(0x60191919); const Color _kSelectScrimColor = const Color(0x60191919);
const Color _kDefaultSelectedColor = const Color(0x30000000); // 19% black
const Color _kDefaultBackgroundColor = const Color(0x14000000); // 8% black
const Color _kDefaultDisabledColor = const Color(0x08000000); // 3% black
const Icon _kDefaultDeleteIcon = const Icon(Icons.cancel, size: _kDeleteIconSize); const Icon _kDefaultDeleteIcon = const Icon(Icons.cancel, size: _kDeleteIconSize);
/// An interface defining the base attributes for a material design chip. /// An interface defining the base attributes for a material design chip.
...@@ -86,7 +82,7 @@ abstract class ChipAttributes { ...@@ -86,7 +82,7 @@ abstract class ChipAttributes {
/// The [ShapeBorder] to draw around the chip. /// The [ShapeBorder] to draw around the chip.
/// ///
/// Defaults to a [StadiumBorder]. Must not be null. /// Defaults to the shape in the ambient [ChipThemeData].
ShapeBorder get shape; ShapeBorder get shape;
/// Color to be used for the unselected, enabled chip's background. /// Color to be used for the unselected, enabled chip's background.
...@@ -104,11 +100,6 @@ abstract class ChipAttributes { ...@@ -104,11 +100,6 @@ abstract class ChipAttributes {
/// By default, this is 4 logical pixels at the beginning and the end of the /// By default, this is 4 logical pixels at the beginning and the end of the
/// label, and zero on top and bottom. /// label, and zero on top and bottom.
EdgeInsetsGeometry get labelPadding; EdgeInsetsGeometry get labelPadding;
/// The padding around the [avatar] widget.
///
/// By default, this is zero on all sides.
EdgeInsetsGeometry get avatarPadding;
} }
/// An interface for material design chips that can be deleted. /// An interface for material design chips that can be deleted.
...@@ -199,9 +190,6 @@ abstract class DeletableChipAttributes { ...@@ -199,9 +190,6 @@ abstract class DeletableChipAttributes {
/// The message to be used for the chip's delete button tooltip. /// The message to be used for the chip's delete button tooltip.
String get deleteButtonTooltipMessage; String get deleteButtonTooltipMessage;
/// The padding around the [deleteIcon] widget.
EdgeInsetsGeometry get deleteIconPadding;
} }
/// An interface for material design chips that can be selected. /// An interface for material design chips that can be selected.
...@@ -422,24 +410,21 @@ abstract class TappableChipAttributes { ...@@ -422,24 +410,21 @@ abstract class TappableChipAttributes {
class Chip extends StatelessWidget implements ChipAttributes, DeletableChipAttributes { class Chip extends StatelessWidget implements ChipAttributes, DeletableChipAttributes {
/// Creates a material design chip. /// Creates a material design chip.
/// ///
/// The [label], [deleteIcon], and [border] arguments must not be null. /// The [label] argument must not be null.
const Chip({ const Chip({
Key key, Key key,
this.avatar, this.avatar,
@required this.label, @required this.label,
this.labelStyle, this.labelStyle,
this.labelPadding, this.labelPadding,
this.avatarPadding,
this.deleteIconPadding,
this.deleteIcon, this.deleteIcon,
this.onDeleted, this.onDeleted,
this.deleteIconColor, this.deleteIconColor,
this.deleteButtonTooltipMessage, this.deleteButtonTooltipMessage,
this.shape: const StadiumBorder(), this.shape,
this.backgroundColor, this.backgroundColor,
this.padding, this.padding,
}) : assert(label != null), }) : assert(label != null),
assert(shape != null),
super(key: key); super(key: key);
@override @override
...@@ -451,16 +436,12 @@ class Chip extends StatelessWidget implements ChipAttributes, DeletableChipAttri ...@@ -451,16 +436,12 @@ class Chip extends StatelessWidget implements ChipAttributes, DeletableChipAttri
@override @override
final EdgeInsetsGeometry labelPadding; final EdgeInsetsGeometry labelPadding;
@override @override
final EdgeInsetsGeometry avatarPadding;
@override
final ShapeBorder shape; final ShapeBorder shape;
@override @override
final Color backgroundColor; final Color backgroundColor;
@override @override
final EdgeInsetsGeometry padding; final EdgeInsetsGeometry padding;
@override @override
final EdgeInsetsGeometry deleteIconPadding;
@override
final Widget deleteIcon; final Widget deleteIcon;
@override @override
final VoidCallback onDeleted; final VoidCallback onDeleted;
...@@ -477,8 +458,6 @@ class Chip extends StatelessWidget implements ChipAttributes, DeletableChipAttri ...@@ -477,8 +458,6 @@ class Chip extends StatelessWidget implements ChipAttributes, DeletableChipAttri
label: label, label: label,
labelStyle: labelStyle, labelStyle: labelStyle,
labelPadding: labelPadding, labelPadding: labelPadding,
avatarPadding: avatarPadding,
deleteIconPadding: deleteIconPadding,
deleteIcon: deleteIcon, deleteIcon: deleteIcon,
onDeleted: onDeleted, onDeleted: onDeleted,
deleteIconColor: deleteIconColor, deleteIconColor: deleteIconColor,
...@@ -548,16 +527,13 @@ class InputChip extends StatelessWidget ...@@ -548,16 +527,13 @@ class InputChip extends StatelessWidget
/// The [onPressed] and [onSelected] callbacks must not both be specified at /// The [onPressed] and [onSelected] callbacks must not both be specified at
/// the same time. /// the same time.
/// ///
/// The [label], [isEnabled], [selected], and [border] arguments must not be /// The [label], [isEnabled] and [selected] arguments must not be null.
/// null.
const InputChip({ const InputChip({
Key key, Key key,
this.avatar, this.avatar,
@required this.label, @required this.label,
this.labelStyle, this.labelStyle,
this.labelPadding, this.labelPadding,
this.avatarPadding,
this.deleteIconPadding,
this.selected: false, this.selected: false,
this.isEnabled: true, this.isEnabled: true,
this.onSelected, this.onSelected,
...@@ -569,13 +545,12 @@ class InputChip extends StatelessWidget ...@@ -569,13 +545,12 @@ class InputChip extends StatelessWidget
this.disabledColor, this.disabledColor,
this.selectedColor, this.selectedColor,
this.tooltip, this.tooltip,
this.shape: const StadiumBorder(), this.shape,
this.backgroundColor, this.backgroundColor,
this.padding, this.padding,
}) : assert(selected != null), }) : assert(selected != null),
assert(isEnabled != null), assert(isEnabled != null),
assert(label != null), assert(label != null),
assert(shape != null),
super(key: key); super(key: key);
@override @override
...@@ -587,10 +562,6 @@ class InputChip extends StatelessWidget ...@@ -587,10 +562,6 @@ class InputChip extends StatelessWidget
@override @override
final EdgeInsetsGeometry labelPadding; final EdgeInsetsGeometry labelPadding;
@override @override
final EdgeInsetsGeometry avatarPadding;
@override
final EdgeInsetsGeometry deleteIconPadding;
@override
final bool selected; final bool selected;
@override @override
final bool isEnabled; final bool isEnabled;
...@@ -627,8 +598,6 @@ class InputChip extends StatelessWidget ...@@ -627,8 +598,6 @@ class InputChip extends StatelessWidget
label: label, label: label,
labelStyle: labelStyle, labelStyle: labelStyle,
labelPadding: labelPadding, labelPadding: labelPadding,
avatarPadding: avatarPadding,
deleteIconPadding: deleteIconPadding,
deleteIcon: deleteIcon, deleteIcon: deleteIcon,
onDeleted: onDeleted, onDeleted: onDeleted,
deleteIconColor: deleteIconColor, deleteIconColor: deleteIconColor,
...@@ -706,25 +675,23 @@ class ChoiceChip extends StatelessWidget ...@@ -706,25 +675,23 @@ class ChoiceChip extends StatelessWidget
DisabledChipAttributes { DisabledChipAttributes {
/// Create a chip that acts like a radio button. /// Create a chip that acts like a radio button.
/// ///
/// The [selected], [label], and [border] arguments must not be null. /// The [label] and [selected] attributes must not be null.
const ChoiceChip({ const ChoiceChip({
Key key, Key key,
this.avatar, this.avatar,
@required this.label, @required this.label,
this.labelStyle, this.labelStyle,
this.labelPadding, this.labelPadding,
this.avatarPadding,
this.onSelected, this.onSelected,
@required this.selected, @required this.selected,
this.selectedColor, this.selectedColor,
this.disabledColor, this.disabledColor,
this.tooltip, this.tooltip,
this.shape: const StadiumBorder(), this.shape,
this.backgroundColor, this.backgroundColor,
this.padding, this.padding,
}) : assert(selected != null), }) : assert(selected != null),
assert(label != null), assert(label != null),
assert(shape != null),
super(key: key); super(key: key);
@override @override
...@@ -736,8 +703,6 @@ class ChoiceChip extends StatelessWidget ...@@ -736,8 +703,6 @@ class ChoiceChip extends StatelessWidget
@override @override
final EdgeInsetsGeometry labelPadding; final EdgeInsetsGeometry labelPadding;
@override @override
final EdgeInsetsGeometry avatarPadding;
@override
final ValueChanged<bool> onSelected; final ValueChanged<bool> onSelected;
@override @override
final bool selected; final bool selected;
...@@ -760,12 +725,12 @@ class ChoiceChip extends StatelessWidget ...@@ -760,12 +725,12 @@ class ChoiceChip extends StatelessWidget
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context)); assert(debugCheckHasMaterial(context));
final ChipThemeData chipTheme = ChipTheme.of(context);
return new RawChip( return new RawChip(
avatar: avatar, avatar: avatar,
label: label, label: label,
labelStyle: labelStyle, labelStyle: labelStyle ?? (selected ? chipTheme.secondaryLabelStyle : null),
labelPadding: labelPadding, labelPadding: labelPadding,
avatarPadding: avatarPadding,
onSelected: onSelected, onSelected: onSelected,
selected: selected, selected: selected,
showCheckmark: false, showCheckmark: false,
...@@ -773,7 +738,7 @@ class ChoiceChip extends StatelessWidget ...@@ -773,7 +738,7 @@ class ChoiceChip extends StatelessWidget
tooltip: tooltip, tooltip: tooltip,
shape: shape, shape: shape,
disabledColor: disabledColor, disabledColor: disabledColor,
selectedColor: selectedColor, selectedColor: selectedColor ?? chipTheme.secondarySelectedColor,
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
padding: padding, padding: padding,
isEnabled: isEnabled, isEnabled: isEnabled,
...@@ -789,8 +754,7 @@ class ChoiceChip extends StatelessWidget ...@@ -789,8 +754,7 @@ class ChoiceChip extends StatelessWidget
/// Unlike these alternatives, filter chips allow for clearly delineated and /// Unlike these alternatives, filter chips allow for clearly delineated and
/// exposed options in a compact area. /// exposed options in a compact area.
/// ///
/// Requires one of its ancestors to be a [Material] widget. The [label] /// Requires one of its ancestors to be a [Material] widget.
/// and [border] arguments must not be null.
/// ///
/// ## Sample code /// ## Sample code
/// ///
...@@ -874,25 +838,23 @@ class FilterChip extends StatelessWidget ...@@ -874,25 +838,23 @@ class FilterChip extends StatelessWidget
DisabledChipAttributes { DisabledChipAttributes {
/// Create a chip that acts like a checkbox. /// Create a chip that acts like a checkbox.
/// ///
/// The [selected], [label], and [border] arguments must not be null. /// The [selected] and [label] attributes must not be null.
const FilterChip({ const FilterChip({
Key key, Key key,
this.avatar, this.avatar,
@required this.label, @required this.label,
this.labelStyle, this.labelStyle,
this.labelPadding, this.labelPadding,
this.avatarPadding,
this.selected: false, this.selected: false,
@required this.onSelected, @required this.onSelected,
this.disabledColor, this.disabledColor,
this.selectedColor, this.selectedColor,
this.tooltip, this.tooltip,
this.shape: const StadiumBorder(), this.shape,
this.backgroundColor, this.backgroundColor,
this.padding, this.padding,
}) : assert(selected != null), }) : assert(selected != null),
assert(label != null), assert(label != null),
assert(shape != null),
super(key: key); super(key: key);
@override @override
...@@ -904,8 +866,6 @@ class FilterChip extends StatelessWidget ...@@ -904,8 +866,6 @@ class FilterChip extends StatelessWidget
@override @override
final EdgeInsetsGeometry labelPadding; final EdgeInsetsGeometry labelPadding;
@override @override
final EdgeInsetsGeometry avatarPadding;
@override
final bool selected; final bool selected;
@override @override
final ValueChanged<bool> onSelected; final ValueChanged<bool> onSelected;
...@@ -933,7 +893,6 @@ class FilterChip extends StatelessWidget ...@@ -933,7 +893,6 @@ class FilterChip extends StatelessWidget
label: label, label: label,
labelStyle: labelStyle, labelStyle: labelStyle,
labelPadding: labelPadding, labelPadding: labelPadding,
avatarPadding: avatarPadding,
onSelected: onSelected, onSelected: onSelected,
selected: selected, selected: selected,
tooltip: tooltip, tooltip: tooltip,
...@@ -964,8 +923,7 @@ class FilterChip extends StatelessWidget ...@@ -964,8 +923,7 @@ class FilterChip extends StatelessWidget
/// [OutlineButton], are an alternative to action chips, which should appear /// [OutlineButton], are an alternative to action chips, which should appear
/// statically and consistently in a UI. /// statically and consistently in a UI.
/// ///
/// Requires one of its ancestors to be a [Material] widget. The [onPressed], /// Requires one of its ancestors to be a [Material] widget.
/// [label], and [border] arguments must not be null.
/// ///
/// ## Sample code /// ## Sample code
/// ///
...@@ -997,21 +955,19 @@ class FilterChip extends StatelessWidget ...@@ -997,21 +955,19 @@ class FilterChip extends StatelessWidget
class ActionChip extends StatelessWidget implements ChipAttributes, TappableChipAttributes { class ActionChip extends StatelessWidget implements ChipAttributes, TappableChipAttributes {
/// Create a chip that acts like a button. /// Create a chip that acts like a button.
/// ///
/// The [label], [border], and [onPressed] arguments must not be null. /// The [label] and [onPressed] arguments must not be null.
const ActionChip({ const ActionChip({
Key key, Key key,
this.avatar, this.avatar,
@required this.label, @required this.label,
this.labelStyle, this.labelStyle,
this.labelPadding, this.labelPadding,
this.avatarPadding,
@required this.onPressed, @required this.onPressed,
this.tooltip, this.tooltip,
this.shape: const StadiumBorder(), this.shape,
this.backgroundColor, this.backgroundColor,
this.padding, this.padding,
}) : assert(label != null), }) : assert(label != null),
assert(shape != null),
assert( assert(
onPressed != null, onPressed != null,
'Rather than disabling an ActionChip by setting onPressed to null, ' 'Rather than disabling an ActionChip by setting onPressed to null, '
...@@ -1028,8 +984,6 @@ class ActionChip extends StatelessWidget implements ChipAttributes, TappableChip ...@@ -1028,8 +984,6 @@ class ActionChip extends StatelessWidget implements ChipAttributes, TappableChip
@override @override
final EdgeInsetsGeometry labelPadding; final EdgeInsetsGeometry labelPadding;
@override @override
final EdgeInsetsGeometry avatarPadding;
@override
final VoidCallback onPressed; final VoidCallback onPressed;
@override @override
final String tooltip; final String tooltip;
...@@ -1053,7 +1007,6 @@ class ActionChip extends StatelessWidget implements ChipAttributes, TappableChip ...@@ -1053,7 +1007,6 @@ class ActionChip extends StatelessWidget implements ChipAttributes, TappableChip
shape: shape, shape: shape,
padding: padding, padding: padding,
labelPadding: labelPadding, labelPadding: labelPadding,
avatarPadding: avatarPadding,
isEnabled: true, isEnabled: true,
); );
} }
...@@ -1101,16 +1054,14 @@ class RawChip extends StatefulWidget ...@@ -1101,16 +1054,14 @@ class RawChip extends StatefulWidget
/// The [onPressed] and [onSelected] callbacks must not both be specified at /// The [onPressed] and [onSelected] callbacks must not both be specified at
/// the same time. /// the same time.
/// ///
/// The [label], [isEnabled], and [border] arguments must not be null. /// The [label] and [isEnabled] arguments must not be null.
const RawChip({ const RawChip({
Key key, Key key,
this.avatar, this.avatar,
@required this.label, @required this.label,
this.labelStyle, this.labelStyle,
EdgeInsetsGeometry padding, this.padding,
EdgeInsetsGeometry labelPadding, this.labelPadding,
EdgeInsetsGeometry avatarPadding,
EdgeInsetsGeometry deleteIconPadding,
Widget deleteIcon, Widget deleteIcon,
this.onDeleted, this.onDeleted,
this.deleteIconColor, this.deleteIconColor,
...@@ -1121,22 +1072,14 @@ class RawChip extends StatefulWidget ...@@ -1121,22 +1072,14 @@ class RawChip extends StatefulWidget
this.selected, this.selected,
this.showCheckmark: true, this.showCheckmark: true,
this.isEnabled: true, this.isEnabled: true,
Color disabledColor, this.disabledColor,
Color selectedColor, this.selectedColor,
this.tooltip, this.tooltip,
@required this.shape, this.shape,
Color backgroundColor, this.backgroundColor,
}) : assert(label != null), }) : assert(label != null),
assert(shape != null),
assert(isEnabled != null), assert(isEnabled != null),
padding = padding ?? _kDefaultPadding,
labelPadding = labelPadding ?? _kDefaultLabelPadding,
avatarPadding = avatarPadding ?? _kDefaultAvatarPadding,
deleteIconPadding = deleteIconPadding ?? _kDefaultDeleteIconPadding,
deleteIcon = deleteIcon ?? _kDefaultDeleteIcon, deleteIcon = deleteIcon ?? _kDefaultDeleteIcon,
disabledColor = disabledColor ?? _kDefaultDisabledColor,
selectedColor = selectedColor ?? _kDefaultSelectedColor,
backgroundColor = backgroundColor ?? _kDefaultBackgroundColor,
super(key: key); super(key: key);
@override @override
...@@ -1148,10 +1091,6 @@ class RawChip extends StatefulWidget ...@@ -1148,10 +1091,6 @@ class RawChip extends StatefulWidget
@override @override
final EdgeInsetsGeometry labelPadding; final EdgeInsetsGeometry labelPadding;
@override @override
final EdgeInsetsGeometry avatarPadding;
@override
final EdgeInsetsGeometry deleteIconPadding;
@override
final Widget deleteIcon; final Widget deleteIcon;
@override @override
final VoidCallback onDeleted; final VoidCallback onDeleted;
...@@ -1337,14 +1276,14 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip ...@@ -1337,14 +1276,14 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip
/// Picks between three different colors, depending upon the state of two /// Picks between three different colors, depending upon the state of two
/// different animations. /// different animations.
Color get backgroundColor { Color getBackgroundColor(ChipThemeData theme) {
final ColorTween backgroundTween = new ColorTween( final ColorTween backgroundTween = new ColorTween(
begin: widget.disabledColor, begin: widget.disabledColor ?? theme.disabledColor,
end: widget.backgroundColor, end: widget.backgroundColor ?? theme.backgroundColor,
); );
final ColorTween selectTween = new ColorTween( final ColorTween selectTween = new ColorTween(
begin: backgroundTween.evaluate(enableController), begin: backgroundTween.evaluate(enableController),
end: widget.selectedColor, end: widget.selectedColor ?? theme.selectedColor,
); );
return selectTween.evaluate(selectionFade); return selectTween.evaluate(selectionFade);
} }
...@@ -1400,7 +1339,7 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip ...@@ -1400,7 +1339,7 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip
); );
} }
Widget _buildDeleteIcon(BuildContext context, ThemeData theme) { Widget _buildDeleteIcon(BuildContext context, ThemeData theme, ChipThemeData chipTheme) {
if (!hasDeleteButton) { if (!hasDeleteButton) {
return null; return null;
} }
...@@ -1409,13 +1348,12 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip ...@@ -1409,13 +1348,12 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip
onTap: widget.isEnabled ? widget.onDeleted : null, onTap: widget.isEnabled ? widget.onDeleted : null,
child: new IconTheme( child: new IconTheme(
data: theme.iconTheme.copyWith( data: theme.iconTheme.copyWith(
color: widget.deleteIconColor ?? theme.iconTheme.color, color: (widget.deleteIconColor ?? chipTheme.deleteIconColor) ?? theme.iconTheme.color,
opacity: _kDeleteIconOpacity,
), ),
child: widget.deleteIcon, child: widget.deleteIcon,
), ),
), ),
widget.deleteButtonTooltipMessage ?? MaterialLocalizations.of(context).deleteButtonTooltip, widget.deleteButtonTooltipMessage ?? MaterialLocalizations.of(context)?.deleteButtonTooltip,
widget.onDeleted, widget.onDeleted,
); );
} }
...@@ -1423,12 +1361,18 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip ...@@ -1423,12 +1361,18 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context)); assert(debugCheckHasMaterial(context));
assert(debugCheckHasMediaQuery(context));
assert(debugCheckHasDirectionality(context));
final ThemeData theme = Theme.of(context); final ThemeData theme = Theme.of(context);
final ChipThemeData chipTheme = ChipTheme.of(context);
final TextDirection textDirection = Directionality.of(context); final TextDirection textDirection = Directionality.of(context);
final ShapeBorder shape = widget.shape ?? chipTheme.shape;
return new Material( return new Material(
elevation: isTapping ? _kPressElevation : 0.0, elevation: isTapping ? _kPressElevation : 0.0,
animationDuration: pressedAnimationDuration, animationDuration: pressedAnimationDuration,
shape: widget.shape, shape: shape,
child: new InkResponse( child: new InkResponse(
onTap: canTap ? _handleTap : null, onTap: canTap ? _handleTap : null,
onTapDown: canTap ? _handleTapDown : null, onTapDown: canTap ? _handleTapDown : null,
...@@ -1438,8 +1382,8 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip ...@@ -1438,8 +1382,8 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip
builder: (BuildContext context, Widget child) { builder: (BuildContext context, Widget child) {
return new Container( return new Container(
decoration: new ShapeDecoration( decoration: new ShapeDecoration(
shape: widget.shape, shape: shape,
color: backgroundColor, color: getBackgroundColor(chipTheme),
), ),
child: child, child: child,
); );
...@@ -1452,10 +1396,7 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip ...@@ -1452,10 +1396,7 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip
textAlign: TextAlign.start, textAlign: TextAlign.start,
maxLines: 1, maxLines: 1,
softWrap: false, softWrap: false,
style: widget.labelStyle ?? style: widget.labelStyle ?? chipTheme.labelStyle,
theme.textTheme.body2.copyWith(
color: Colors.black.withAlpha(_kTextLabelAlpha),
),
child: widget.label, child: widget.label,
), ),
avatar: new AnimatedChildSwitcher( avatar: new AnimatedChildSwitcher(
...@@ -1464,14 +1405,13 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip ...@@ -1464,14 +1405,13 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip
switchInCurve: Curves.fastOutSlowIn, switchInCurve: Curves.fastOutSlowIn,
), ),
deleteIcon: new AnimatedChildSwitcher( deleteIcon: new AnimatedChildSwitcher(
child: _buildDeleteIcon(context, theme), child: _buildDeleteIcon(context, theme, chipTheme),
duration: _kDrawerDuration, duration: _kDrawerDuration,
switchInCurve: Curves.fastOutSlowIn, switchInCurve: Curves.fastOutSlowIn,
), ),
padding: widget.padding?.resolve(textDirection), brightness: chipTheme.brightness,
labelPadding: widget.labelPadding?.resolve(textDirection), padding: (widget.padding ?? chipTheme.padding).resolve(textDirection),
avatarPadding: widget.avatarPadding?.resolve(textDirection), labelPadding: (widget.labelPadding ?? chipTheme.labelPadding).resolve(textDirection),
deleteIconPadding: widget.deleteIconPadding?.resolve(textDirection),
showAvatar: hasAvatar, showAvatar: hasAvatar,
showCheckmark: widget.showCheckmark, showCheckmark: widget.showCheckmark,
canTapBody: canTap, canTapBody: canTap,
...@@ -1662,10 +1602,9 @@ class _ChipRenderTheme { ...@@ -1662,10 +1602,9 @@ class _ChipRenderTheme {
@required this.avatar, @required this.avatar,
@required this.label, @required this.label,
@required this.deleteIcon, @required this.deleteIcon,
@required this.brightness,
@required this.padding, @required this.padding,
@required this.labelPadding, @required this.labelPadding,
@required this.avatarPadding,
@required this.deleteIconPadding,
@required this.showAvatar, @required this.showAvatar,
@required this.showCheckmark, @required this.showCheckmark,
@required this.canTapBody, @required this.canTapBody,
...@@ -1674,10 +1613,9 @@ class _ChipRenderTheme { ...@@ -1674,10 +1613,9 @@ class _ChipRenderTheme {
final Widget avatar; final Widget avatar;
final Widget label; final Widget label;
final Widget deleteIcon; final Widget deleteIcon;
final Brightness brightness;
final EdgeInsets padding; final EdgeInsets padding;
final EdgeInsets labelPadding; final EdgeInsets labelPadding;
final EdgeInsets avatarPadding;
final EdgeInsets deleteIconPadding;
final bool showAvatar; final bool showAvatar;
final bool showCheckmark; final bool showCheckmark;
final bool canTapBody; final bool canTapBody;
...@@ -1694,10 +1632,9 @@ class _ChipRenderTheme { ...@@ -1694,10 +1632,9 @@ class _ChipRenderTheme {
return typedOther.avatar == avatar return typedOther.avatar == avatar
&& typedOther.label == label && typedOther.label == label
&& typedOther.deleteIcon == deleteIcon && typedOther.deleteIcon == deleteIcon
&& typedOther.brightness == brightness
&& typedOther.padding == padding && typedOther.padding == padding
&& typedOther.labelPadding == labelPadding && typedOther.labelPadding == labelPadding
&& typedOther.avatarPadding == avatarPadding
&& typedOther.deleteIconPadding == deleteIconPadding
&& typedOther.showAvatar == showAvatar && typedOther.showAvatar == showAvatar
&& typedOther.showCheckmark == showCheckmark && typedOther.showCheckmark == showCheckmark
&& typedOther.canTapBody == canTapBody; && typedOther.canTapBody == canTapBody;
...@@ -1709,10 +1646,9 @@ class _ChipRenderTheme { ...@@ -1709,10 +1646,9 @@ class _ChipRenderTheme {
avatar, avatar,
label, label,
deleteIcon, deleteIcon,
brightness,
padding, padding,
labelPadding, labelPadding,
avatarPadding,
deleteIconPadding,
showAvatar, showAvatar,
showCheckmark, showCheckmark,
canTapBody, canTapBody,
...@@ -1888,9 +1824,7 @@ class _RenderChip extends RenderBox { ...@@ -1888,9 +1824,7 @@ class _RenderChip extends RenderBox {
// because we add the padding regardless to give extra padding for the label // because we add the padding regardless to give extra padding for the label
// when they're missing. // when they're missing.
final double overallPadding = theme.padding.horizontal + final double overallPadding = theme.padding.horizontal +
theme.labelPadding.horizontal + theme.labelPadding.horizontal;
theme.deleteIconPadding.horizontal +
theme.avatarPadding.horizontal;
return overallPadding + return overallPadding +
_minWidth(avatar, height) + _minWidth(avatar, height) +
_minWidth(label, height) + _minWidth(label, height) +
...@@ -1900,9 +1834,7 @@ class _RenderChip extends RenderBox { ...@@ -1900,9 +1834,7 @@ class _RenderChip extends RenderBox {
@override @override
double computeMaxIntrinsicWidth(double height) { double computeMaxIntrinsicWidth(double height) {
final double overallPadding = theme.padding.vertical + final double overallPadding = theme.padding.vertical +
theme.labelPadding.horizontal + theme.labelPadding.horizontal;
theme.deleteIconPadding.horizontal +
theme.avatarPadding.horizontal;
return overallPadding + return overallPadding +
_maxWidth(avatar, height) + _maxWidth(avatar, height) +
_maxWidth(label, height) + _maxWidth(label, height) +
...@@ -1961,7 +1893,7 @@ class _RenderChip extends RenderBox { ...@@ -1961,7 +1893,7 @@ class _RenderChip extends RenderBox {
} }
Size _layoutAvatar(BoxConstraints contentConstraints, double contentSize) { Size _layoutAvatar(BoxConstraints contentConstraints, double contentSize) {
final double requestedSize = math.max(0.0, contentSize - theme.avatarPadding.vertical); final double requestedSize = math.max(0.0, contentSize);
final BoxConstraints avatarConstraints = new BoxConstraints.tightFor( final BoxConstraints avatarConstraints = new BoxConstraints.tightFor(
width: requestedSize, width: requestedSize,
height: requestedSize, height: requestedSize,
...@@ -1970,8 +1902,8 @@ class _RenderChip extends RenderBox { ...@@ -1970,8 +1902,8 @@ class _RenderChip extends RenderBox {
if (!theme.showCheckmark && !theme.showAvatar) { if (!theme.showCheckmark && !theme.showAvatar) {
return new Size(0.0, contentSize); return new Size(0.0, contentSize);
} }
double avatarWidth = theme.avatarPadding.horizontal; double avatarWidth = 0.0;
double avatarHeight = theme.avatarPadding.vertical; double avatarHeight = 0.0;
final Size avatarBoxSize = _boxSize(avatar); final Size avatarBoxSize = _boxSize(avatar);
if (theme.showAvatar) { if (theme.showAvatar) {
avatarWidth += avatarDrawerAnimation.value * avatarBoxSize.width; avatarWidth += avatarDrawerAnimation.value * avatarBoxSize.width;
...@@ -1983,7 +1915,7 @@ class _RenderChip extends RenderBox { ...@@ -1983,7 +1915,7 @@ class _RenderChip extends RenderBox {
} }
Size _layoutDeleteIcon(BoxConstraints contentConstraints, double contentSize) { Size _layoutDeleteIcon(BoxConstraints contentConstraints, double contentSize) {
final double requestedSize = math.max(0.0, contentSize - theme.deleteIconPadding.vertical); final double requestedSize = math.max(0.0, contentSize);
final BoxConstraints deleteIconConstraints = new BoxConstraints.tightFor( final BoxConstraints deleteIconConstraints = new BoxConstraints.tightFor(
width: requestedSize, width: requestedSize,
height: requestedSize, height: requestedSize,
...@@ -1992,8 +1924,8 @@ class _RenderChip extends RenderBox { ...@@ -1992,8 +1924,8 @@ class _RenderChip extends RenderBox {
if (!deleteIconShowing) { if (!deleteIconShowing) {
return new Size(0.0, contentSize); return new Size(0.0, contentSize);
} }
double deleteIconWidth = theme.deleteIconPadding.horizontal; double deleteIconWidth = 0.0;
double deleteIconHeight = theme.deleteIconPadding.vertical; double deleteIconHeight = 0.0;
final Size boxSize = _boxSize(deleteIcon); final Size boxSize = _boxSize(deleteIcon);
deleteIconWidth += deleteDrawerAnimation.value * boxSize.width; deleteIconWidth += deleteDrawerAnimation.value * boxSize.width;
deleteIconHeight += boxSize.height; deleteIconHeight += boxSize.height;
...@@ -2118,9 +2050,9 @@ class _RenderChip extends RenderBox { ...@@ -2118,9 +2050,9 @@ class _RenderChip extends RenderBox {
0.0, 0.0,
((labelSize.height - theme.labelPadding.vertical) - _boxSize(label).height) / 2.0, ((labelSize.height - theme.labelPadding.vertical) - _boxSize(label).height) / 2.0,
); );
_boxParentData(avatar).offset = theme.padding.topLeft + avatarOffset + theme.avatarPadding.topLeft; _boxParentData(avatar).offset = theme.padding.topLeft + avatarOffset;
_boxParentData(label).offset = theme.padding.topLeft + labelOffset + theme.labelPadding.topLeft; _boxParentData(label).offset = theme.padding.topLeft + labelOffset + theme.labelPadding.topLeft;
_boxParentData(deleteIcon).offset = theme.padding.topLeft + deleteIconOffset + theme.deleteIconPadding.topLeft; _boxParentData(deleteIcon).offset = theme.padding.topLeft + deleteIconOffset;
final Size paddedSize = new Size( final Size paddedSize = new Size(
overallSize.width + theme.padding.horizontal, overallSize.width + theme.padding.horizontal,
overallSize.height + theme.padding.vertical, overallSize.height + theme.padding.vertical,
...@@ -2136,11 +2068,6 @@ class _RenderChip extends RenderBox { ...@@ -2136,11 +2068,6 @@ class _RenderChip extends RenderBox {
'${constraints.constrainWidth(paddedSize.width)}'); '${constraints.constrainWidth(paddedSize.width)}');
} }
static final ColorTween enableTween = new ColorTween(
begin: Colors.white.withAlpha(_kDisabledAlpha),
end: Colors.white,
);
static final ColorTween selectionScrimTween = new ColorTween( static final ColorTween selectionScrimTween = new ColorTween(
begin: Colors.transparent, begin: Colors.transparent,
end: _kSelectScrimColor, end: _kSelectScrimColor,
...@@ -2150,11 +2077,35 @@ class _RenderChip extends RenderBox { ...@@ -2150,11 +2077,35 @@ class _RenderChip extends RenderBox {
if (enableAnimation == null || enableAnimation.isCompleted) { if (enableAnimation == null || enableAnimation.isCompleted) {
return Colors.white; return Colors.white;
} }
ColorTween enableTween;
switch (theme.brightness) {
case Brightness.light:
enableTween = new ColorTween(
begin: Colors.white.withAlpha(_kDisabledAlpha),
end: Colors.white,
);
break;
case Brightness.dark:
enableTween = new ColorTween(
begin: Colors.black.withAlpha(_kDisabledAlpha),
end: Colors.black,
);
break;
}
return enableTween.evaluate(enableAnimation); return enableTween.evaluate(enableAnimation);
} }
void _paintCheck(Canvas canvas, Offset origin, double size) { void _paintCheck(Canvas canvas, Offset origin, double size) {
Color paintColor = theme.showAvatar ? Colors.white : Colors.black87; Color paintColor;
switch (theme.brightness) {
case Brightness.light:
paintColor = theme.showAvatar ? Colors.white : Colors.black.withAlpha(_kCheckmarkAlpha);
break;
case Brightness.dark:
paintColor = theme.showAvatar ? Colors.black : Colors.white.withAlpha(_kCheckmarkAlpha);
break;
}
final ColorTween fadeTween = new ColorTween(begin: Colors.transparent, end: paintColor); final ColorTween fadeTween = new ColorTween(begin: Colors.transparent, end: paintColor);
paintColor = checkmarkAnimation.status == AnimationStatus.reverse paintColor = checkmarkAnimation.status == AnimationStatus.reverse
...@@ -2196,11 +2147,13 @@ class _RenderChip extends RenderBox { ...@@ -2196,11 +2147,13 @@ class _RenderChip extends RenderBox {
void _paintSelectionOverlay(PaintingContext context, Offset offset) { void _paintSelectionOverlay(PaintingContext context, Offset offset) {
if (isDrawingCheckmark) { if (isDrawingCheckmark) {
if (theme.showAvatar) {
final Rect avatarRect = _boxRect(avatar).shift(offset); final Rect avatarRect = _boxRect(avatar).shift(offset);
final Paint darkenPaint = new Paint() final Paint darkenPaint = new Paint()
..color = selectionScrimTween.evaluate(checkmarkAnimation) ..color = selectionScrimTween.evaluate(checkmarkAnimation)
..blendMode = BlendMode.srcATop; ..blendMode = BlendMode.srcATop;
context.canvas.drawRect(avatarRect, darkenPaint); context.canvas.drawRect(avatarRect, darkenPaint);
}
// Need to make the check mark be a little smaller than the avatar. // Need to make the check mark be a little smaller than the avatar.
final double checkSize = avatar.size.height * 0.75; final double checkSize = avatar.size.height * 0.75;
final Offset checkOffset = _boxParentData(avatar).offset + final Offset checkOffset = _boxParentData(avatar).offset +
...@@ -2218,14 +2171,15 @@ class _RenderChip extends RenderBox { ...@@ -2218,14 +2171,15 @@ class _RenderChip extends RenderBox {
if (theme.showAvatar == false && avatarDrawerAnimation.isDismissed) { if (theme.showAvatar == false && avatarDrawerAnimation.isDismissed) {
return; return;
} }
final int disabledColorAlpha = _disabledColor.alpha; final Color disabledColor = _disabledColor;
final int disabledColorAlpha = disabledColor.alpha;
if (needsCompositing) { if (needsCompositing) {
context.pushLayer(new OpacityLayer(alpha: disabledColorAlpha), paintWithOverlay, offset); context.pushLayer(new OpacityLayer(alpha: disabledColorAlpha), paintWithOverlay, offset);
} else { } else {
if (disabledColorAlpha != 0xff) { if (disabledColorAlpha != 0xff) {
context.canvas.saveLayer( context.canvas.saveLayer(
_boxRect(avatar).shift(offset).inflate(20.0), _boxRect(avatar).shift(offset).inflate(20.0),
new Paint()..color = _disabledColor, new Paint()..color = disabledColor,
); );
} }
paintWithOverlay(context, offset); paintWithOverlay(context, offset);
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'colors.dart';
import 'theme.dart';
import 'theme_data.dart';
/// Applies a chip theme to descendant [RawChip]-based widgets, like [Chip],
/// [InputChip], [ChoiceChip], [FilterChip], and [ActionChip].
///
/// A chip theme describes the color, shape and text styles for the chips it is
/// applied to
///
/// Descendant widgets obtain the current theme's [ChipThemeData] object using
/// [ChipTheme.of]. When a widget uses [ChipTheme.of], it is automatically
/// rebuilt if the theme later changes.
///
/// The [ThemeData] object given by the [Theme.of] call also contains a default
/// [Theme.chipTheme] that can be customized by copying it (using
/// [ChipThemeData.copyWith]).
///
/// See also:
///
/// * [Chip], a chip that displays information and can be deleted.
/// * [InputChip], a chip that represents a complex piece of information, such
/// as an entity (person, place, or thing) or conversational text, in a
/// compact form.
/// * [ChoiceChip], allows a single selection from a set of options. Choice
/// chips contain related descriptive text or categories.
/// * [FilterChip], uses tags or descriptive words as a way to filter content.
/// * [ActionChip], represents an action related to primary content.
/// * [ChipThemeData], which describes the actual configuration of a chip
/// theme.
/// * [ThemeData], which describes the overall theme information for the
/// application.
class ChipTheme extends InheritedWidget {
/// Applies the given theme [data] to [child].
///
/// The [data] and [child] arguments must not be null.
const ChipTheme({
Key key,
@required this.data,
@required Widget child,
}) : assert(child != null),
assert(data != null),
super(key: key, child: child);
/// Specifies the color, shape, and text style values for descendant chip
/// widgets.
final ChipThemeData data;
/// Returns the data from the closest [ChipTheme] instance that encloses
/// the given context.
///
/// Defaults to the ambient [ThemeData.chipTheme] if there is no
/// [ChipTheme] in the given build context.
///
/// ## Sample code
///
/// ```dart
/// class Spaceship extends StatelessWidget {
/// @override
/// Widget build(BuildContext context) {
/// return new ChipTheme(
/// data: ChipTheme.of(context).copyWith(backgroundColor: Colors.red),
/// child: new ActionChip(
/// label: const Text('Launch'),
/// onPressed: () { print('We have liftoff!'); },
/// ),
/// );
/// }
/// }
/// ```
///
/// See also:
///
/// * [ChipThemeData], which describes the actual configuration of a chip
/// theme.
static ChipThemeData of(BuildContext context) {
final ChipTheme inheritedTheme = context.inheritFromWidgetOfExactType(ChipTheme);
return inheritedTheme?.data ?? Theme.of(context).chipTheme;
}
@override
bool updateShouldNotify(ChipTheme oldWidget) => data != oldWidget.data;
}
/// Holds the color, shape, and text styles for a material design chip theme.
///
/// Use this class to configure a [ChipTheme] widget, or to set the
/// [ThemeData.chipTheme] for a [Theme] widget.
///
/// To obtain the current ambient chip theme, use [ChipTheme.of].
///
/// The parts of a chip are:
///
/// * The "avatar", which is a widget that appears at the beginning of the
/// chip. This is typically a [CircleAvatar] widget.
/// * The "label", which is the widget displayed in the center of the chip.
/// Typically this is a [Text] widget.
/// * The "delete icon", which is a widget that appears at the end of the chip.
/// * The chip is disabled when it is not accepting user input. Only some chips
/// have a disabled state: [InputChip], [ChoiceChip], and [FilterChip].
///
/// The simplest way to create a ChipThemeData is to use [copyWith] on the one
/// you get from [ChipTheme.of], or create an entirely new one with
/// [ChipThemeData..fromDefaults].
///
/// ## Sample code
///
/// ```dart
/// class CarColor extends StatefulWidget {
/// @override
/// State createState() => new _CarColorState();
/// }
///
/// class _CarColorState extends State<CarColor> {
/// Color _color = Colors.red;
///
/// @override
/// Widget build(BuildContext context) {
/// return new ChipTheme(
/// data: ChipTheme.of(context).copyWith(backgroundColor: Colors.lightBlue),
/// child: new ChoiceChip(
/// label: new Text('Light Blue'),
/// onSelected: (bool value) {
/// setState(() {
/// _color = value ? Colors.lightBlue : Colors.red;
/// });
/// },
/// selected: _color == Colors.lightBlue,
/// ),
/// );
/// }
/// }
/// ```
///
/// See also:
///
/// * [Chip], a chip that displays information and can be deleted.
/// * [InputChip], a chip that represents a complex piece of information, such
/// as an entity (person, place, or thing) or conversational text, in a
/// compact form.
/// * [ChoiceChip], allows a single selection from a set of options. Choice
/// chips contain related descriptive text or categories.
/// * [FilterChip], uses tags or descriptive words as a way to filter content.
/// * [ActionChip], represents an action related to primary content.
/// * [CircleAvatar], which shows images or initials of entities.
/// * [Wrap], A widget that displays its children in multiple horizontal or
/// vertical runs.
/// * [ChipTheme] widget, which can override the chip theme of its
/// children.
/// * [Theme] widget, which performs a similar function to [ChipTheme],
/// but for overall themes.
/// * [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.
///
/// This will rarely be used directly. It is used by [lerp] to
/// create intermediate themes based on two themes.
const ChipThemeData({
@required this.backgroundColor,
this.deleteIconColor,
@required this.disabledColor,
@required this.selectedColor,
@required this.secondarySelectedColor,
@required this.labelPadding,
@required this.padding,
@required this.shape,
@required this.labelStyle,
@required this.secondaryLabelStyle,
@required this.brightness,
}) : assert(backgroundColor != null),
assert(disabledColor != null),
assert(selectedColor != null),
assert(secondarySelectedColor != null),
assert(labelPadding != null),
assert(padding != null),
assert(shape != null),
assert(labelStyle != null),
assert(secondaryLabelStyle != null),
assert(brightness != null);
/// Generates a ChipThemeData from a brightness, a primary color, and a text
/// style.
///
/// The [brightness] is used to select a primary color from the default
/// values.
///
/// The optional [primaryColor] is used as the base color for the other
/// colors. The opacity of the [primaryColor] is ignored. If a [primaryColor]
/// is specified, then the [brightness] is ignored, and the theme brightness
/// is determined from the [primaryColor].
///
/// Only one of [primaryColor] or [brightness] may be specified.
///
/// The [secondaryColor] is used for the selection colors needed by
/// [ChoiceChip].
///
/// This is used to generate the default chip theme for a [ThemeData].
factory ChipThemeData.fromDefaults({
Brightness brightness,
Color primaryColor,
@required Color secondaryColor,
@required TextStyle labelStyle,
}) {
assert(primaryColor != null || brightness != null,
'One of primaryColor or brightness must be specified');
assert(primaryColor == null || brightness == null,
'Only one of primaryColor or brightness may be specified');
assert(secondaryColor != null);
assert(labelStyle != null);
if (primaryColor != null) {
brightness = ThemeData.estimateBrightnessForColor(primaryColor);
}
// These are Material Design defaults, and are used to derive
// component Colors (with opacity) from base colors.
const int backgroundAlpha = 0x1f; // 12%
const int deleteIconAlpha = 0xde; // 87%
const int disabledAlpha = 0x0c; // 38% * 12% = 5%
const int selectAlpha = 0x3d; // 12% + 12% = 24%
const int textLabelAlpha = 0xde; // 87%
const ShapeBorder shape = const StadiumBorder();
const EdgeInsetsGeometry labelPadding = const EdgeInsets.symmetric(horizontal: 8.0);
const EdgeInsetsGeometry padding = const EdgeInsets.all(4.0);
primaryColor = primaryColor ?? (brightness == Brightness.light ? Colors.black : Colors.white);
final Color backgroundColor = primaryColor.withAlpha(backgroundAlpha);
final Color deleteIconColor = primaryColor.withAlpha(deleteIconAlpha);
final Color disabledColor = primaryColor.withAlpha(disabledAlpha);
final Color selectedColor = primaryColor.withAlpha(selectAlpha);
final Color secondarySelectedColor = secondaryColor.withAlpha(selectAlpha);
final TextStyle secondaryLabelStyle = labelStyle.copyWith(
color: secondaryColor.withAlpha(textLabelAlpha),
);
labelStyle = labelStyle.copyWith(color: primaryColor.withAlpha(textLabelAlpha));
return new ChipThemeData(
backgroundColor: backgroundColor,
deleteIconColor: deleteIconColor,
disabledColor: disabledColor,
selectedColor: selectedColor,
secondarySelectedColor: secondarySelectedColor,
labelPadding: labelPadding,
padding: padding,
shape: shape,
labelStyle: labelStyle,
secondaryLabelStyle: secondaryLabelStyle,
brightness: brightness,
);
}
/// Color to be used for the unselected, enabled chip's background.
///
/// The default is light grey.
final Color backgroundColor;
/// The [Color] for the delete icon. The default is Color(0xde000000)
/// (slightly transparent black) for light themes, and Color(0xdeffffff)
/// (slightly transparent white) for dark themes.
///
/// May be set to null, in which case the ambient [IconTheme.color] is used.
final Color deleteIconColor;
/// Color to be used for the chip's background indicating that it is disabled.
///
/// The chip is disabled when [isEnabled] is false, or all three of
/// [SelectableChipAttributes.onSelected], [TappableChipAttributes.onPressed],
/// and [DeletableChipAttributes.onDelete] are null.
///
/// It defaults to [Colors.black38].
final Color disabledColor;
/// Color to be used for the chip's background, indicating that it is
/// selected.
///
/// The chip is selected when [selected] is true.
final Color selectedColor;
/// An alternate color to be used for the chip's background, indicating that
/// it is selected. For example, this color is used by [ChoiceChip] when the
/// choice is selected.
///
/// The chip is selected when [selected] is true.
final Color secondarySelectedColor;
/// The padding around the [label] widget.
///
/// By default, this is 4 logical pixels at the beginning and the end of the
/// label, and zero on top and bottom.
final EdgeInsetsGeometry labelPadding;
/// The padding between the contents of the chip and the outside [border].
///
/// Defaults to 4 logical pixels on all sides.
final EdgeInsetsGeometry padding;
/// The border to draw around the chip.
///
/// Defaults to a [StadiumBorder]. Must not be null.
final ShapeBorder shape;
/// The style to be applied to the chip's label.
///
/// This only has an effect on label widgets that respect the
/// [DefaultTextStyle], such as [Text].
final TextStyle labelStyle;
/// An alternate style to be applied to the chip's label. For example, this
/// style is applied to the text of a selected [ChoiceChip].
///
/// This only has an effect on label widgets that respect the
/// [DefaultTextStyle], such as [Text].
final TextStyle secondaryLabelStyle;
/// The brightness setting for this theme.
///
/// This affects various base material color choices in the chip rendering.
final Brightness brightness;
/// Creates a copy of this object but with the given fields replaced with the
/// new values.
ChipThemeData copyWith({
Color backgroundColor,
Color deleteIconColor,
Color disabledColor,
Color selectedColor,
Color secondarySelectedColor,
EdgeInsetsGeometry labelPadding,
EdgeInsetsGeometry padding,
ShapeBorder shape,
TextStyle labelStyle,
TextStyle secondaryLabelStyle,
Brightness brightness,
}) {
return new ChipThemeData(
backgroundColor: backgroundColor ?? this.backgroundColor,
deleteIconColor: deleteIconColor ?? this.deleteIconColor,
disabledColor: disabledColor ?? this.disabledColor,
selectedColor: selectedColor ?? this.selectedColor,
secondarySelectedColor: secondarySelectedColor ?? this.secondarySelectedColor,
labelPadding: labelPadding ?? this.labelPadding,
padding: padding ?? this.padding,
shape: shape ?? this.shape,
labelStyle: labelStyle ?? this.labelStyle,
secondaryLabelStyle: secondaryLabelStyle ?? this.secondaryLabelStyle,
brightness: brightness ?? this.brightness,
);
}
/// Linearly interpolate between two chip themes.
///
/// The arguments must not be null.
///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static ChipThemeData lerp(ChipThemeData a, ChipThemeData b, double t) {
assert(t != null);
if (a == null && b == null)
return null;
return new ChipThemeData(
backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t),
deleteIconColor: Color.lerp(a?.deleteIconColor, b?.deleteIconColor, t),
disabledColor: Color.lerp(a?.disabledColor, b?.disabledColor, t),
selectedColor: Color.lerp(a?.selectedColor, b?.selectedColor, t),
secondarySelectedColor: Color.lerp(a?.secondarySelectedColor, b?.secondarySelectedColor, 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),
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,
);
}
@override
int get hashCode {
return hashValues(
backgroundColor,
deleteIconColor,
disabledColor,
selectedColor,
secondarySelectedColor,
labelPadding,
padding,
shape,
labelStyle,
secondaryLabelStyle,
brightness,
);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) {
return true;
}
if (other.runtimeType != runtimeType) {
return false;
}
final ChipThemeData otherData = other;
return otherData.backgroundColor == backgroundColor
&& otherData.deleteIconColor == deleteIconColor
&& otherData.disabledColor == disabledColor
&& otherData.selectedColor == selectedColor
&& otherData.secondarySelectedColor == secondarySelectedColor
&& otherData.labelPadding == labelPadding
&& otherData.padding == padding
&& otherData.shape == shape
&& otherData.labelStyle == labelStyle
&& otherData.secondaryLabelStyle == secondaryLabelStyle
&& otherData.brightness == brightness;
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
final ThemeData defaultTheme = new ThemeData.fallback();
final ChipThemeData defaultData = new ChipThemeData.fromDefaults(
secondaryColor: defaultTheme.primaryColor,
brightness: defaultTheme.brightness,
labelStyle: defaultTheme.textTheme.body2,
);
properties.add(new DiagnosticsProperty<Color>('backgroundColor', backgroundColor, defaultValue: defaultData.backgroundColor));
properties.add(new DiagnosticsProperty<Color>('deleteIconColor', deleteIconColor, defaultValue: defaultData.deleteIconColor));
properties.add(new DiagnosticsProperty<Color>('disabledColor', disabledColor, defaultValue: defaultData.disabledColor));
properties.add(new DiagnosticsProperty<Color>('selectedColor', selectedColor, defaultValue: defaultData.selectedColor));
properties.add(new DiagnosticsProperty<Color>('secondarySelectedColor', secondarySelectedColor, defaultValue: defaultData.secondarySelectedColor));
properties.add(new DiagnosticsProperty<EdgeInsetsGeometry>('labelPadding', labelPadding, defaultValue: defaultData.labelPadding));
properties.add(new DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding, defaultValue: defaultData.padding));
properties.add(new DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: defaultData.shape));
properties.add(new DiagnosticsProperty<TextStyle>('labelStyle', labelStyle, defaultValue: defaultData.labelStyle));
properties.add(new DiagnosticsProperty<TextStyle>('secondaryLabelStyle', secondaryLabelStyle, defaultValue: defaultData.secondaryLabelStyle));
properties.add(new EnumProperty<Brightness>('brightness', brightness, defaultValue: defaultData.brightness));
}
}
...@@ -8,6 +8,7 @@ import 'package:flutter/foundation.dart'; ...@@ -8,6 +8,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'button_theme.dart'; import 'button_theme.dart';
import 'chip_theme.dart';
import 'colors.dart'; import 'colors.dart';
import 'ink_splash.dart'; import 'ink_splash.dart';
import 'ink_well.dart' show InteractiveInkFeatureFactory; import 'ink_well.dart' show InteractiveInkFeatureFactory;
...@@ -113,6 +114,7 @@ class ThemeData extends Diagnosticable { ...@@ -113,6 +114,7 @@ class ThemeData extends Diagnosticable {
IconThemeData primaryIconTheme, IconThemeData primaryIconTheme,
IconThemeData accentIconTheme, IconThemeData accentIconTheme,
SliderThemeData sliderTheme, SliderThemeData sliderTheme,
ChipThemeData chipTheme,
TargetPlatform platform, TargetPlatform platform,
}) { }) {
brightness ??= Brightness.light; brightness ??= Brightness.light;
...@@ -168,6 +170,11 @@ class ThemeData extends Diagnosticable { ...@@ -168,6 +170,11 @@ class ThemeData extends Diagnosticable {
primaryColorDark: primaryColorDark, primaryColorDark: primaryColorDark,
valueIndicatorTextStyle: accentTextTheme.body2, valueIndicatorTextStyle: accentTextTheme.body2,
); );
chipTheme ??= new ChipThemeData.fromDefaults(
secondaryColor: primaryColor,
brightness: brightness,
labelStyle: textTheme.body2,
);
return new ThemeData.raw( return new ThemeData.raw(
brightness: brightness, brightness: brightness,
primaryColor: primaryColor, primaryColor: primaryColor,
...@@ -205,6 +212,7 @@ class ThemeData extends Diagnosticable { ...@@ -205,6 +212,7 @@ class ThemeData extends Diagnosticable {
primaryIconTheme: primaryIconTheme, primaryIconTheme: primaryIconTheme,
accentIconTheme: accentIconTheme, accentIconTheme: accentIconTheme,
sliderTheme: sliderTheme, sliderTheme: sliderTheme,
chipTheme: chipTheme,
platform: platform, platform: platform,
); );
} }
...@@ -252,6 +260,7 @@ class ThemeData extends Diagnosticable { ...@@ -252,6 +260,7 @@ class ThemeData extends Diagnosticable {
@required this.primaryIconTheme, @required this.primaryIconTheme,
@required this.accentIconTheme, @required this.accentIconTheme,
@required this.sliderTheme, @required this.sliderTheme,
@required this.chipTheme,
@required this.platform, @required this.platform,
}) : assert(brightness != null), }) : assert(brightness != null),
assert(primaryColor != null), assert(primaryColor != null),
...@@ -288,6 +297,7 @@ class ThemeData extends Diagnosticable { ...@@ -288,6 +297,7 @@ class ThemeData extends Diagnosticable {
assert(primaryIconTheme != null), assert(primaryIconTheme != null),
assert(accentIconTheme != null), assert(accentIconTheme != null),
assert(sliderTheme != null), assert(sliderTheme != null),
assert(chipTheme != null),
assert(platform != null); assert(platform != null);
/// A default light blue theme. /// A default light blue theme.
...@@ -463,6 +473,11 @@ class ThemeData extends Diagnosticable { ...@@ -463,6 +473,11 @@ class ThemeData extends Diagnosticable {
/// This is the value returned from [SliderTheme.of]. /// This is the value returned from [SliderTheme.of].
final SliderThemeData sliderTheme; final SliderThemeData sliderTheme;
/// The colors and styles used to render [Chip], [
///
/// This is the value returned from [ChipTheme.of].
final ChipThemeData chipTheme;
/// The platform the material widgets should adapt to target. /// The platform the material widgets should adapt to target.
/// ///
/// Defaults to the current platform. /// Defaults to the current platform.
...@@ -506,6 +521,7 @@ class ThemeData extends Diagnosticable { ...@@ -506,6 +521,7 @@ class ThemeData extends Diagnosticable {
IconThemeData primaryIconTheme, IconThemeData primaryIconTheme,
IconThemeData accentIconTheme, IconThemeData accentIconTheme,
SliderThemeData sliderTheme, SliderThemeData sliderTheme,
ChipThemeData chipTheme,
TargetPlatform platform, TargetPlatform platform,
}) { }) {
return new ThemeData.raw( return new ThemeData.raw(
...@@ -545,6 +561,7 @@ class ThemeData extends Diagnosticable { ...@@ -545,6 +561,7 @@ class ThemeData extends Diagnosticable {
primaryIconTheme: primaryIconTheme ?? this.primaryIconTheme, primaryIconTheme: primaryIconTheme ?? this.primaryIconTheme,
accentIconTheme: accentIconTheme ?? this.accentIconTheme, accentIconTheme: accentIconTheme ?? this.accentIconTheme,
sliderTheme: sliderTheme ?? this.sliderTheme, sliderTheme: sliderTheme ?? this.sliderTheme,
chipTheme: chipTheme ?? this.chipTheme,
platform: platform ?? this.platform, platform: platform ?? this.platform,
); );
} }
...@@ -670,6 +687,7 @@ class ThemeData extends Diagnosticable { ...@@ -670,6 +687,7 @@ class ThemeData extends Diagnosticable {
primaryIconTheme: IconThemeData.lerp(a.primaryIconTheme, b.primaryIconTheme, t), primaryIconTheme: IconThemeData.lerp(a.primaryIconTheme, b.primaryIconTheme, t),
accentIconTheme: IconThemeData.lerp(a.accentIconTheme, b.accentIconTheme, t), accentIconTheme: IconThemeData.lerp(a.accentIconTheme, b.accentIconTheme, t),
sliderTheme: SliderThemeData.lerp(a.sliderTheme, b.sliderTheme, t), sliderTheme: SliderThemeData.lerp(a.sliderTheme, b.sliderTheme, t),
chipTheme: ChipThemeData.lerp(a.chipTheme, b.chipTheme, t),
platform: t < 0.5 ? a.platform : b.platform, platform: t < 0.5 ? a.platform : b.platform,
); );
} }
...@@ -713,6 +731,7 @@ class ThemeData extends Diagnosticable { ...@@ -713,6 +731,7 @@ class ThemeData extends Diagnosticable {
(otherData.primaryIconTheme == primaryIconTheme) && (otherData.primaryIconTheme == primaryIconTheme) &&
(otherData.accentIconTheme == accentIconTheme) && (otherData.accentIconTheme == accentIconTheme) &&
(otherData.sliderTheme == sliderTheme) && (otherData.sliderTheme == sliderTheme) &&
(otherData.chipTheme == chipTheme) &&
(otherData.platform == platform); (otherData.platform == platform);
} }
...@@ -754,6 +773,7 @@ class ThemeData extends Diagnosticable { ...@@ -754,6 +773,7 @@ class ThemeData extends Diagnosticable {
primaryIconTheme, primaryIconTheme,
accentIconTheme, accentIconTheme,
sliderTheme, sliderTheme,
chipTheme,
platform, platform,
), ),
); );
...@@ -797,6 +817,7 @@ class ThemeData extends Diagnosticable { ...@@ -797,6 +817,7 @@ class ThemeData extends Diagnosticable {
properties.add(new DiagnosticsProperty<IconThemeData>('primaryIconTheme', primaryIconTheme)); properties.add(new DiagnosticsProperty<IconThemeData>('primaryIconTheme', primaryIconTheme));
properties.add(new DiagnosticsProperty<IconThemeData>('accentIconTheme', accentIconTheme)); properties.add(new DiagnosticsProperty<IconThemeData>('accentIconTheme', accentIconTheme));
properties.add(new DiagnosticsProperty<SliderThemeData>('sliderTheme', sliderTheme)); properties.add(new DiagnosticsProperty<SliderThemeData>('sliderTheme', sliderTheme));
properties.add(new DiagnosticsProperty<ChipThemeData>('chipTheme', chipTheme));
} }
} }
......
...@@ -504,17 +504,58 @@ class TextStyle extends Diagnosticable { ...@@ -504,17 +504,58 @@ class TextStyle extends Diagnosticable {
/// Values for `t` are usually obtained from an [Animation<double>], such as /// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController]. /// an [AnimationController].
static TextStyle lerp(TextStyle a, TextStyle b, double t) { static TextStyle lerp(TextStyle a, TextStyle b, double t) {
assert(a != null);
assert(b != null);
assert(t != null); assert(t != null);
assert(a.inherit == b.inherit); assert(a == null || b == null || a.inherit == b.inherit);
if (a == null && b == null) {
return null;
}
String lerpDebugLabel; String lerpDebugLabel;
assert(() { assert(() {
lerpDebugLabel = 'lerp(${a.debugLabel ?? _kDefaultDebugLabel}${t.toStringAsFixed(1)}${b.debugLabel ?? _kDefaultDebugLabel})'; lerpDebugLabel = 'lerp(${a?.debugLabel ?? _kDefaultDebugLabel}${t.toStringAsFixed(1)}${b?.debugLabel ?? _kDefaultDebugLabel})';
return true; return true;
}()); }());
if (a == null) {
return new TextStyle(
inherit: b.inherit,
color: Color.lerp(null, b.color, t),
fontFamily: t < 0.5 ? null : b.fontFamily,
fontSize: t < 0.5 ? null : b.fontSize,
fontWeight: FontWeight.lerp(null, b.fontWeight, t),
fontStyle: t < 0.5 ? null : b.fontStyle,
letterSpacing: t < 0.5 ? null : b.letterSpacing,
wordSpacing: t < 0.5 ? null : b.wordSpacing,
textBaseline: t < 0.5 ? null : b.textBaseline,
height: t < 0.5 ? null : b.height,
locale: t < 0.5 ? null : b.locale,
decoration: t < 0.5 ? null : b.decoration,
decorationColor: Color.lerp(null, b.decorationColor, t),
decorationStyle: t < 0.5 ? null : b.decorationStyle,
debugLabel: lerpDebugLabel,
);
}
if (b == null) {
return new TextStyle(
inherit: a.inherit,
color: Color.lerp(a.color, null, t),
fontFamily: t < 0.5 ? a.fontFamily : null,
fontSize: t < 0.5 ? a.fontSize : null,
fontWeight: FontWeight.lerp(a.fontWeight, null, t),
fontStyle: t < 0.5 ? a.fontStyle : null,
letterSpacing: t < 0.5 ? a.letterSpacing : null,
wordSpacing: t < 0.5 ? a.wordSpacing : null,
textBaseline: t < 0.5 ? a.textBaseline : null,
height: t < 0.5 ? a.height : null,
locale: t < 0.5 ? a.locale : null,
decoration: t < 0.5 ? a.decoration : null,
decorationColor: Color.lerp(a.decorationColor, null, t),
decorationStyle: t < 0.5 ? a.decorationStyle : null,
debugLabel: lerpDebugLabel,
);
}
return new TextStyle( return new TextStyle(
inherit: b.inherit, inherit: b.inherit,
color: Color.lerp(a.color, b.color, t), color: Color.lerp(a.color, b.color, t),
......
...@@ -2,16 +2,49 @@ ...@@ -2,16 +2,49 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' show window;
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart'; import 'package:flutter/scheduler.dart';
import '../rendering/mock_canvas.dart';
import 'feedback_tester.dart'; import 'feedback_tester.dart';
Finder findRenderChipElement() { Finder findRenderChipElement() {
return find.byElementPredicate((Element e) => '${e.runtimeType}' == '_RenderChipElement'); return find.byElementPredicate((Element e) => '${e.runtimeType}' == '_RenderChipElement');
} }
RenderBox getMaterialBox(WidgetTester tester) {
return tester.firstRenderObject<RenderBox>(
find.descendant(
of: find.byType(RawChip),
matching: find.byType(CustomPaint),
),
);
}
IconThemeData getIconData(WidgetTester tester) {
final IconTheme iconTheme = tester.firstWidget(
find.descendant(
of: find.byType(RawChip),
matching: find.byType(IconTheme),
),
);
return iconTheme.data;
}
DefaultTextStyle getLabelStyle(WidgetTester tester) {
return tester.widget(
find
.descendant(
of: find.byType(RawChip),
matching: find.byType(DefaultTextStyle),
)
.last,
);
}
dynamic getRenderChip(WidgetTester tester) { dynamic getRenderChip(WidgetTester tester) {
if (!tester.any(findRenderChipElement())) { if (!tester.any(findRenderChipElement())) {
return null; return null;
...@@ -25,6 +58,30 @@ double getAvatarDrawerProgress(WidgetTester tester) => getRenderChip(tester)?.av ...@@ -25,6 +58,30 @@ double getAvatarDrawerProgress(WidgetTester tester) => getRenderChip(tester)?.av
double getDeleteDrawerProgress(WidgetTester tester) => getRenderChip(tester)?.deleteDrawerAnimation?.value; double getDeleteDrawerProgress(WidgetTester tester) => getRenderChip(tester)?.deleteDrawerAnimation?.value;
double getEnableProgress(WidgetTester tester) => getRenderChip(tester)?.enableAnimation?.value; double getEnableProgress(WidgetTester tester) => getRenderChip(tester)?.enableAnimation?.value;
/// Adds the basic requirements for a Chip.
Widget _wrapForChip({
Widget child,
TextDirection textDirection: TextDirection.ltr,
double textScaleFactor: 1.0,
}) {
return new MaterialApp(
home: new Localizations(
locale: const Locale('en', 'US'),
delegates: const <LocalizationsDelegate<dynamic>>[
DefaultWidgetsLocalizations.delegate,
DefaultMaterialLocalizations.delegate,
],
child: new Directionality(
textDirection: textDirection,
child: new MediaQuery(
data: new MediaQueryData.fromWindow(window).copyWith(textScaleFactor: textScaleFactor),
child: new Material(child: child),
),
),
),
);
}
/// Tests that a [Chip] that has its size constrained by its parent is /// Tests that a [Chip] that has its size constrained by its parent is
/// further constraining the size of its child, the label widget. /// further constraining the size of its child, the label widget.
/// Optionally, adding an avatar or delete icon to the chip should not /// Optionally, adding an avatar or delete icon to the chip should not
...@@ -41,8 +98,7 @@ Future<Null> _testConstrainedLabel( ...@@ -41,8 +98,7 @@ Future<Null> _testConstrainedLabel(
final Key labelKey = new UniqueKey(); final Key labelKey = new UniqueKey();
await tester.pumpWidget( await tester.pumpWidget(
new MaterialApp( _wrapForChip(
home: new Material(
child: new Center( child: new Center(
child: new Container( child: new Container(
width: chipParentWidth, width: chipParentWidth,
...@@ -59,7 +115,6 @@ Future<Null> _testConstrainedLabel( ...@@ -59,7 +115,6 @@ Future<Null> _testConstrainedLabel(
), ),
), ),
), ),
),
); );
final Size labelSize = tester.getSize(find.byKey(labelKey)); final Size labelSize = tester.getSize(find.byKey(labelKey));
...@@ -75,9 +130,10 @@ void main() { ...@@ -75,9 +130,10 @@ void main() {
testWidgets('Chip control test', (WidgetTester tester) async { testWidgets('Chip control test', (WidgetTester tester) async {
final FeedbackTester feedback = new FeedbackTester(); final FeedbackTester feedback = new FeedbackTester();
final List<String> deletedChipLabels = <String>[]; final List<String> deletedChipLabels = <String>[];
await tester.pumpWidget(new MaterialApp( await tester.pumpWidget(
home: new Material( _wrapForChip(
child: new Column(children: <Widget>[ child: new Column(
children: <Widget>[
new Chip( new Chip(
avatar: const CircleAvatar(child: const Text('A')), avatar: const CircleAvatar(child: const Text('A')),
label: const Text('Chip A'), label: const Text('Chip A'),
...@@ -94,7 +150,10 @@ void main() { ...@@ -94,7 +150,10 @@ void main() {
}, },
deleteButtonTooltipMessage: 'Delete chip B', deleteButtonTooltipMessage: 'Delete chip B',
), ),
])))); ],
),
),
);
expect(tester.widget(find.byTooltip('Delete chip A')), isNotNull); expect(tester.widget(find.byTooltip('Delete chip A')), isNotNull);
expect(tester.widget(find.byTooltip('Delete chip B')), isNotNull); expect(tester.widget(find.byTooltip('Delete chip B')), isNotNull);
...@@ -125,9 +184,8 @@ void main() { ...@@ -125,9 +184,8 @@ void main() {
final Key labelKey = new UniqueKey(); final Key labelKey = new UniqueKey();
await tester.pumpWidget( await tester.pumpWidget(
new Material( _wrapForChip(
child: new MaterialApp( child: new Center(
home: new Center(
child: new Container( child: new Container(
width: 500.0, width: 500.0,
height: 500.0, height: 500.0,
...@@ -145,7 +203,6 @@ void main() { ...@@ -145,7 +203,6 @@ void main() {
), ),
), ),
), ),
),
); );
final Size labelSize = tester.getSize(find.byKey(labelKey)); final Size labelSize = tester.getSize(find.byKey(labelKey));
...@@ -190,41 +247,35 @@ void main() { ...@@ -190,41 +247,35 @@ void main() {
testWidgets('Chip in row works ok', (WidgetTester tester) async { testWidgets('Chip in row works ok', (WidgetTester tester) async {
const TextStyle style = const TextStyle(fontFamily: 'Ahem', fontSize: 10.0); const TextStyle style = const TextStyle(fontFamily: 'Ahem', fontSize: 10.0);
await tester.pumpWidget( await tester.pumpWidget(
new MaterialApp( _wrapForChip(
home: new Material(
child: new Row( child: new Row(
children: const <Widget>[ children: const <Widget>[
const Chip(label: const Text('Test'), labelStyle: style), const Chip(label: const Text('Test'), labelStyle: style),
], ],
), ),
), ),
),
); );
expect(tester.getSize(find.byType(Text)), const Size(40.0, 10.0)); expect(tester.getSize(find.byType(Text)), const Size(40.0, 10.0));
expect(tester.getSize(find.byType(Chip)), const Size(64.0, 32.0)); expect(tester.getSize(find.byType(Chip)), const Size(64.0, 32.0));
await tester.pumpWidget( await tester.pumpWidget(
new MaterialApp( _wrapForChip(
home: new Material(
child: new Row( child: new Row(
children: const <Widget>[ children: const <Widget>[
const Flexible(child: const Chip(label: const Text('Test'), labelStyle: style)), const Flexible(child: const Chip(label: const Text('Test'), labelStyle: style)),
], ],
), ),
), ),
),
); );
expect(tester.getSize(find.byType(Text)), const Size(40.0, 10.0)); expect(tester.getSize(find.byType(Text)), const Size(40.0, 10.0));
expect(tester.getSize(find.byType(Chip)), const Size(64.0, 32.0)); expect(tester.getSize(find.byType(Chip)), const Size(64.0, 32.0));
await tester.pumpWidget( await tester.pumpWidget(
new MaterialApp( _wrapForChip(
home: new Material(
child: new Row( child: new Row(
children: const <Widget>[ children: const <Widget>[
const Expanded(child: const Chip(label: const Text('Test'), labelStyle: style)), const Expanded(child: const Chip(label: const Text('Test'), labelStyle: style)),
], ],
), ),
), ),
),
); );
expect(tester.getSize(find.byType(Text)), const Size(40.0, 10.0)); expect(tester.getSize(find.byType(Text)), const Size(40.0, 10.0));
expect(tester.getSize(find.byType(Chip)), const Size(800.0, 32.0)); expect(tester.getSize(find.byType(Chip)), const Size(800.0, 32.0));
...@@ -249,32 +300,18 @@ void main() { ...@@ -249,32 +300,18 @@ void main() {
); );
await tester.pumpWidget( await tester.pumpWidget(
new Localizations( _wrapForChip(
locale: const Locale('en', 'US'),
delegates: const <LocalizationsDelegate<dynamic>>[
DefaultWidgetsLocalizations.delegate,
DefaultMaterialLocalizations.delegate,
],
child: new Directionality(
textDirection: TextDirection.rtl,
child: test, child: test,
), textDirection: TextDirection.rtl,
), ),
); );
await tester.pumpAndSettle(const Duration(milliseconds: 500)); await tester.pumpAndSettle(const Duration(milliseconds: 500));
expect(tester.getCenter(find.text('ABC')).dx, greaterThan(tester.getCenter(find.byKey(iconKey)).dx)); expect(tester.getCenter(find.text('ABC')).dx, greaterThan(tester.getCenter(find.byKey(iconKey)).dx));
await tester.pumpWidget( await tester.pumpWidget(
new Localizations( _wrapForChip(
locale: const Locale('en', 'US'),
delegates: const <LocalizationsDelegate<dynamic>>[
DefaultWidgetsLocalizations.delegate,
DefaultMaterialLocalizations.delegate,
],
child: new Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: test, child: test,
), ),
),
); );
await tester.pumpAndSettle(const Duration(milliseconds: 500)); await tester.pumpAndSettle(const Duration(milliseconds: 500));
expect(tester.getCenter(find.text('ABC')).dx, lessThan(tester.getCenter(find.byKey(iconKey)).dx)); expect(tester.getCenter(find.text('ABC')).dx, lessThan(tester.getCenter(find.byKey(iconKey)).dx));
...@@ -282,8 +319,7 @@ void main() { ...@@ -282,8 +319,7 @@ void main() {
testWidgets('Chip responds to textScaleFactor', (WidgetTester tester) async { testWidgets('Chip responds to textScaleFactor', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
new MaterialApp( _wrapForChip(
home: new Material(
child: new Column( child: new Column(
children: const <Widget>[ children: const <Widget>[
const Chip( const Chip(
...@@ -297,7 +333,6 @@ void main() { ...@@ -297,7 +333,6 @@ void main() {
], ],
), ),
), ),
),
); );
// TODO(gspencer): Update this test when the font metric bug is fixed to remove the anyOfs. // TODO(gspencer): Update this test when the font metric bug is fixed to remove the anyOfs.
...@@ -314,10 +349,8 @@ void main() { ...@@ -314,10 +349,8 @@ void main() {
expect(tester.getSize(find.byType(Chip).last), anyOf(const Size(132.0, 32.0), const Size(131.0, 32.0))); expect(tester.getSize(find.byType(Chip).last), anyOf(const Size(132.0, 32.0), const Size(131.0, 32.0)));
await tester.pumpWidget( await tester.pumpWidget(
new MaterialApp( _wrapForChip(
home: new MediaQuery( textScaleFactor: 3.0,
data: const MediaQueryData(textScaleFactor: 3.0),
child: new Material(
child: new Column( child: new Column(
children: const <Widget>[ children: const <Widget>[
const Chip( const Chip(
...@@ -331,8 +364,6 @@ void main() { ...@@ -331,8 +364,6 @@ void main() {
], ],
), ),
), ),
),
),
); );
// TODO(gspencer): Update this test when the font metric bug is fixed to remove the anyOfs. // TODO(gspencer): Update this test when the font metric bug is fixed to remove the anyOfs.
...@@ -346,8 +377,7 @@ void main() { ...@@ -346,8 +377,7 @@ void main() {
// Check that individual text scales are taken into account. // Check that individual text scales are taken into account.
await tester.pumpWidget( await tester.pumpWidget(
new MaterialApp( _wrapForChip(
home: new Material(
child: new Column( child: new Column(
children: const <Widget>[ children: const <Widget>[
const Chip( const Chip(
...@@ -361,7 +391,6 @@ void main() { ...@@ -361,7 +391,6 @@ void main() {
], ],
), ),
), ),
),
); );
// TODO(gspencer): Update this test when the font metric bug is fixed to remove the anyOfs. // TODO(gspencer): Update this test when the font metric bug is fixed to remove the anyOfs.
...@@ -377,8 +406,7 @@ void main() { ...@@ -377,8 +406,7 @@ void main() {
final Key keyA = new GlobalKey(); final Key keyA = new GlobalKey();
final Key keyB = new GlobalKey(); final Key keyB = new GlobalKey();
await tester.pumpWidget( await tester.pumpWidget(
new MaterialApp( _wrapForChip(
home: new Material(
child: new Column( child: new Column(
children: <Widget>[ children: <Widget>[
new Chip( new Chip(
...@@ -392,7 +420,6 @@ void main() { ...@@ -392,7 +420,6 @@ void main() {
], ],
), ),
), ),
),
); );
// TODO(gspencer): Update this test when the font metric bug is fixed to remove the anyOfs. // TODO(gspencer): Update this test when the font metric bug is fixed to remove the anyOfs.
...@@ -412,8 +439,7 @@ void main() { ...@@ -412,8 +439,7 @@ void main() {
testWidgets('Avatars can be non-circle avatar widgets', (WidgetTester tester) async { testWidgets('Avatars can be non-circle avatar widgets', (WidgetTester tester) async {
final Key keyA = new GlobalKey(); final Key keyA = new GlobalKey();
await tester.pumpWidget( await tester.pumpWidget(
new MaterialApp( _wrapForChip(
home: new Material(
child: new Column( child: new Column(
children: <Widget>[ children: <Widget>[
new Chip( new Chip(
...@@ -423,7 +449,6 @@ void main() { ...@@ -423,7 +449,6 @@ void main() {
], ],
), ),
), ),
),
); );
expect(tester.getSize(find.byKey(keyA)), equals(const Size(20.0, 20.0))); expect(tester.getSize(find.byKey(keyA)), equals(const Size(20.0, 20.0)));
...@@ -432,8 +457,7 @@ void main() { ...@@ -432,8 +457,7 @@ void main() {
testWidgets('Delete icons can be non-icon widgets', (WidgetTester tester) async { testWidgets('Delete icons can be non-icon widgets', (WidgetTester tester) async {
final Key keyA = new GlobalKey(); final Key keyA = new GlobalKey();
await tester.pumpWidget( await tester.pumpWidget(
new MaterialApp( _wrapForChip(
home: new Material(
child: new Column( child: new Column(
children: <Widget>[ children: <Widget>[
new Chip( new Chip(
...@@ -444,7 +468,6 @@ void main() { ...@@ -444,7 +468,6 @@ void main() {
], ],
), ),
), ),
),
); );
expect(tester.getSize(find.byKey(keyA)), equals(const Size(20.0, 20.0))); expect(tester.getSize(find.byKey(keyA)), equals(const Size(20.0, 20.0)));
...@@ -454,13 +477,7 @@ void main() { ...@@ -454,13 +477,7 @@ void main() {
final GlobalKey keyA = new GlobalKey(); final GlobalKey keyA = new GlobalKey();
final GlobalKey keyB = new GlobalKey(); final GlobalKey keyB = new GlobalKey();
await tester.pumpWidget( await tester.pumpWidget(
new Localizations( _wrapForChip(
locale: const Locale('en', 'US'),
delegates: const <LocalizationsDelegate<dynamic>>[
DefaultWidgetsLocalizations.delegate,
DefaultMaterialLocalizations.delegate,
],
child: new Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: new Overlay( child: new Overlay(
initialEntries: <OverlayEntry>[ initialEntries: <OverlayEntry>[
...@@ -484,7 +501,6 @@ void main() { ...@@ -484,7 +501,6 @@ void main() {
], ],
), ),
), ),
),
); );
expect(tester.getTopLeft(find.byKey(keyA)), const Offset(332.0, 280.0)); expect(tester.getTopLeft(find.byKey(keyA)), const Offset(332.0, 280.0));
expect(tester.getBottomRight(find.byKey(keyA)), const Offset(372.0, 320.0)); expect(tester.getBottomRight(find.byKey(keyA)), const Offset(372.0, 320.0));
...@@ -498,13 +514,7 @@ void main() { ...@@ -498,13 +514,7 @@ void main() {
final GlobalKey keyA = new GlobalKey(); final GlobalKey keyA = new GlobalKey();
final GlobalKey keyB = new GlobalKey(); final GlobalKey keyB = new GlobalKey();
await tester.pumpWidget( await tester.pumpWidget(
new Localizations( _wrapForChip(
locale: const Locale('en', 'US'),
delegates: const <LocalizationsDelegate<dynamic>>[
DefaultWidgetsLocalizations.delegate,
DefaultMaterialLocalizations.delegate,
],
child: new Directionality(
textDirection: TextDirection.rtl, textDirection: TextDirection.rtl,
child: new Overlay( child: new Overlay(
initialEntries: <OverlayEntry>[ initialEntries: <OverlayEntry>[
...@@ -528,7 +538,6 @@ void main() { ...@@ -528,7 +538,6 @@ void main() {
], ],
), ),
), ),
),
); );
expect(tester.getTopLeft(find.byKey(keyA)), const Offset(428.0, 280.0)); expect(tester.getTopLeft(find.byKey(keyA)), const Offset(428.0, 280.0));
...@@ -543,8 +552,7 @@ void main() { ...@@ -543,8 +552,7 @@ void main() {
final GlobalKey labelKey = new GlobalKey(); final GlobalKey labelKey = new GlobalKey();
Future<Null> pushChip({Widget avatar}) async { Future<Null> pushChip({Widget avatar}) async {
return tester.pumpWidget( return tester.pumpWidget(
new MaterialApp( _wrapForChip(
home: new Material(
child: new Wrap( child: new Wrap(
children: <Widget>[ children: <Widget>[
new RawChip( new RawChip(
...@@ -555,7 +563,6 @@ void main() { ...@@ -555,7 +563,6 @@ void main() {
], ],
), ),
), ),
),
); );
} }
...@@ -659,8 +666,7 @@ void main() { ...@@ -659,8 +666,7 @@ void main() {
bool wasDeleted = false; bool wasDeleted = false;
Future<Null> pushChip({bool deletable: false}) async { Future<Null> pushChip({bool deletable: false}) async {
return tester.pumpWidget( return tester.pumpWidget(
new MaterialApp( _wrapForChip(
home: new Material(
child: new Wrap( child: new Wrap(
children: <Widget>[ children: <Widget>[
new StatefulBuilder(builder: (BuildContext context, StateSetter setState) { new StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
...@@ -680,7 +686,6 @@ void main() { ...@@ -680,7 +686,6 @@ void main() {
], ],
), ),
), ),
),
); );
} }
...@@ -776,8 +781,7 @@ void main() { ...@@ -776,8 +781,7 @@ void main() {
final UniqueKey labelKey = new UniqueKey(); final UniqueKey labelKey = new UniqueKey();
Future<Null> pushChip({Widget avatar, bool selectable: false}) async { Future<Null> pushChip({Widget avatar, bool selectable: false}) async {
return tester.pumpWidget( return tester.pumpWidget(
new MaterialApp( _wrapForChip(
home: new Material(
child: new Wrap( child: new Wrap(
children: <Widget>[ children: <Widget>[
new StatefulBuilder(builder: (BuildContext context, StateSetter setState) { new StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
...@@ -801,7 +805,6 @@ void main() { ...@@ -801,7 +805,6 @@ void main() {
], ],
), ),
), ),
),
); );
} }
...@@ -861,8 +864,7 @@ void main() { ...@@ -861,8 +864,7 @@ void main() {
final UniqueKey labelKey = new UniqueKey(); final UniqueKey labelKey = new UniqueKey();
Future<Null> pushChip({bool selectable: false}) async { Future<Null> pushChip({bool selectable: false}) async {
return tester.pumpWidget( return tester.pumpWidget(
new MaterialApp( _wrapForChip(
home: new Material(
child: new Wrap( child: new Wrap(
children: <Widget>[ children: <Widget>[
new StatefulBuilder(builder: (BuildContext context, StateSetter setState) { new StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
...@@ -885,7 +887,6 @@ void main() { ...@@ -885,7 +887,6 @@ void main() {
], ],
), ),
), ),
),
); );
} }
...@@ -939,8 +940,7 @@ void main() { ...@@ -939,8 +940,7 @@ void main() {
final UniqueKey labelKey = new UniqueKey(); final UniqueKey labelKey = new UniqueKey();
Future<Null> pushChip({Widget avatar, bool selectable: false}) async { Future<Null> pushChip({Widget avatar, bool selectable: false}) async {
return tester.pumpWidget( return tester.pumpWidget(
new MaterialApp( _wrapForChip(
home: new Material(
child: new Wrap( child: new Wrap(
children: <Widget>[ children: <Widget>[
new StatefulBuilder(builder: (BuildContext context, StateSetter setState) { new StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
...@@ -964,7 +964,6 @@ void main() { ...@@ -964,7 +964,6 @@ void main() {
], ],
), ),
), ),
),
); );
} }
...@@ -993,4 +992,157 @@ void main() { ...@@ -993,4 +992,157 @@ void main() {
expect(getDeleteDrawerProgress(tester), equals(0.0)); expect(getDeleteDrawerProgress(tester), equals(0.0));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
}); });
testWidgets('Chip uses ThemeData chip theme if present', (WidgetTester tester) async {
final ThemeData theme = new ThemeData(
platform: TargetPlatform.android,
primarySwatch: Colors.red,
);
final ChipThemeData chipTheme = theme.chipTheme;
Widget buildChip(ChipThemeData data) {
return _wrapForChip(
textDirection: TextDirection.ltr,
child: new Theme(
data: theme,
child: const InputChip(
label: const Text('Label'),
),
),
);
}
await tester.pumpWidget(buildChip(chipTheme));
final RenderBox materialBox = tester.firstRenderObject<RenderBox>(
find.descendant(
of: find.byType(RawChip),
matching: find.byType(CustomPaint),
),
);
expect(materialBox, paints..path(color: chipTheme.disabledColor));
});
testWidgets('Chip uses the right theme colors for the right components', (WidgetTester tester) async {
final ThemeData themeData = new ThemeData(
platform: TargetPlatform.android,
primarySwatch: Colors.blue,
);
final ChipThemeData chipTheme = themeData.chipTheme;
bool value = false;
Widget buildApp({
ChipThemeData theme,
Widget avatar,
Widget deleteIcon,
bool isSelectable: true,
bool isPressable: false,
bool isDeletable: true,
bool showCheckmark: true,
}) {
theme ??= chipTheme;
return _wrapForChip(
child: new Theme(
data: themeData,
child: new ChipTheme(
data: theme,
child: new StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
return new RawChip(
showCheckmark: showCheckmark,
onDeleted: isDeletable ? () {} : null,
tapEnabled: true,
avatar: avatar,
deleteIcon: deleteIcon,
isEnabled: isSelectable || isPressable,
shape: theme.shape,
selected: isSelectable ? value : null,
label: new Text('$value'),
onSelected: isSelectable
? (bool newValue) {
setState(() {
value = newValue;
});
}
: null,
onPressed: isPressable
? () {
setState(() {
value = true;
});
}
: null,
);
}),
),
),
);
}
await tester.pumpWidget(buildApp());
RenderBox materialBox = getMaterialBox(tester);
IconThemeData iconData = getIconData(tester);
DefaultTextStyle labelStyle = getLabelStyle(tester);
// Check default theme for enabled widget.
expect(materialBox, paints..path(color: chipTheme.backgroundColor));
expect(iconData.color, equals(const Color(0xde000000)));
expect(labelStyle.style.color, equals(Colors.black.withAlpha(0xde)));
await tester.tap(find.byType(RawChip));
await tester.pumpAndSettle();
materialBox = getMaterialBox(tester);
expect(materialBox, paints..path(color: chipTheme.selectedColor));
await tester.tap(find.byType(RawChip));
await tester.pumpAndSettle();
// Check default theme with disabled widget.
await tester.pumpWidget(buildApp(isSelectable: false, isPressable: false, isDeletable: true));
await tester.pumpAndSettle();
materialBox = getMaterialBox(tester);
labelStyle = getLabelStyle(tester);
expect(materialBox, paints..path(color: chipTheme.disabledColor));
expect(labelStyle.style.color, equals(Colors.black.withAlpha(0xde)));
// Apply a custom theme.
const Color customColor1 = const Color(0xcafefeed);
const Color customColor2 = const Color(0xdeadbeef);
const Color customColor3 = const Color(0xbeefcafe);
const Color customColor4 = const Color(0xaddedabe);
final ChipThemeData customTheme = chipTheme.copyWith(
brightness: Brightness.dark,
backgroundColor: customColor1,
disabledColor: customColor2,
selectedColor: customColor3,
deleteIconColor: customColor4,
);
await tester.pumpWidget(buildApp(theme: customTheme));
await tester.pumpAndSettle();
materialBox = getMaterialBox(tester);
iconData = getIconData(tester);
labelStyle = getLabelStyle(tester);
// Check custom theme for enabled widget.
expect(materialBox, paints..path(color: customTheme.backgroundColor));
expect(iconData.color, equals(customTheme.deleteIconColor));
expect(labelStyle.style.color, equals(Colors.black.withAlpha(0xde)));
await tester.tap(find.byType(RawChip));
await tester.pumpAndSettle();
materialBox = getMaterialBox(tester);
expect(materialBox, paints..path(color: customTheme.selectedColor));
await tester.tap(find.byType(RawChip));
await tester.pumpAndSettle();
// Check custom theme with disabled widget.
await tester.pumpWidget(buildApp(
theme: customTheme,
isSelectable: false,
isPressable: false,
isDeletable: true,
));
await tester.pumpAndSettle();
materialBox = getMaterialBox(tester);
labelStyle = getLabelStyle(tester);
expect(materialBox, paints..path(color: customTheme.disabledColor));
expect(labelStyle.style.color, equals(Colors.black.withAlpha(0xde)));
});
} }
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui' show window;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/painting.dart';
import '../rendering/mock_canvas.dart';
RenderBox getMaterialBox(WidgetTester tester) {
return tester.firstRenderObject<RenderBox>(
find.descendant(
of: find.byType(RawChip),
matching: find.byType(CustomPaint),
),
);
}
IconThemeData getIconData(WidgetTester tester) {
final IconTheme iconTheme = tester.firstWidget(
find.descendant(
of: find.byType(RawChip),
matching: find.byType(IconTheme),
),
);
return iconTheme.data;
}
DefaultTextStyle getLabelStyle(WidgetTester tester) {
return tester.widget(
find
.descendant(
of: find.byType(RawChip),
matching: find.byType(DefaultTextStyle),
)
.last,
);
}
void main() {
testWidgets('Chip theme is built by ThemeData', (WidgetTester tester) async {
final ThemeData theme = new ThemeData(
platform: TargetPlatform.android,
primarySwatch: Colors.red,
);
final ChipThemeData chipTheme = theme.chipTheme;
expect(chipTheme.backgroundColor, equals(Colors.black.withAlpha(0x1f)));
expect(chipTheme.selectedColor, equals(Colors.black.withAlpha(0x3d)));
expect(chipTheme.deleteIconColor, equals(Colors.black.withAlpha(0xde)));
});
testWidgets('Chip uses ThemeData chip theme if present', (WidgetTester tester) async {
final ThemeData theme = new ThemeData(
platform: TargetPlatform.android,
primarySwatch: Colors.red,
backgroundColor: Colors.blue,
);
final ChipThemeData chipTheme = theme.chipTheme;
bool value;
Widget buildChip(ChipThemeData data) {
return new Directionality(
textDirection: TextDirection.ltr,
child: new MediaQuery(
data: new MediaQueryData.fromWindow(window),
child: new Material(
child: new Center(
child: new Theme(
data: theme,
child: new RawChip(
showCheckmark: true,
onDeleted: () {},
tapEnabled: true,
avatar: const Placeholder(),
deleteIcon: const Placeholder(),
isEnabled: true,
selected: value,
label: new Text('$value'),
onSelected: (bool newValue) {},
onPressed: null,
),
),
),
),
),
);
}
await tester.pumpWidget(buildChip(chipTheme));
await tester.pumpAndSettle();
final RenderBox materialBox = getMaterialBox(tester);
expect(materialBox, paints..path(color: chipTheme.backgroundColor));
});
testWidgets('Chip overrides ThemeData theme if ChipTheme present', (WidgetTester tester) async {
final ThemeData theme = new ThemeData(
platform: TargetPlatform.android,
primarySwatch: Colors.red,
);
final ChipThemeData chipTheme = theme.chipTheme;
final ChipThemeData customTheme = chipTheme.copyWith(
backgroundColor: Colors.purple,
deleteIconColor: Colors.purple.withAlpha(0x3d),
);
const bool value = false;
Widget buildChip(ChipThemeData data) {
return new Directionality(
textDirection: TextDirection.ltr,
child: new MediaQuery(
data: new MediaQueryData.fromWindow(window),
child: new Material(
child: new Center(
child: new Theme(
data: theme,
child: new ChipTheme(
data: customTheme,
child: new RawChip(
showCheckmark: true,
onDeleted: () {},
tapEnabled: true,
avatar: const Placeholder(),
deleteIcon: const Placeholder(),
isEnabled: true,
selected: value,
label: const Text('$value'),
onSelected: (bool newValue) {},
onPressed: null,
),
),
),
),
),
),
);
}
await tester.pumpWidget(buildChip(chipTheme));
await tester.pumpAndSettle();
final RenderBox materialBox = getMaterialBox(tester);
expect(materialBox, paints..path(color: new Color(customTheme.backgroundColor.value)));
});
testWidgets('ChipThemeData generates correct opacities for defaults', (WidgetTester tester) async {
const Color customColor1 = const Color(0xcafefeed);
const Color customColor2 = const Color(0xdeadbeef);
final TextStyle customStyle = new ThemeData.fallback().accentTextTheme.body2.copyWith(color: customColor2);
final ChipThemeData lightTheme = new ChipThemeData.fromDefaults(
secondaryColor: customColor1,
brightness: Brightness.light,
labelStyle: customStyle,
);
expect(lightTheme.backgroundColor, equals(Colors.black.withAlpha(0x1f)));
expect(lightTheme.deleteIconColor, equals(Colors.black.withAlpha(0xde)));
expect(lightTheme.disabledColor, equals(Colors.black.withAlpha(0x0c)));
expect(lightTheme.selectedColor, equals(Colors.black.withAlpha(0x3d)));
expect(lightTheme.secondarySelectedColor, equals(customColor1.withAlpha(0x3d)));
expect(lightTheme.labelPadding, equals(const EdgeInsets.symmetric(horizontal: 8.0)));
expect(lightTheme.padding, equals(const EdgeInsets.all(4.0)));
expect(lightTheme.shape, equals(const isInstanceOf<StadiumBorder>()));
expect(lightTheme.labelStyle.color, equals(Colors.black.withAlpha(0xde)));
expect(lightTheme.secondaryLabelStyle.color, equals(customColor1.withAlpha(0xde)));
expect(lightTheme.brightness, equals(Brightness.light));
final ChipThemeData darkTheme = new ChipThemeData.fromDefaults(
secondaryColor: customColor1,
brightness: Brightness.dark,
labelStyle: customStyle,
);
expect(darkTheme.backgroundColor, equals(Colors.white.withAlpha(0x1f)));
expect(darkTheme.deleteIconColor, equals(Colors.white.withAlpha(0xde)));
expect(darkTheme.disabledColor, equals(Colors.white.withAlpha(0x0c)));
expect(darkTheme.selectedColor, equals(Colors.white.withAlpha(0x3d)));
expect(darkTheme.secondarySelectedColor, equals(customColor1.withAlpha(0x3d)));
expect(darkTheme.labelPadding, equals(const EdgeInsets.symmetric(horizontal: 8.0)));
expect(darkTheme.padding, equals(const EdgeInsets.all(4.0)));
expect(darkTheme.shape, equals(const isInstanceOf<StadiumBorder>()));
expect(darkTheme.labelStyle.color, equals(Colors.white.withAlpha(0xde)));
expect(darkTheme.secondaryLabelStyle.color, equals(customColor1.withAlpha(0xde)));
expect(darkTheme.brightness, equals(Brightness.dark));
final ChipThemeData customTheme = new ChipThemeData.fromDefaults(
primaryColor: customColor1,
secondaryColor: customColor2,
labelStyle: customStyle,
);
expect(customTheme.backgroundColor, equals(customColor1.withAlpha(0x1f)));
expect(customTheme.deleteIconColor, equals(customColor1.withAlpha(0xde)));
expect(customTheme.disabledColor, equals(customColor1.withAlpha(0x0c)));
expect(customTheme.selectedColor, equals(customColor1.withAlpha(0x3d)));
expect(customTheme.secondarySelectedColor, equals(customColor2.withAlpha(0x3d)));
expect(customTheme.labelPadding, equals(const EdgeInsets.symmetric(horizontal: 8.0)));
expect(customTheme.padding, equals(const EdgeInsets.all(4.0)));
expect(customTheme.shape, equals(const isInstanceOf<StadiumBorder>()));
expect(customTheme.labelStyle.color, equals(customColor1.withAlpha(0xde)));
expect(customTheme.secondaryLabelStyle.color, equals(customColor2.withAlpha(0xde)));
expect(customTheme.brightness, equals(Brightness.light));
});
testWidgets('ChipThemeData lerps correctly', (WidgetTester tester) async {
final ChipThemeData chipThemeBlack = new ChipThemeData.fromDefaults(
secondaryColor: Colors.black,
brightness: Brightness.dark,
labelStyle: new ThemeData.fallback().accentTextTheme.body2.copyWith(color: Colors.black),
);
final ChipThemeData chipThemeWhite = new ChipThemeData.fromDefaults(
secondaryColor: Colors.white,
brightness: Brightness.light,
labelStyle: new 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));
final ChipThemeData lerp = ChipThemeData.lerp(chipThemeBlack, chipThemeWhite, 0.5);
const Color middleGrey = const Color(0xff7f7f7f);
expect(lerp.backgroundColor, equals(middleGrey.withAlpha(0x1f)));
expect(lerp.deleteIconColor, equals(middleGrey.withAlpha(0xde)));
expect(lerp.disabledColor, equals(middleGrey.withAlpha(0x0c)));
expect(lerp.selectedColor, equals(middleGrey.withAlpha(0x3d)));
expect(lerp.secondarySelectedColor, equals(middleGrey.withAlpha(0x3d)));
expect(lerp.labelPadding, equals(const EdgeInsets.all(4.0)));
expect(lerp.padding, equals(const EdgeInsets.all(3.0)));
expect(lerp.shape, equals(const isInstanceOf<StadiumBorder>()));
expect(lerp.labelStyle.color, equals(middleGrey.withAlpha(0xde)));
expect(lerp.secondaryLabelStyle.color, equals(middleGrey.withAlpha(0xde)));
expect(lerp.brightness, equals(Brightness.light));
expect(ChipThemeData.lerp(null, null, 0.25), isNull);
final ChipThemeData lerpANull25 = ChipThemeData.lerp(null, chipThemeWhite, 0.25);
expect(lerpANull25.backgroundColor, equals(Colors.black.withAlpha(0x08)));
expect(lerpANull25.deleteIconColor, equals(Colors.black.withAlpha(0x38)));
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.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(const isInstanceOf<StadiumBorder>()));
expect(lerpANull25.labelStyle.color, equals(Colors.black.withAlpha(0x38)));
expect(lerpANull25.secondaryLabelStyle.color, equals(Colors.white.withAlpha(0x38)));
expect(lerpANull25.brightness, equals(Brightness.light));
final ChipThemeData lerpANull75 = ChipThemeData.lerp(null, chipThemeWhite, 0.75);
expect(lerpANull75.backgroundColor, equals(Colors.black.withAlpha(0x17)));
expect(lerpANull75.deleteIconColor, equals(Colors.black.withAlpha(0xa7)));
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.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(const isInstanceOf<StadiumBorder>()));
expect(lerpANull75.labelStyle.color, equals(Colors.black.withAlpha(0xa7)));
expect(lerpANull75.secondaryLabelStyle.color, equals(Colors.white.withAlpha(0xa7)));
expect(lerpANull75.brightness, equals(Brightness.light));
final ChipThemeData lerpBNull25 = ChipThemeData.lerp(chipThemeBlack, null, 0.25);
expect(lerpBNull25.backgroundColor, equals(Colors.white.withAlpha(0x17)));
expect(lerpBNull25.deleteIconColor, equals(Colors.white.withAlpha(0xa7)));
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.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(const isInstanceOf<StadiumBorder>()));
expect(lerpBNull25.labelStyle.color, equals(Colors.white.withAlpha(0xa7)));
expect(lerpBNull25.secondaryLabelStyle.color, equals(Colors.black.withAlpha(0xa7)));
expect(lerpBNull25.brightness, equals(Brightness.dark));
final ChipThemeData lerpBNull75 = ChipThemeData.lerp(chipThemeBlack, null, 0.75);
expect(lerpBNull75.backgroundColor, equals(Colors.white.withAlpha(0x08)));
expect(lerpBNull75.deleteIconColor, equals(Colors.white.withAlpha(0x38)));
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.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(const isInstanceOf<StadiumBorder>()));
expect(lerpBNull75.labelStyle.color, equals(Colors.white.withAlpha(0x38)));
expect(lerpBNull75.secondaryLabelStyle.color, equals(Colors.black.withAlpha(0x38)));
expect(lerpBNull75.brightness, equals(Brightness.light));
});
}
...@@ -100,7 +100,7 @@ void main() { ...@@ -100,7 +100,7 @@ void main() {
expect(sliderBox, paints..rect(color: customTheme.disabledActiveRailColor)..rect(color: customTheme.disabledInactiveRailColor)); expect(sliderBox, paints..rect(color: customTheme.disabledActiveRailColor)..rect(color: customTheme.disabledInactiveRailColor));
}); });
testWidgets('SliderThemeData generates correct opacities for materialDefaults', (WidgetTester tester) async { testWidgets('SliderThemeData generates correct opacities for fromPrimaryColors', (WidgetTester tester) async {
const Color customColor1 = const Color(0xcafefeed); const Color customColor1 = const Color(0xcafefeed);
const Color customColor2 = const Color(0xdeadbeef); const Color customColor2 = const Color(0xdeadbeef);
const Color customColor3 = const Color(0xdecaface); const Color customColor3 = const Color(0xdecaface);
......
...@@ -107,6 +107,60 @@ void main() { ...@@ -107,6 +107,60 @@ void main() {
expect(s5.height, 123.0); expect(s5.height, 123.0);
expect(s5.color, isNull); expect(s5.color, isNull);
expect(TextStyle.lerp(null, null, 0.5), isNull);
final TextStyle s6 = TextStyle.lerp(null, s3, 0.25);
expect(s3.fontFamily, isNull);
expect(s3.fontSize, 18.0);
expect(s3.fontWeight, FontWeight.w400);
expect(s3.height, 123.0);
expect(s3.color, isNull);
expect(s3, isNot(equals(s6)));
expect(s6.fontFamily, isNull);
expect(s6.fontSize, isNull);
expect(s6.fontWeight, FontWeight.w400);
expect(s6.height, isNull);
expect(s6.color, isNull);
final TextStyle s7 = TextStyle.lerp(null, s3, 0.75);
expect(s3.fontFamily, isNull);
expect(s3.fontSize, 18.0);
expect(s3.fontWeight, FontWeight.w400);
expect(s3.height, 123.0);
expect(s3.color, isNull);
expect(s3, equals(s7));
expect(s7.fontFamily, isNull);
expect(s7.fontSize, 18.0);
expect(s7.fontWeight, FontWeight.w400);
expect(s7.height, 123.0);
expect(s7.color, isNull);
final TextStyle s8 = TextStyle.lerp(s3, null, 0.25);
expect(s3.fontFamily, isNull);
expect(s3.fontSize, 18.0);
expect(s3.fontWeight, FontWeight.w400);
expect(s3.height, 123.0);
expect(s3.color, isNull);
expect(s3, equals(s8));
expect(s8.fontFamily, isNull);
expect(s8.fontSize, 18.0);
expect(s8.fontWeight, FontWeight.w400);
expect(s8.height, 123.0);
expect(s8.color, isNull);
final TextStyle s9 = TextStyle.lerp(s3, null, 0.75);
expect(s3.fontFamily, isNull);
expect(s3.fontSize, 18.0);
expect(s3.fontWeight, FontWeight.w400);
expect(s3.height, 123.0);
expect(s3.color, isNull);
expect(s3, isNot(equals(s9)));
expect(s9.fontFamily, isNull);
expect(s9.fontSize, isNull);
expect(s9.fontWeight, FontWeight.w400);
expect(s9.height, isNull);
expect(s9.color, isNull);
final ui.TextStyle ts5 = s5.getTextStyle(); final ui.TextStyle ts5 = s5.getTextStyle();
expect(ts5, equals(new ui.TextStyle(fontWeight: FontWeight.w700, fontSize: 12.0, height: 123.0))); expect(ts5, equals(new ui.TextStyle(fontWeight: FontWeight.w700, fontSize: 12.0, height: 123.0)));
expect(ts5.toString(), 'TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, fontWeight: FontWeight.w700, fontStyle: unspecified, textBaseline: unspecified, fontFamily: unspecified, fontSize: 12.0, letterSpacing: unspecified, wordSpacing: unspecified, height: 123.0x, locale: unspecified)'); expect(ts5.toString(), 'TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, fontWeight: FontWeight.w700, fontStyle: unspecified, textBaseline: unspecified, fontFamily: unspecified, fontSize: 12.0, letterSpacing: unspecified, wordSpacing: unspecified, height: 123.0x, locale: unspecified)');
......
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