您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
2887 行
98 KiB
2887 行
98 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using uiwidgets;
|
|
using Unity.UIWidgets.animation;
|
|
using Unity.UIWidgets.foundation;
|
|
using Unity.UIWidgets.gestures;
|
|
using Unity.UIWidgets.painting;
|
|
using Unity.UIWidgets.rendering;
|
|
using Unity.UIWidgets.service;
|
|
using Unity.UIWidgets.ui;
|
|
using Unity.UIWidgets.widgets;
|
|
using UnityEngine;
|
|
using Canvas = Unity.UIWidgets.ui.Canvas;
|
|
using Color = Unity.UIWidgets.ui.Color;
|
|
using Rect = Unity.UIWidgets.ui.Rect;
|
|
using TextStyle = Unity.UIWidgets.painting.TextStyle;
|
|
|
|
namespace Unity.UIWidgets.material {
|
|
class ChipUtils {
|
|
public const float _kChipHeight = 32.0f;
|
|
public const float _kDeleteIconSize = 18.0f;
|
|
public const int _kCheckmarkAlpha = 0xde; // 87%
|
|
public const int _kDisabledAlpha = 0x61; // 38%
|
|
public const float _kCheckmarkStrokeWidth = 2.0f;
|
|
public static readonly TimeSpan _kSelectDuration = new TimeSpan(0, 0, 0, 0, 195);
|
|
public static readonly TimeSpan _kCheckmarkDuration = new TimeSpan(0, 0, 0, 0, 150);
|
|
public static readonly TimeSpan _kCheckmarkReverseDuration = new TimeSpan(0, 0, 0, 0, 50);
|
|
public static readonly TimeSpan _kDrawerDuration = new TimeSpan(0, 0, 0, 0, 150);
|
|
public static readonly TimeSpan _kReverseDrawerDuration = new TimeSpan(0, 0, 0, 0, 100);
|
|
public static readonly TimeSpan _kDisableDuration = new TimeSpan(0, 0, 0, 0, 75);
|
|
public static readonly Color _kSelectScrimColor = new Color(0x60191919);
|
|
public static readonly Icon _kDefaultDeleteIcon = new Icon(Icons.cancel, size: _kDeleteIconSize);
|
|
}
|
|
|
|
public interface ChipAttributes {
|
|
Widget label { get; }
|
|
|
|
Widget avatar { get; }
|
|
|
|
TextStyle labelStyle { get; }
|
|
|
|
ShapeBorder shape { get; }
|
|
|
|
Clip clipBehavior { get; }
|
|
|
|
FocusNode focusNode { get; }
|
|
|
|
bool autofocus { get; }
|
|
|
|
Color backgroundColor { get; }
|
|
|
|
EdgeInsetsGeometry padding { get; }
|
|
|
|
VisualDensity visualDensity { get; }
|
|
|
|
EdgeInsetsGeometry labelPadding { get; }
|
|
|
|
MaterialTapTargetSize? materialTapTargetSize { get; }
|
|
|
|
float? elevation { get; }
|
|
|
|
Color shadowColor { get; }
|
|
}
|
|
|
|
public interface DeletableChipAttributes {
|
|
Widget deleteIcon { get; }
|
|
|
|
VoidCallback onDeleted { get; }
|
|
|
|
Color deleteIconColor { get; }
|
|
|
|
string deleteButtonTooltipMessage { get; }
|
|
}
|
|
|
|
public interface CheckmarkableChipAttributes {
|
|
bool? showCheckmark { get; }
|
|
|
|
Color checkmarkColor { get; }
|
|
}
|
|
|
|
public interface SelectableChipAttributes {
|
|
bool? selected { get; }
|
|
|
|
ValueChanged<bool> onSelected { get; }
|
|
|
|
float? pressElevation { get; }
|
|
|
|
Color selectedColor { get; }
|
|
|
|
string tooltip { get; }
|
|
|
|
ShapeBorder avatarBorder { get; }
|
|
|
|
Color selectedShadowColor { get; }
|
|
}
|
|
|
|
public interface DisabledChipAttributes {
|
|
bool? isEnabled { get; }
|
|
|
|
Color disabledColor { get; }
|
|
}
|
|
|
|
public interface TappableChipAttributes {
|
|
VoidCallback onPressed { get; }
|
|
|
|
float? pressElevation { get; }
|
|
|
|
string tooltip { get; }
|
|
}
|
|
|
|
public class Chip : StatelessWidget, ChipAttributes, DeletableChipAttributes {
|
|
public Chip(
|
|
Key key = null,
|
|
Widget avatar = null,
|
|
Widget label = null,
|
|
TextStyle labelStyle = null,
|
|
EdgeInsetsGeometry labelPadding = null,
|
|
Widget deleteIcon = null,
|
|
VoidCallback onDeleted = null,
|
|
Color deleteIconColor = null,
|
|
string deleteButtonTooltipMessage = null,
|
|
ShapeBorder shape = null,
|
|
Clip clipBehavior = Clip.none,
|
|
FocusNode focusNode = null,
|
|
bool autofocus = false,
|
|
Color backgroundColor = null,
|
|
EdgeInsetsGeometry padding = null,
|
|
VisualDensity visualDensity = null,
|
|
MaterialTapTargetSize? materialTapTargetSize = null,
|
|
float? elevation = null,
|
|
Color shadowColor = null
|
|
) : base(key: key) {
|
|
D.assert(label != null);
|
|
D.assert(elevation == null || elevation >= 0.0f);
|
|
_avatar = avatar;
|
|
_label = label;
|
|
_labelStyle = labelStyle;
|
|
_labelPadding = labelPadding;
|
|
_deleteIcon = deleteIcon;
|
|
_onDeleted = onDeleted;
|
|
_deleteIconColor = deleteIconColor;
|
|
_deleteButtonTooltipMessage = deleteButtonTooltipMessage;
|
|
_shape = shape;
|
|
_clipBehavior = clipBehavior;
|
|
this.focusNode = focusNode;
|
|
this.autofocus = autofocus;
|
|
_backgroundColor = backgroundColor;
|
|
_padding = padding;
|
|
_materialTapTargetSize = materialTapTargetSize;
|
|
_elevation = elevation;
|
|
_shadowColor = shadowColor;
|
|
}
|
|
|
|
public Widget avatar {
|
|
get { return _avatar; }
|
|
}
|
|
|
|
Widget _avatar;
|
|
|
|
public Widget label {
|
|
get { return _label; }
|
|
}
|
|
|
|
Widget _label;
|
|
|
|
public TextStyle labelStyle {
|
|
get { return _labelStyle; }
|
|
}
|
|
|
|
TextStyle _labelStyle;
|
|
|
|
public EdgeInsetsGeometry labelPadding {
|
|
get { return _labelPadding; }
|
|
}
|
|
|
|
EdgeInsetsGeometry _labelPadding;
|
|
|
|
public ShapeBorder shape {
|
|
get { return _shape; }
|
|
}
|
|
|
|
ShapeBorder _shape;
|
|
|
|
public Clip clipBehavior {
|
|
get { return _clipBehavior; }
|
|
}
|
|
|
|
Clip _clipBehavior;
|
|
|
|
public FocusNode focusNode { get; }
|
|
|
|
public bool autofocus { get; }
|
|
|
|
public Color backgroundColor {
|
|
get { return _backgroundColor; }
|
|
}
|
|
|
|
Color _backgroundColor;
|
|
|
|
public EdgeInsetsGeometry padding {
|
|
get { return _padding; }
|
|
}
|
|
|
|
public VisualDensity visualDensity { get; }
|
|
|
|
EdgeInsetsGeometry _padding;
|
|
|
|
public Widget deleteIcon {
|
|
get { return _deleteIcon; }
|
|
}
|
|
|
|
Widget _deleteIcon;
|
|
|
|
public VoidCallback onDeleted {
|
|
get { return _onDeleted; }
|
|
}
|
|
|
|
VoidCallback _onDeleted;
|
|
|
|
public Color deleteIconColor {
|
|
get { return _deleteIconColor; }
|
|
}
|
|
|
|
Color _deleteIconColor;
|
|
|
|
public string deleteButtonTooltipMessage {
|
|
get { return _deleteButtonTooltipMessage; }
|
|
}
|
|
|
|
string _deleteButtonTooltipMessage;
|
|
|
|
public MaterialTapTargetSize? materialTapTargetSize {
|
|
get { return _materialTapTargetSize; }
|
|
}
|
|
|
|
MaterialTapTargetSize? _materialTapTargetSize;
|
|
|
|
public float? elevation {
|
|
get { return _elevation; }
|
|
}
|
|
|
|
float? _elevation;
|
|
|
|
public Color shadowColor {
|
|
get { return _shadowColor; }
|
|
}
|
|
|
|
Color _shadowColor;
|
|
|
|
public override Widget build(BuildContext context) {
|
|
D.assert(material_.debugCheckHasMaterial(context));
|
|
return new RawChip(
|
|
avatar: avatar,
|
|
label: label,
|
|
labelStyle: labelStyle,
|
|
labelPadding: labelPadding,
|
|
deleteIcon: deleteIcon,
|
|
onDeleted: onDeleted,
|
|
deleteIconColor: deleteIconColor,
|
|
deleteButtonTooltipMessage: deleteButtonTooltipMessage,
|
|
tapEnabled: false,
|
|
shape: shape,
|
|
clipBehavior: clipBehavior,
|
|
focusNode: focusNode,
|
|
autofocus: autofocus,
|
|
backgroundColor: backgroundColor,
|
|
padding: padding,
|
|
visualDensity: visualDensity,
|
|
materialTapTargetSize: materialTapTargetSize,
|
|
elevation: elevation,
|
|
shadowColor: shadowColor,
|
|
isEnabled: true
|
|
);
|
|
}
|
|
}
|
|
|
|
public class InputChip : StatelessWidget,
|
|
ChipAttributes,
|
|
DeletableChipAttributes,
|
|
SelectableChipAttributes,
|
|
CheckmarkableChipAttributes,
|
|
DisabledChipAttributes,
|
|
TappableChipAttributes {
|
|
public InputChip(
|
|
Key key = null,
|
|
Widget avatar = null,
|
|
Widget label = null,
|
|
TextStyle labelStyle = null,
|
|
EdgeInsetsGeometry labelPadding = null,
|
|
bool selected = false,
|
|
bool isEnabled = true,
|
|
ValueChanged<bool> onSelected = null,
|
|
Widget deleteIcon = null,
|
|
VoidCallback onDeleted = null,
|
|
Color deleteIconColor = null,
|
|
string deleteButtonTooltipMessage = null,
|
|
VoidCallback onPressed = null,
|
|
float? pressElevation = null,
|
|
Color disabledColor = null,
|
|
Color selectedColor = null,
|
|
string tooltip = null,
|
|
ShapeBorder shape = null,
|
|
Clip clipBehavior = Clip.none,
|
|
FocusNode focusNode = null,
|
|
bool autofocus = false,
|
|
Color backgroundColor = null,
|
|
EdgeInsetsGeometry padding = null,
|
|
VisualDensity visualDensity = null,
|
|
MaterialTapTargetSize? materialTapTargetSize = null,
|
|
float? elevation = null,
|
|
Color shadowColor = null,
|
|
Color selectedShadowColor = null,
|
|
ShapeBorder avatarBorder = null,
|
|
bool showCheckmark = false,
|
|
Color checkmarkColor = null
|
|
) : base(key: key) {
|
|
D.assert(label != null);
|
|
D.assert(pressElevation == null || pressElevation >= 0.0f);
|
|
D.assert(elevation == null || elevation >= 0.0f);
|
|
_avatarBorder = avatarBorder ?? new CircleBorder();
|
|
_avatar = avatar;
|
|
_label = label;
|
|
_labelStyle = labelStyle;
|
|
_labelPadding = labelPadding;
|
|
_selected = selected;
|
|
_isEnabled = isEnabled;
|
|
_onSelected = onSelected;
|
|
_deleteIcon = deleteIcon;
|
|
_onDeleted = onDeleted;
|
|
_deleteIconColor = deleteIconColor;
|
|
_deleteButtonTooltipMessage = deleteButtonTooltipMessage;
|
|
_onPressed = onPressed;
|
|
_pressElevation = pressElevation;
|
|
_disabledColor = disabledColor;
|
|
_selectedColor = selectedColor;
|
|
_tooltip = tooltip;
|
|
_shape = shape;
|
|
_clipBehavior = clipBehavior;
|
|
this.focusNode = focusNode;
|
|
this.autofocus = autofocus;
|
|
_backgroundColor = backgroundColor;
|
|
_padding = padding;
|
|
this.visualDensity = visualDensity;
|
|
_materialTapTargetSize = materialTapTargetSize;
|
|
_elevation = elevation;
|
|
_shadowColor = shadowColor;
|
|
_selectedShadowColor = selectedShadowColor;
|
|
this.showCheckmark = showCheckmark;
|
|
this.checkmarkColor = checkmarkColor;
|
|
}
|
|
|
|
public Widget avatar {
|
|
get { return _avatar; }
|
|
}
|
|
|
|
Widget _avatar;
|
|
|
|
public Widget label {
|
|
get { return _label; }
|
|
}
|
|
|
|
Widget _label;
|
|
|
|
public TextStyle labelStyle {
|
|
get { return _labelStyle; }
|
|
}
|
|
|
|
TextStyle _labelStyle;
|
|
|
|
public EdgeInsetsGeometry labelPadding {
|
|
get { return _labelPadding; }
|
|
}
|
|
|
|
EdgeInsetsGeometry _labelPadding;
|
|
|
|
public bool? selected {
|
|
get { return _selected; }
|
|
}
|
|
|
|
bool _selected;
|
|
|
|
public bool? isEnabled {
|
|
get { return _isEnabled; }
|
|
}
|
|
|
|
bool _isEnabled;
|
|
|
|
public ValueChanged<bool> onSelected {
|
|
get { return _onSelected; }
|
|
}
|
|
|
|
ValueChanged<bool> _onSelected;
|
|
|
|
public Widget deleteIcon {
|
|
get { return _deleteIcon; }
|
|
}
|
|
|
|
Widget _deleteIcon;
|
|
|
|
public VoidCallback onDeleted {
|
|
get { return _onDeleted; }
|
|
}
|
|
|
|
VoidCallback _onDeleted;
|
|
|
|
public Color deleteIconColor {
|
|
get { return _deleteIconColor; }
|
|
}
|
|
|
|
Color _deleteIconColor;
|
|
|
|
public string deleteButtonTooltipMessage {
|
|
get { return _deleteButtonTooltipMessage; }
|
|
}
|
|
|
|
string _deleteButtonTooltipMessage;
|
|
|
|
public VoidCallback onPressed {
|
|
get { return _onPressed; }
|
|
}
|
|
|
|
VoidCallback _onPressed;
|
|
|
|
public float? pressElevation {
|
|
get { return _pressElevation; }
|
|
}
|
|
|
|
float? _pressElevation;
|
|
|
|
public Color disabledColor {
|
|
get { return _disabledColor; }
|
|
}
|
|
|
|
Color _disabledColor;
|
|
|
|
public Color selectedColor {
|
|
get { return _selectedColor; }
|
|
}
|
|
|
|
Color _selectedColor;
|
|
|
|
public string tooltip {
|
|
get { return _tooltip; }
|
|
}
|
|
|
|
string _tooltip;
|
|
|
|
public ShapeBorder shape {
|
|
get { return _shape; }
|
|
}
|
|
|
|
ShapeBorder _shape;
|
|
|
|
public Clip clipBehavior {
|
|
get { return _clipBehavior; }
|
|
}
|
|
|
|
Clip _clipBehavior;
|
|
|
|
public FocusNode focusNode { get; }
|
|
|
|
public bool autofocus { get; }
|
|
|
|
public Color backgroundColor {
|
|
get { return _backgroundColor; }
|
|
}
|
|
|
|
Color _backgroundColor;
|
|
|
|
public EdgeInsetsGeometry padding {
|
|
get { return _padding; }
|
|
}
|
|
|
|
EdgeInsetsGeometry _padding;
|
|
|
|
public VisualDensity visualDensity { get; }
|
|
|
|
public MaterialTapTargetSize? materialTapTargetSize {
|
|
get { return _materialTapTargetSize; }
|
|
}
|
|
|
|
MaterialTapTargetSize? _materialTapTargetSize;
|
|
|
|
public float? elevation {
|
|
get { return _elevation; }
|
|
}
|
|
|
|
float? _elevation;
|
|
|
|
public Color shadowColor {
|
|
get { return _shadowColor; }
|
|
}
|
|
|
|
Color _shadowColor;
|
|
|
|
public Color selectedShadowColor {
|
|
get { return _selectedShadowColor; }
|
|
}
|
|
|
|
Color _selectedShadowColor;
|
|
|
|
public ShapeBorder avatarBorder {
|
|
get { return _avatarBorder; }
|
|
}
|
|
|
|
public bool? showCheckmark { get; }
|
|
|
|
public Color checkmarkColor { get; }
|
|
|
|
ShapeBorder _avatarBorder;
|
|
|
|
public override Widget build(BuildContext context) {
|
|
D.assert(material_.debugCheckHasMaterial(context));
|
|
return new RawChip(
|
|
avatar: avatar,
|
|
label: label,
|
|
labelStyle: labelStyle,
|
|
labelPadding: labelPadding,
|
|
deleteIcon: deleteIcon,
|
|
onDeleted: onDeleted,
|
|
deleteIconColor: deleteIconColor,
|
|
deleteButtonTooltipMessage: deleteButtonTooltipMessage,
|
|
onSelected: onSelected,
|
|
onPressed: onPressed,
|
|
pressElevation: pressElevation,
|
|
selected: selected,
|
|
tapEnabled: true,
|
|
disabledColor: disabledColor,
|
|
selectedColor: selectedColor,
|
|
tooltip: tooltip,
|
|
shape: shape,
|
|
clipBehavior: clipBehavior,
|
|
focusNode: focusNode,
|
|
autofocus: autofocus,
|
|
backgroundColor: backgroundColor,
|
|
padding: padding,
|
|
visualDensity: visualDensity,
|
|
materialTapTargetSize: materialTapTargetSize,
|
|
elevation: elevation,
|
|
shadowColor: shadowColor,
|
|
selectedShadowColor: selectedShadowColor,
|
|
showCheckmark: showCheckmark,
|
|
checkmarkColor: checkmarkColor,
|
|
isEnabled: isEnabled == true &&
|
|
(onSelected != null || onDeleted != null || onPressed != null),
|
|
avatarBorder: avatarBorder
|
|
);
|
|
}
|
|
}
|
|
|
|
public class ChoiceChip : StatelessWidget,
|
|
ChipAttributes,
|
|
SelectableChipAttributes,
|
|
DisabledChipAttributes {
|
|
public ChoiceChip(
|
|
Key key,
|
|
Widget avatar = null,
|
|
Widget label = null,
|
|
TextStyle labelStyle = null,
|
|
EdgeInsetsGeometry labelPadding = null,
|
|
ValueChanged<bool> onSelected = null,
|
|
float? pressElevation = null,
|
|
bool? selected = null,
|
|
Color selectedColor = null,
|
|
Color disabledColor = null,
|
|
string tooltip = null,
|
|
ShapeBorder shape = null,
|
|
Clip clipBehavior = Clip.none,
|
|
FocusNode focusNode = null,
|
|
bool autofocus = false,
|
|
Color backgroundColor = null,
|
|
EdgeInsetsGeometry padding = null,
|
|
VisualDensity visualDensity = null,
|
|
MaterialTapTargetSize? materialTapTargetSize = null,
|
|
float? elevation = null,
|
|
Color shadowColor = null,
|
|
Color selectedShadowColor = null,
|
|
ShapeBorder avatarBorder = null
|
|
) : base(key: key) {
|
|
D.assert(selected != null);
|
|
D.assert(label != null);
|
|
D.assert(pressElevation == null || pressElevation >= 0.0f);
|
|
D.assert(elevation == null || elevation >= 0.0f);
|
|
_avatarBorder = avatarBorder ?? new CircleBorder();
|
|
_avatar = avatar;
|
|
_label = label;
|
|
_labelStyle = labelStyle;
|
|
_labelPadding = labelPadding;
|
|
_onSelected = onSelected;
|
|
_pressElevation = pressElevation;
|
|
_selected = selected;
|
|
_selectedColor = selectedColor;
|
|
_disabledColor = disabledColor;
|
|
_tooltip = tooltip;
|
|
_shape = shape;
|
|
_clipBehavior = clipBehavior;
|
|
this.focusNode = focusNode;
|
|
this.autofocus = autofocus;
|
|
_backgroundColor = backgroundColor;
|
|
_padding = padding;
|
|
this.visualDensity = visualDensity;
|
|
_materialTapTargetSize = materialTapTargetSize;
|
|
_elevation = elevation;
|
|
_shadowColor = shadowColor;
|
|
_selectedShadowColor = selectedShadowColor;
|
|
}
|
|
|
|
public Widget avatar {
|
|
get { return _avatar; }
|
|
}
|
|
|
|
Widget _avatar;
|
|
|
|
public Widget label {
|
|
get { return _label; }
|
|
}
|
|
|
|
Widget _label;
|
|
|
|
public TextStyle labelStyle {
|
|
get { return _labelStyle; }
|
|
}
|
|
|
|
TextStyle _labelStyle;
|
|
|
|
public EdgeInsetsGeometry labelPadding {
|
|
get { return _labelPadding; }
|
|
}
|
|
|
|
EdgeInsetsGeometry _labelPadding;
|
|
|
|
public ValueChanged<bool> onSelected {
|
|
get { return _onSelected; }
|
|
}
|
|
|
|
ValueChanged<bool> _onSelected;
|
|
|
|
public float? pressElevation {
|
|
get { return _pressElevation; }
|
|
}
|
|
|
|
float? _pressElevation;
|
|
|
|
public bool? selected {
|
|
get { return _selected; }
|
|
}
|
|
|
|
bool? _selected;
|
|
|
|
public Color disabledColor {
|
|
get { return _disabledColor; }
|
|
}
|
|
|
|
Color _disabledColor;
|
|
|
|
public Color selectedColor {
|
|
get { return _selectedColor; }
|
|
}
|
|
|
|
Color _selectedColor;
|
|
|
|
public string tooltip {
|
|
get { return _tooltip; }
|
|
}
|
|
|
|
string _tooltip;
|
|
|
|
public ShapeBorder shape {
|
|
get { return _shape; }
|
|
}
|
|
|
|
ShapeBorder _shape;
|
|
|
|
public Clip clipBehavior {
|
|
get { return _clipBehavior; }
|
|
}
|
|
|
|
Clip _clipBehavior;
|
|
|
|
public FocusNode focusNode { get; }
|
|
public bool autofocus { get; }
|
|
|
|
public Color backgroundColor {
|
|
get { return _backgroundColor; }
|
|
}
|
|
|
|
Color _backgroundColor;
|
|
|
|
public EdgeInsetsGeometry padding {
|
|
get { return _padding; }
|
|
}
|
|
|
|
EdgeInsetsGeometry _padding;
|
|
|
|
public VisualDensity visualDensity { get; }
|
|
|
|
public MaterialTapTargetSize? materialTapTargetSize {
|
|
get { return _materialTapTargetSize; }
|
|
}
|
|
|
|
MaterialTapTargetSize? _materialTapTargetSize;
|
|
|
|
public float? elevation {
|
|
get { return _elevation; }
|
|
}
|
|
|
|
float? _elevation;
|
|
|
|
public Color shadowColor {
|
|
get { return _shadowColor; }
|
|
}
|
|
|
|
Color _shadowColor;
|
|
|
|
public Color selectedShadowColor {
|
|
get { return _selectedShadowColor; }
|
|
}
|
|
|
|
Color _selectedShadowColor;
|
|
|
|
public ShapeBorder avatarBorder {
|
|
get { return _avatarBorder; }
|
|
}
|
|
|
|
ShapeBorder _avatarBorder;
|
|
|
|
public bool? isEnabled {
|
|
get { return onSelected != null; }
|
|
}
|
|
|
|
public override Widget build(BuildContext context) {
|
|
D.assert(material_.debugCheckHasMaterial(context));
|
|
ChipThemeData chipTheme = ChipTheme.of(context);
|
|
return new RawChip(
|
|
avatar: avatar,
|
|
label: label,
|
|
labelStyle: labelStyle ?? (selected == true ? chipTheme.secondaryLabelStyle : null),
|
|
labelPadding: labelPadding,
|
|
onSelected: onSelected,
|
|
pressElevation: pressElevation,
|
|
selected: selected,
|
|
showCheckmark: false,
|
|
onDeleted: null,
|
|
tooltip: tooltip,
|
|
shape: shape,
|
|
clipBehavior: clipBehavior,
|
|
focusNode: focusNode,
|
|
autofocus: autofocus,
|
|
disabledColor: disabledColor,
|
|
selectedColor: selectedColor ?? chipTheme.secondarySelectedColor,
|
|
backgroundColor: backgroundColor,
|
|
padding: padding,
|
|
visualDensity: visualDensity,
|
|
isEnabled: isEnabled,
|
|
materialTapTargetSize: materialTapTargetSize,
|
|
elevation: elevation,
|
|
shadowColor: shadowColor,
|
|
selectedShadowColor: selectedShadowColor,
|
|
avatarBorder: avatarBorder
|
|
);
|
|
}
|
|
}
|
|
|
|
public class FilterChip : StatelessWidget,
|
|
ChipAttributes,
|
|
SelectableChipAttributes,
|
|
CheckmarkableChipAttributes,
|
|
DisabledChipAttributes {
|
|
public FilterChip(
|
|
Key key = null,
|
|
Widget avatar = null,
|
|
Widget label = null,
|
|
TextStyle labelStyle = null,
|
|
EdgeInsetsGeometry labelPadding = null,
|
|
bool selected = false,
|
|
ValueChanged<bool> onSelected = null,
|
|
float? pressElevation = null,
|
|
Color disabledColor = null,
|
|
Color selectedColor = null,
|
|
string tooltip = null,
|
|
ShapeBorder shape = null,
|
|
Clip clipBehavior = Clip.none,
|
|
FocusNode focusNode = null,
|
|
bool autofocus = false,
|
|
Color backgroundColor = null,
|
|
EdgeInsetsGeometry padding = null,
|
|
VisualDensity visualDensity = null,
|
|
MaterialTapTargetSize? materialTapTargetSize = null,
|
|
float? elevation = null,
|
|
Color shadowColor = null,
|
|
Color selectedShadowColor = null,
|
|
ShapeBorder avatarBorder = null,
|
|
bool showCheckmark = false,
|
|
Color checkmarkColor = null
|
|
) : base(key: key) {
|
|
D.assert(label != null);
|
|
D.assert(pressElevation == null || pressElevation >= 0.0f);
|
|
D.assert(elevation == null || elevation >= 0.0f);
|
|
_avatarBorder = avatarBorder ?? new CircleBorder();
|
|
_avatar = avatar;
|
|
_label = label;
|
|
_labelStyle = labelStyle;
|
|
_labelPadding = labelPadding;
|
|
_selected = selected;
|
|
_onSelected = onSelected;
|
|
_pressElevation = pressElevation;
|
|
_disabledColor = disabledColor;
|
|
_selectedColor = selectedColor;
|
|
_tooltip = tooltip;
|
|
_shape = shape;
|
|
_clipBehavior = clipBehavior;
|
|
this.focusNode = focusNode;
|
|
this.autofocus = autofocus;
|
|
_backgroundColor = backgroundColor;
|
|
_padding = padding;
|
|
this.visualDensity = visualDensity;
|
|
_materialTapTargetSize = materialTapTargetSize;
|
|
_elevation = elevation;
|
|
_shadowColor = shadowColor;
|
|
_selectedShadowColor = selectedShadowColor;
|
|
this.showCheckmark = showCheckmark;
|
|
this.checkmarkColor = checkmarkColor;
|
|
}
|
|
|
|
public Widget avatar {
|
|
get { return _avatar; }
|
|
}
|
|
|
|
Widget _avatar;
|
|
|
|
public Widget label {
|
|
get { return _label; }
|
|
}
|
|
|
|
Widget _label;
|
|
|
|
public TextStyle labelStyle {
|
|
get { return _labelStyle; }
|
|
}
|
|
|
|
TextStyle _labelStyle;
|
|
|
|
public EdgeInsetsGeometry labelPadding {
|
|
get { return _labelPadding; }
|
|
}
|
|
|
|
EdgeInsetsGeometry _labelPadding;
|
|
|
|
public bool? selected {
|
|
get { return _selected; }
|
|
}
|
|
|
|
bool _selected;
|
|
|
|
public ValueChanged<bool> onSelected {
|
|
get { return _onSelected; }
|
|
}
|
|
|
|
ValueChanged<bool> _onSelected;
|
|
|
|
public float? pressElevation {
|
|
get { return _pressElevation; }
|
|
}
|
|
|
|
float? _pressElevation;
|
|
|
|
public Color disabledColor {
|
|
get { return _disabledColor; }
|
|
}
|
|
|
|
Color _disabledColor;
|
|
|
|
public Color selectedColor {
|
|
get { return _selectedColor; }
|
|
}
|
|
|
|
Color _selectedColor;
|
|
|
|
public string tooltip {
|
|
get { return _tooltip; }
|
|
}
|
|
|
|
string _tooltip;
|
|
|
|
public ShapeBorder shape {
|
|
get { return _shape; }
|
|
}
|
|
|
|
ShapeBorder _shape;
|
|
|
|
public Clip clipBehavior {
|
|
get { return _clipBehavior; }
|
|
}
|
|
|
|
Clip _clipBehavior;
|
|
|
|
public FocusNode focusNode { get; }
|
|
|
|
public bool autofocus { get; }
|
|
|
|
public Color backgroundColor {
|
|
get { return _backgroundColor; }
|
|
}
|
|
|
|
Color _backgroundColor;
|
|
|
|
public EdgeInsetsGeometry padding {
|
|
get { return _padding; }
|
|
}
|
|
|
|
EdgeInsetsGeometry _padding;
|
|
|
|
public VisualDensity visualDensity { get; }
|
|
|
|
public MaterialTapTargetSize? materialTapTargetSize {
|
|
get { return _materialTapTargetSize; }
|
|
}
|
|
|
|
MaterialTapTargetSize? _materialTapTargetSize;
|
|
|
|
public float? elevation {
|
|
get { return _elevation; }
|
|
}
|
|
|
|
float? _elevation;
|
|
|
|
public Color shadowColor {
|
|
get { return _shadowColor; }
|
|
}
|
|
|
|
Color _shadowColor;
|
|
|
|
public Color selectedShadowColor {
|
|
get { return _selectedShadowColor; }
|
|
}
|
|
|
|
Color _selectedShadowColor;
|
|
|
|
public bool? showCheckmark { get; }
|
|
|
|
public Color checkmarkColor { get; }
|
|
|
|
public ShapeBorder avatarBorder {
|
|
get { return _avatarBorder; }
|
|
}
|
|
|
|
ShapeBorder _avatarBorder;
|
|
|
|
public bool? isEnabled {
|
|
get { return onSelected != null; }
|
|
}
|
|
|
|
public override Widget build(BuildContext context) {
|
|
D.assert(material_.debugCheckHasMaterial(context));
|
|
return new RawChip(
|
|
avatar: avatar,
|
|
label: label,
|
|
labelStyle: labelStyle,
|
|
labelPadding: labelPadding,
|
|
onSelected: onSelected,
|
|
pressElevation: pressElevation,
|
|
selected: selected,
|
|
tooltip: tooltip,
|
|
shape: shape,
|
|
clipBehavior: clipBehavior,
|
|
focusNode: focusNode,
|
|
autofocus: autofocus,
|
|
backgroundColor: backgroundColor,
|
|
disabledColor: disabledColor,
|
|
selectedColor: selectedColor,
|
|
padding: padding,
|
|
visualDensity: visualDensity,
|
|
isEnabled: isEnabled,
|
|
materialTapTargetSize: materialTapTargetSize,
|
|
elevation: elevation,
|
|
shadowColor: shadowColor,
|
|
selectedShadowColor: selectedShadowColor,
|
|
showCheckmark: showCheckmark,
|
|
checkmarkColor: checkmarkColor,
|
|
avatarBorder: avatarBorder
|
|
);
|
|
}
|
|
}
|
|
|
|
public class ActionChip : StatelessWidget, ChipAttributes, TappableChipAttributes {
|
|
public ActionChip(
|
|
Key key = null,
|
|
Widget avatar = null,
|
|
Widget label = null,
|
|
TextStyle labelStyle = null,
|
|
EdgeInsetsGeometry labelPadding = null,
|
|
VoidCallback onPressed = null,
|
|
float? pressElevation = null,
|
|
string tooltip = null,
|
|
ShapeBorder shape = null,
|
|
Clip clipBehavior = Clip.none,
|
|
FocusNode focusNode = null,
|
|
bool autofocus = false,
|
|
Color backgroundColor = null,
|
|
EdgeInsetsGeometry padding = null,
|
|
VisualDensity visualDensity = null,
|
|
MaterialTapTargetSize? materialTapTargetSize = null,
|
|
float? elevation = null,
|
|
Color shadowColor = null
|
|
) : base(key: key) {
|
|
D.assert(label != null);
|
|
D.assert(
|
|
onPressed != null,
|
|
() => "Rather than disabling an ActionChip by setting onPressed to null, " +
|
|
"remove it from the interface entirely."
|
|
);
|
|
D.assert(pressElevation == null || pressElevation >= 0.0f);
|
|
D.assert(elevation == null || elevation >= 0.0f);
|
|
_avatar = avatar;
|
|
_label = label;
|
|
_labelStyle = labelStyle;
|
|
_labelPadding = labelPadding;
|
|
_onPressed = onPressed;
|
|
_pressElevation = pressElevation;
|
|
_tooltip = tooltip;
|
|
_shape = shape;
|
|
_clipBehavior = clipBehavior;
|
|
this.focusNode = focusNode;
|
|
this.autofocus = autofocus;
|
|
_backgroundColor = backgroundColor;
|
|
_padding = padding;
|
|
this.visualDensity = visualDensity;
|
|
_materialTapTargetSize = materialTapTargetSize;
|
|
_elevation = elevation;
|
|
_shadowColor = shadowColor;
|
|
}
|
|
|
|
|
|
public Widget avatar {
|
|
get { return _avatar; }
|
|
}
|
|
|
|
Widget _avatar;
|
|
|
|
public Widget label {
|
|
get { return _label; }
|
|
}
|
|
|
|
Widget _label;
|
|
|
|
public TextStyle labelStyle {
|
|
get { return _labelStyle; }
|
|
}
|
|
|
|
TextStyle _labelStyle;
|
|
|
|
public EdgeInsetsGeometry labelPadding {
|
|
get { return _labelPadding; }
|
|
}
|
|
|
|
EdgeInsetsGeometry _labelPadding;
|
|
|
|
public VoidCallback onPressed {
|
|
get { return _onPressed; }
|
|
}
|
|
|
|
VoidCallback _onPressed;
|
|
|
|
public float? pressElevation {
|
|
get { return _pressElevation; }
|
|
}
|
|
|
|
float? _pressElevation;
|
|
|
|
public string tooltip {
|
|
get { return _tooltip; }
|
|
}
|
|
|
|
string _tooltip;
|
|
|
|
public ShapeBorder shape {
|
|
get { return _shape; }
|
|
}
|
|
|
|
ShapeBorder _shape;
|
|
|
|
public Clip clipBehavior {
|
|
get { return _clipBehavior; }
|
|
}
|
|
|
|
Clip _clipBehavior;
|
|
|
|
public FocusNode focusNode { get; }
|
|
|
|
public bool autofocus { get; }
|
|
|
|
public Color backgroundColor {
|
|
get { return _backgroundColor; }
|
|
}
|
|
|
|
Color _backgroundColor;
|
|
|
|
public EdgeInsetsGeometry padding {
|
|
get { return _padding; }
|
|
}
|
|
|
|
EdgeInsetsGeometry _padding;
|
|
|
|
public VisualDensity visualDensity { get; }
|
|
|
|
public MaterialTapTargetSize? materialTapTargetSize {
|
|
get { return _materialTapTargetSize; }
|
|
}
|
|
|
|
MaterialTapTargetSize? _materialTapTargetSize;
|
|
|
|
public float? elevation {
|
|
get { return _elevation; }
|
|
}
|
|
|
|
float? _elevation;
|
|
|
|
public Color shadowColor {
|
|
get { return _shadowColor; }
|
|
}
|
|
|
|
Color _shadowColor;
|
|
|
|
public override Widget build(BuildContext context) {
|
|
D.assert(material_.debugCheckHasMaterial(context));
|
|
return new RawChip(
|
|
avatar: avatar,
|
|
label: label,
|
|
onPressed: onPressed,
|
|
pressElevation: pressElevation,
|
|
tooltip: tooltip,
|
|
labelStyle: labelStyle,
|
|
backgroundColor: backgroundColor,
|
|
shape: shape,
|
|
clipBehavior: clipBehavior,
|
|
focusNode: focusNode,
|
|
autofocus: autofocus,
|
|
padding: padding,
|
|
visualDensity: visualDensity,
|
|
labelPadding: labelPadding,
|
|
isEnabled: true,
|
|
materialTapTargetSize: materialTapTargetSize,
|
|
elevation: elevation,
|
|
shadowColor: _shadowColor
|
|
);
|
|
}
|
|
}
|
|
|
|
public class RawChip : StatefulWidget,
|
|
ChipAttributes,
|
|
DeletableChipAttributes,
|
|
SelectableChipAttributes,
|
|
CheckmarkableChipAttributes,
|
|
DisabledChipAttributes,
|
|
TappableChipAttributes {
|
|
public RawChip(
|
|
Key key = null,
|
|
Widget avatar = null,
|
|
Widget label = null,
|
|
TextStyle labelStyle = null,
|
|
EdgeInsetsGeometry padding = null,
|
|
VisualDensity visualDensity = null,
|
|
EdgeInsetsGeometry labelPadding = null,
|
|
Widget deleteIcon = null,
|
|
VoidCallback onDeleted = null,
|
|
Color deleteIconColor = null,
|
|
string deleteButtonTooltipMessage = null,
|
|
VoidCallback onPressed = null,
|
|
ValueChanged<bool> onSelected = null,
|
|
float? pressElevation = null,
|
|
bool? tapEnabled = true,
|
|
bool? selected = null,
|
|
bool? isEnabled = true,
|
|
Color disabledColor = null,
|
|
Color selectedColor = null,
|
|
string tooltip = null,
|
|
ShapeBorder shape = null,
|
|
Clip clipBehavior = Clip.none,
|
|
FocusNode focusNode = null,
|
|
bool autofocus = false,
|
|
Color backgroundColor = null,
|
|
MaterialTapTargetSize? materialTapTargetSize = null,
|
|
float? elevation = null,
|
|
Color shadowColor = null,
|
|
Color selectedShadowColor = null,
|
|
bool? showCheckmark = true,
|
|
Color checkmarkColor = null,
|
|
ShapeBorder avatarBorder = null
|
|
) : base(key: key) {
|
|
D.assert(label != null);
|
|
D.assert(isEnabled != null);
|
|
D.assert(pressElevation == null || pressElevation >= 0.0f);
|
|
D.assert(elevation == null || elevation >= 0.0f);
|
|
deleteIcon = deleteIcon ?? ChipUtils._kDefaultDeleteIcon;
|
|
_avatarBorder = avatarBorder ?? new CircleBorder();
|
|
_avatar = avatar;
|
|
_label = label;
|
|
_labelStyle = labelStyle;
|
|
_padding = padding;
|
|
this.visualDensity = visualDensity;
|
|
_labelPadding = labelPadding;
|
|
_deleteIcon = deleteIcon;
|
|
_onDeleted = onDeleted;
|
|
_deleteIconColor = deleteIconColor;
|
|
_deleteButtonTooltipMessage = deleteButtonTooltipMessage;
|
|
_onPressed = onPressed;
|
|
_onSelected = onSelected;
|
|
_pressElevation = pressElevation;
|
|
_tapEnabled = tapEnabled;
|
|
_selected = selected;
|
|
_isEnabled = isEnabled;
|
|
_disabledColor = disabledColor;
|
|
_selectedColor = selectedColor;
|
|
_tooltip = tooltip;
|
|
_shape = shape;
|
|
_clipBehavior = clipBehavior;
|
|
this.focusNode = focusNode;
|
|
this.autofocus = autofocus;
|
|
_backgroundColor = backgroundColor;
|
|
_materialTapTargetSize = materialTapTargetSize;
|
|
_elevation = elevation;
|
|
_shadowColor = shadowColor;
|
|
_selectedShadowColor = selectedShadowColor;
|
|
this.showCheckmark = showCheckmark;
|
|
this.checkmarkColor = checkmarkColor;
|
|
}
|
|
|
|
|
|
public Widget avatar {
|
|
get { return _avatar; }
|
|
}
|
|
|
|
Widget _avatar;
|
|
|
|
public Widget label {
|
|
get { return _label; }
|
|
}
|
|
|
|
Widget _label;
|
|
|
|
public TextStyle labelStyle {
|
|
get { return _labelStyle; }
|
|
}
|
|
|
|
TextStyle _labelStyle;
|
|
|
|
public EdgeInsetsGeometry labelPadding {
|
|
get { return _labelPadding; }
|
|
}
|
|
|
|
EdgeInsetsGeometry _labelPadding;
|
|
|
|
public Widget deleteIcon {
|
|
get { return _deleteIcon; }
|
|
}
|
|
|
|
Widget _deleteIcon;
|
|
|
|
public VoidCallback onDeleted {
|
|
get { return _onDeleted; }
|
|
}
|
|
|
|
VoidCallback _onDeleted;
|
|
|
|
public Color deleteIconColor {
|
|
get { return _deleteIconColor; }
|
|
}
|
|
|
|
Color _deleteIconColor;
|
|
|
|
public string deleteButtonTooltipMessage {
|
|
get { return _deleteButtonTooltipMessage; }
|
|
}
|
|
|
|
string _deleteButtonTooltipMessage;
|
|
|
|
public ValueChanged<bool> onSelected {
|
|
get { return _onSelected; }
|
|
}
|
|
|
|
ValueChanged<bool> _onSelected;
|
|
|
|
public VoidCallback onPressed {
|
|
get { return _onPressed; }
|
|
}
|
|
|
|
VoidCallback _onPressed;
|
|
|
|
public float? pressElevation {
|
|
get { return _pressElevation; }
|
|
}
|
|
|
|
float? _pressElevation;
|
|
|
|
public bool? selected {
|
|
get { return _selected; }
|
|
}
|
|
|
|
bool? _selected;
|
|
|
|
public bool? isEnabled {
|
|
get { return _isEnabled; }
|
|
}
|
|
|
|
bool? _isEnabled;
|
|
|
|
public Color disabledColor {
|
|
get { return _disabledColor; }
|
|
}
|
|
|
|
Color _disabledColor;
|
|
|
|
public Color selectedColor {
|
|
get { return _selectedColor; }
|
|
}
|
|
|
|
Color _selectedColor;
|
|
|
|
public string tooltip {
|
|
get { return _tooltip; }
|
|
}
|
|
|
|
string _tooltip;
|
|
|
|
public ShapeBorder shape {
|
|
get { return _shape; }
|
|
}
|
|
|
|
ShapeBorder _shape;
|
|
|
|
public Clip clipBehavior {
|
|
get { return _clipBehavior; }
|
|
}
|
|
|
|
Clip _clipBehavior;
|
|
|
|
public FocusNode focusNode { get; }
|
|
|
|
public bool autofocus { get; }
|
|
|
|
public Color backgroundColor {
|
|
get { return _backgroundColor; }
|
|
}
|
|
|
|
Color _backgroundColor;
|
|
|
|
public EdgeInsetsGeometry padding {
|
|
get { return _padding; }
|
|
}
|
|
|
|
EdgeInsetsGeometry _padding;
|
|
|
|
public VisualDensity visualDensity { get; }
|
|
|
|
public MaterialTapTargetSize? materialTapTargetSize {
|
|
get { return _materialTapTargetSize; }
|
|
}
|
|
|
|
MaterialTapTargetSize? _materialTapTargetSize;
|
|
|
|
public float? elevation {
|
|
get { return _elevation; }
|
|
}
|
|
|
|
float? _elevation;
|
|
|
|
public Color shadowColor {
|
|
get { return _shadowColor; }
|
|
}
|
|
|
|
Color _shadowColor;
|
|
|
|
public Color selectedShadowColor {
|
|
get { return _selectedShadowColor; }
|
|
}
|
|
|
|
Color _selectedShadowColor;
|
|
|
|
public bool? showCheckmark { get; }
|
|
|
|
public Color checkmarkColor { get; }
|
|
|
|
public ShapeBorder avatarBorder {
|
|
get { return _avatarBorder; }
|
|
}
|
|
|
|
ShapeBorder _avatarBorder;
|
|
|
|
public bool? tapEnabled {
|
|
get { return _tapEnabled; }
|
|
}
|
|
|
|
bool? _tapEnabled;
|
|
|
|
public override State createState() {
|
|
return new _RawChipState();
|
|
}
|
|
}
|
|
|
|
class _RawChipState : TickerProviderStateMixin<RawChip> {
|
|
static readonly TimeSpan pressedAnimationDuration = new TimeSpan(0, 0, 0, 0, 75);
|
|
|
|
AnimationController selectController;
|
|
AnimationController avatarDrawerController;
|
|
AnimationController deleteDrawerController;
|
|
AnimationController enableController;
|
|
Animation<float> checkmarkAnimation;
|
|
Animation<float> avatarDrawerAnimation;
|
|
Animation<float> deleteDrawerAnimation;
|
|
Animation<float> enableAnimation;
|
|
Animation<float> selectionFade;
|
|
|
|
|
|
readonly HashSet<MaterialState> _states = new HashSet<MaterialState>();
|
|
|
|
public readonly GlobalKey deleteIconKey = GlobalKey.key();
|
|
|
|
|
|
public bool hasDeleteButton {
|
|
get { return widget.onDeleted != null; }
|
|
}
|
|
|
|
public bool hasAvatar {
|
|
get { return widget.avatar != null; }
|
|
}
|
|
|
|
public bool canTap {
|
|
get {
|
|
return widget.isEnabled == true
|
|
&& widget.tapEnabled == true
|
|
&& (widget.onPressed != null || widget.onSelected != null);
|
|
}
|
|
}
|
|
|
|
bool _isTapping = false;
|
|
|
|
public bool isTapping {
|
|
get { return canTap && _isTapping; }
|
|
}
|
|
|
|
public override void initState() {
|
|
D.assert(widget.onSelected == null || widget.onPressed == null);
|
|
base.initState();
|
|
_updateState(MaterialState.disabled, !(widget.isEnabled != null && widget.isEnabled.Value));
|
|
_updateState(MaterialState.selected, widget.selected != null && widget.selected.Value);
|
|
selectController = new AnimationController(
|
|
duration: ChipUtils._kSelectDuration,
|
|
value: widget.selected == true ? 1.0f : 0.0f,
|
|
vsync: this
|
|
);
|
|
selectionFade = new CurvedAnimation(
|
|
parent: selectController,
|
|
curve: Curves.fastOutSlowIn
|
|
);
|
|
avatarDrawerController = new AnimationController(
|
|
duration: ChipUtils._kDrawerDuration,
|
|
value: hasAvatar || widget.selected == true ? 1.0f : 0.0f,
|
|
vsync: this
|
|
);
|
|
deleteDrawerController = new AnimationController(
|
|
duration: ChipUtils._kDrawerDuration,
|
|
value: hasDeleteButton ? 1.0f : 0.0f,
|
|
vsync: this
|
|
);
|
|
enableController = new AnimationController(
|
|
duration: ChipUtils._kDisableDuration,
|
|
value: widget.isEnabled == true ? 1.0f : 0.0f,
|
|
vsync: this
|
|
);
|
|
|
|
float checkmarkPercentage = (float) (ChipUtils._kCheckmarkDuration.TotalMilliseconds /
|
|
ChipUtils._kSelectDuration.TotalMilliseconds);
|
|
float checkmarkReversePercentage = (float) (ChipUtils._kCheckmarkReverseDuration.TotalMilliseconds /
|
|
ChipUtils._kSelectDuration.TotalMilliseconds);
|
|
float avatarDrawerReversePercentage = (float) (ChipUtils._kReverseDrawerDuration.TotalMilliseconds /
|
|
ChipUtils._kSelectDuration.TotalMilliseconds);
|
|
checkmarkAnimation = new CurvedAnimation(
|
|
parent: selectController,
|
|
curve: new Interval(1.0f - checkmarkPercentage, 1.0f, curve: Curves.fastOutSlowIn),
|
|
reverseCurve: new Interval(
|
|
1.0f - checkmarkReversePercentage,
|
|
1.0f,
|
|
curve: Curves.fastOutSlowIn
|
|
)
|
|
);
|
|
deleteDrawerAnimation = new CurvedAnimation(
|
|
parent: deleteDrawerController,
|
|
curve: Curves.fastOutSlowIn
|
|
);
|
|
avatarDrawerAnimation = new CurvedAnimation(
|
|
parent: avatarDrawerController,
|
|
curve: Curves.fastOutSlowIn,
|
|
reverseCurve: new Interval(
|
|
1.0f - avatarDrawerReversePercentage,
|
|
1.0f,
|
|
curve: Curves.fastOutSlowIn
|
|
)
|
|
);
|
|
enableAnimation = new CurvedAnimation(
|
|
parent: enableController,
|
|
curve: Curves.fastOutSlowIn
|
|
);
|
|
}
|
|
|
|
public override void dispose() {
|
|
selectController.dispose();
|
|
avatarDrawerController.dispose();
|
|
deleteDrawerController.dispose();
|
|
enableController.dispose();
|
|
base.dispose();
|
|
}
|
|
|
|
void _updateState(MaterialState state, bool value) {
|
|
if (value) {
|
|
_states.Add(state);
|
|
}
|
|
else {
|
|
_states.Remove(state);
|
|
}
|
|
}
|
|
|
|
void _handleTapDown(TapDownDetails details) {
|
|
if (!canTap) {
|
|
return;
|
|
}
|
|
|
|
setState(() => {
|
|
_isTapping = true;
|
|
_updateState(MaterialState.pressed, true);
|
|
});
|
|
}
|
|
|
|
void _handleTapCancel() {
|
|
if (!canTap) {
|
|
return;
|
|
}
|
|
|
|
setState(() => {
|
|
_isTapping = false;
|
|
_updateState(MaterialState.pressed, false);
|
|
});
|
|
}
|
|
|
|
void _handleTap() {
|
|
if (!canTap) {
|
|
return;
|
|
}
|
|
|
|
setState(() => {
|
|
_isTapping = false;
|
|
_updateState(MaterialState.pressed, false);
|
|
});
|
|
widget.onSelected?.Invoke(!widget.selected == true);
|
|
widget.onPressed?.Invoke();
|
|
}
|
|
|
|
void _handleFocus(bool isFocused) {
|
|
setState(() => { _updateState(MaterialState.focused, isFocused); });
|
|
}
|
|
|
|
void _handleHover(bool isHovered) {
|
|
setState(() => { _updateState(MaterialState.hovered, isHovered); });
|
|
}
|
|
|
|
Color getBackgroundColor(ChipThemeData theme) {
|
|
ColorTween backgroundTween = new ColorTween(
|
|
begin: widget.disabledColor ?? theme.disabledColor,
|
|
end: widget.backgroundColor ?? theme.backgroundColor
|
|
);
|
|
ColorTween selectTween = new ColorTween(
|
|
begin: backgroundTween.evaluate(enableController),
|
|
end: widget.selectedColor ?? theme.selectedColor
|
|
);
|
|
return selectTween.evaluate(selectionFade);
|
|
}
|
|
|
|
public override void didUpdateWidget(StatefulWidget _oldWidget) {
|
|
RawChip oldWidget = _oldWidget as RawChip;
|
|
base.didUpdateWidget(oldWidget);
|
|
if (oldWidget.isEnabled != widget.isEnabled) {
|
|
setState(() => {
|
|
_updateState(MaterialState.disabled, !(widget.isEnabled != null && widget.isEnabled.Value));
|
|
if (widget.isEnabled == true) {
|
|
enableController.forward();
|
|
}
|
|
else {
|
|
enableController.reverse();
|
|
}
|
|
});
|
|
}
|
|
|
|
if (oldWidget.avatar != widget.avatar || oldWidget.selected != widget.selected) {
|
|
setState(() => {
|
|
if (hasAvatar || widget.selected == true) {
|
|
avatarDrawerController.forward();
|
|
}
|
|
else {
|
|
avatarDrawerController.reverse();
|
|
}
|
|
});
|
|
}
|
|
|
|
if (oldWidget.selected != widget.selected) {
|
|
setState(() => {
|
|
_updateState(MaterialState.selected, widget.selected != null && widget.selected.Value);
|
|
if (widget.selected == true) {
|
|
selectController.forward();
|
|
}
|
|
else {
|
|
selectController.reverse();
|
|
}
|
|
});
|
|
}
|
|
|
|
if (oldWidget.onDeleted != widget.onDeleted) {
|
|
setState(() => {
|
|
if (hasDeleteButton) {
|
|
deleteDrawerController.forward();
|
|
}
|
|
else {
|
|
deleteDrawerController.reverse();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
Widget _wrapWithTooltip(string tooltip, VoidCallback callback, Widget child) {
|
|
if (child == null || callback == null || tooltip == null) {
|
|
return child;
|
|
}
|
|
|
|
return new Tooltip(
|
|
message: tooltip,
|
|
child: child
|
|
);
|
|
}
|
|
|
|
Widget _buildDeleteIcon(
|
|
BuildContext context,
|
|
ThemeData theme,
|
|
ChipThemeData chipTheme,
|
|
GlobalKey deleteIconKey
|
|
) {
|
|
if (!hasDeleteButton) {
|
|
return null;
|
|
}
|
|
|
|
return _wrapWithTooltip(
|
|
widget.deleteButtonTooltipMessage ?? MaterialLocalizations.of(context)?.deleteButtonTooltip,
|
|
widget.onDeleted,
|
|
new GestureDetector(
|
|
key: deleteIconKey,
|
|
behavior: HitTestBehavior.opaque,
|
|
onTap: widget.isEnabled != null && widget.isEnabled.Value
|
|
? () => {
|
|
Feedback.forTap(context);
|
|
widget.onDeleted();
|
|
}
|
|
: (GestureTapCallback) null,
|
|
child: new IconTheme(
|
|
data: theme.iconTheme.copyWith(
|
|
color: widget.deleteIconColor ?? chipTheme.deleteIconColor
|
|
),
|
|
child: widget.deleteIcon
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
const float _defaultElevation = 0.0f;
|
|
const float _defaultPressElevation = 8.0f;
|
|
static readonly Color _defaultShadowColor = Colors.black;
|
|
|
|
public override Widget build(BuildContext context) {
|
|
D.assert(material_.debugCheckHasMaterial(context));
|
|
D.assert(WidgetsD.debugCheckHasMediaQuery(context));
|
|
D.assert(WidgetsD.debugCheckHasDirectionality(context));
|
|
D.assert(material_.debugCheckHasMaterialLocalizations(context));
|
|
|
|
ThemeData theme = Theme.of(context);
|
|
ChipThemeData chipTheme = ChipTheme.of(context);
|
|
TextDirection textDirection = Directionality.of(context);
|
|
ShapeBorder shape = widget.shape ?? chipTheme.shape;
|
|
float elevation = widget.elevation ?? (chipTheme.elevation ?? _defaultElevation);
|
|
float pressElevation = widget.pressElevation ?? (chipTheme.pressElevation ?? _defaultPressElevation);
|
|
Color shadowColor = widget.shadowColor ?? chipTheme.shadowColor ?? _defaultShadowColor;
|
|
Color selectedShadowColor =
|
|
widget.selectedShadowColor ?? chipTheme.selectedShadowColor ?? _defaultShadowColor;
|
|
Color checkmarkColor = widget.checkmarkColor ?? chipTheme.checkmarkColor;
|
|
bool showCheckmark = widget.showCheckmark ?? chipTheme.showCheckmark ?? true;
|
|
|
|
TextStyle effectiveLabelStyle = widget.labelStyle ?? chipTheme.labelStyle;
|
|
Color resolvedLabelColor = MaterialStateProperty<Color>.resolveAs(effectiveLabelStyle?.color, _states);
|
|
TextStyle resolvedLabelStyle = effectiveLabelStyle?.copyWith(color: resolvedLabelColor);
|
|
|
|
Widget result = new Material(
|
|
elevation: isTapping ? pressElevation : elevation,
|
|
shadowColor: widget.selected ?? false ? selectedShadowColor : shadowColor,
|
|
animationDuration: pressedAnimationDuration,
|
|
shape: shape,
|
|
clipBehavior: widget.clipBehavior,
|
|
child: new InkWell(
|
|
onFocusChange: _handleFocus,
|
|
focusNode: widget.focusNode,
|
|
autofocus: widget.autofocus,
|
|
canRequestFocus: widget.isEnabled ?? false,
|
|
onTap: canTap ? _handleTap : (GestureTapCallback) null,
|
|
onTapDown: canTap ? _handleTapDown : (GestureTapDownCallback) null,
|
|
onTapCancel: canTap ? _handleTapCancel : (GestureTapCancelCallback) null,
|
|
onHover: canTap ? _handleHover : (ValueChanged<bool>) null,
|
|
splashFactory: new _LocationAwareInkRippleFactory(
|
|
hasDeleteButton,
|
|
context,
|
|
deleteIconKey
|
|
),
|
|
customBorder: shape,
|
|
child: new AnimatedBuilder(
|
|
animation: ListenableUtils.merge(new List<Listenable>
|
|
{selectController, enableController}),
|
|
builder: (BuildContext _context, Widget child) => {
|
|
return new Container(
|
|
decoration: new ShapeDecoration(
|
|
shape: shape,
|
|
color: getBackgroundColor(chipTheme)
|
|
),
|
|
child: child
|
|
);
|
|
},
|
|
child: _wrapWithTooltip(widget.tooltip, widget.onPressed,
|
|
new _ChipRenderWidget(
|
|
theme: new _ChipRenderTheme(
|
|
label: new DefaultTextStyle(
|
|
overflow: TextOverflow.fade,
|
|
textAlign: TextAlign.left,
|
|
maxLines: 1,
|
|
softWrap: false,
|
|
style: resolvedLabelStyle,
|
|
child: widget.label
|
|
),
|
|
avatar: new AnimatedSwitcher(
|
|
child: widget.avatar,
|
|
duration: ChipUtils._kDrawerDuration,
|
|
switchInCurve: Curves.fastOutSlowIn
|
|
),
|
|
deleteIcon: new AnimatedSwitcher(
|
|
child: _buildDeleteIcon(context, theme, chipTheme, deleteIconKey),
|
|
duration: ChipUtils._kDrawerDuration,
|
|
switchInCurve: Curves.fastOutSlowIn
|
|
),
|
|
brightness: chipTheme.brightness,
|
|
padding: (widget.padding ?? chipTheme.padding).resolve(textDirection),
|
|
visualDensity: widget.visualDensity ?? theme.visualDensity,
|
|
labelPadding:
|
|
(widget.labelPadding ?? chipTheme.labelPadding).resolve(textDirection),
|
|
showAvatar: hasAvatar,
|
|
showCheckmark: showCheckmark,
|
|
checkmarkColor: checkmarkColor,
|
|
canTapBody: canTap
|
|
),
|
|
value: widget.selected,
|
|
checkmarkAnimation: checkmarkAnimation,
|
|
enableAnimation: enableAnimation,
|
|
avatarDrawerAnimation: avatarDrawerAnimation,
|
|
deleteDrawerAnimation: deleteDrawerAnimation,
|
|
isEnabled: widget.isEnabled,
|
|
avatarBorder: widget.avatarBorder
|
|
)
|
|
)
|
|
)
|
|
)
|
|
);
|
|
BoxConstraints constraints;
|
|
Offset densityAdjustment = (widget.visualDensity ?? theme.visualDensity).baseSizeAdjustment;
|
|
switch (widget.materialTapTargetSize ?? theme.materialTapTargetSize) {
|
|
case MaterialTapTargetSize.padded:
|
|
constraints =
|
|
new BoxConstraints(minHeight: material_.kMinInteractiveDimension + densityAdjustment.dy);
|
|
break;
|
|
case MaterialTapTargetSize.shrinkWrap:
|
|
constraints = new BoxConstraints();
|
|
break;
|
|
default:
|
|
throw new Exception("Unknown Material Tap Target Size: " + widget.materialTapTargetSize);
|
|
}
|
|
|
|
result = new _ChipRedirectingHitDetectionWidget(
|
|
constraints: constraints,
|
|
child: new Center(
|
|
child: result,
|
|
widthFactor: 1.0f,
|
|
heightFactor: 1.0f
|
|
)
|
|
);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
class _ChipRedirectingHitDetectionWidget : SingleChildRenderObjectWidget {
|
|
public _ChipRedirectingHitDetectionWidget(
|
|
Key key = null,
|
|
Widget child = null,
|
|
BoxConstraints constraints = null
|
|
) : base(key: key, child: child) {
|
|
this.constraints = constraints;
|
|
}
|
|
|
|
public readonly BoxConstraints constraints;
|
|
|
|
public override RenderObject createRenderObject(BuildContext context) {
|
|
return new _RenderChipRedirectingHitDetection(constraints);
|
|
}
|
|
|
|
public override void updateRenderObject(BuildContext context, RenderObject _renderObject) {
|
|
_RenderChipRedirectingHitDetection renderObject = _renderObject as _RenderChipRedirectingHitDetection;
|
|
renderObject.additionalConstraints = constraints;
|
|
}
|
|
}
|
|
|
|
class _RenderChipRedirectingHitDetection : RenderConstrainedBox {
|
|
public _RenderChipRedirectingHitDetection(BoxConstraints additionalConstraints) : base(
|
|
additionalConstraints: additionalConstraints) {
|
|
}
|
|
|
|
public override bool hitTest(BoxHitTestResult result, Offset position = null) {
|
|
if (!size.contains(position)) {
|
|
return false;
|
|
}
|
|
|
|
Offset offset = new Offset(position.dx, size.height / 2);
|
|
return result.addWithRawTransform(
|
|
transform: MatrixUtils.forceToPoint(offset),
|
|
position: position,
|
|
hitTest: (BoxHitTestResult _result, Offset _position) => {
|
|
D.assert(_position == offset);
|
|
return child.hitTest(_result, position: offset);
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
class _ChipRenderWidget : RenderObjectWidget {
|
|
public _ChipRenderWidget(
|
|
Key key = null,
|
|
_ChipRenderTheme theme = null,
|
|
bool? value = null,
|
|
bool? isEnabled = null,
|
|
Animation<float> checkmarkAnimation = null,
|
|
Animation<float> avatarDrawerAnimation = null,
|
|
Animation<float> deleteDrawerAnimation = null,
|
|
Animation<float> enableAnimation = null,
|
|
ShapeBorder avatarBorder = null
|
|
) : base(key: key) {
|
|
D.assert(theme != null);
|
|
this.theme = theme;
|
|
this.value = value;
|
|
this.isEnabled = isEnabled;
|
|
this.checkmarkAnimation = checkmarkAnimation;
|
|
this.avatarDrawerAnimation = avatarDrawerAnimation;
|
|
this.deleteDrawerAnimation = deleteDrawerAnimation;
|
|
this.enableAnimation = enableAnimation;
|
|
this.avatarBorder = avatarBorder;
|
|
}
|
|
|
|
public readonly _ChipRenderTheme theme;
|
|
public readonly bool? value;
|
|
public readonly bool? isEnabled;
|
|
public readonly Animation<float> checkmarkAnimation;
|
|
public readonly Animation<float> avatarDrawerAnimation;
|
|
public readonly Animation<float> deleteDrawerAnimation;
|
|
public readonly Animation<float> enableAnimation;
|
|
public readonly ShapeBorder avatarBorder;
|
|
|
|
public override Element createElement() {
|
|
return new _RenderChipElement(this);
|
|
}
|
|
|
|
public override void updateRenderObject(BuildContext context, RenderObject _renderObject) {
|
|
_RenderChip renderObject = _renderObject as _RenderChip;
|
|
renderObject.theme = theme;
|
|
renderObject.textDirection = Directionality.of(context);
|
|
renderObject.value = value ?? false;
|
|
renderObject.isEnabled = isEnabled ?? false;
|
|
renderObject.checkmarkAnimation = checkmarkAnimation;
|
|
renderObject.avatarDrawerAnimation = avatarDrawerAnimation;
|
|
renderObject.deleteDrawerAnimation = deleteDrawerAnimation;
|
|
renderObject.enableAnimation = enableAnimation;
|
|
renderObject.avatarBorder = avatarBorder;
|
|
}
|
|
|
|
public override RenderObject createRenderObject(BuildContext context) {
|
|
return new _RenderChip(
|
|
theme: theme,
|
|
textDirection: Directionality.of(context),
|
|
value: value,
|
|
isEnabled: isEnabled,
|
|
checkmarkAnimation: checkmarkAnimation,
|
|
avatarDrawerAnimation: avatarDrawerAnimation,
|
|
deleteDrawerAnimation: deleteDrawerAnimation,
|
|
enableAnimation: enableAnimation,
|
|
avatarBorder: avatarBorder
|
|
);
|
|
}
|
|
}
|
|
|
|
enum _ChipSlot {
|
|
label,
|
|
avatar,
|
|
deleteIcon
|
|
}
|
|
|
|
class _RenderChipElement : RenderObjectElement {
|
|
public _RenderChipElement(_ChipRenderWidget chip) : base(chip) {
|
|
}
|
|
|
|
Dictionary<_ChipSlot, Element> slotToChild = new Dictionary<_ChipSlot, Element> { };
|
|
Dictionary<Element, _ChipSlot> childToSlot = new Dictionary<Element, _ChipSlot> { };
|
|
|
|
public new _ChipRenderWidget widget {
|
|
get { return (_ChipRenderWidget) base.widget; }
|
|
}
|
|
|
|
public new _RenderChip renderObject {
|
|
get { return (_RenderChip) base.renderObject; }
|
|
}
|
|
|
|
public override void visitChildren(ElementVisitor visitor) {
|
|
slotToChild.Values.Each((value) => { visitor(value); });
|
|
}
|
|
|
|
internal override void forgetChild(Element child) {
|
|
D.assert(slotToChild.ContainsValue(child));
|
|
D.assert(childToSlot.ContainsKey(child));
|
|
_ChipSlot slot = childToSlot[child];
|
|
childToSlot.Remove(child);
|
|
slotToChild.Remove(slot);
|
|
base.forgetChild(child);
|
|
}
|
|
|
|
void _mountChild(Widget widget, _ChipSlot slot) {
|
|
Element oldChild = slotToChild.getOrDefault(slot);
|
|
Element newChild = updateChild(oldChild, widget, slot);
|
|
if (oldChild != null) {
|
|
slotToChild.Remove(slot);
|
|
childToSlot.Remove(oldChild);
|
|
}
|
|
|
|
if (newChild != null) {
|
|
slotToChild[slot] = newChild;
|
|
childToSlot[newChild] = slot;
|
|
}
|
|
}
|
|
|
|
public override void mount(Element parent, object newSlot) {
|
|
base.mount(parent, newSlot);
|
|
_mountChild(widget.theme.avatar, _ChipSlot.avatar);
|
|
_mountChild(widget.theme.deleteIcon, _ChipSlot.deleteIcon);
|
|
_mountChild(widget.theme.label, _ChipSlot.label);
|
|
}
|
|
|
|
void _updateChild(Widget widget, _ChipSlot slot) {
|
|
Element oldChild = slotToChild[slot];
|
|
Element newChild = updateChild(oldChild, widget, slot);
|
|
if (oldChild != null) {
|
|
childToSlot.Remove(oldChild);
|
|
slotToChild.Remove(slot);
|
|
}
|
|
|
|
if (newChild != null) {
|
|
slotToChild[slot] = newChild;
|
|
childToSlot[newChild] = slot;
|
|
}
|
|
}
|
|
|
|
public override void update(Widget _newWidget) {
|
|
_ChipRenderWidget newWidget = _newWidget as _ChipRenderWidget;
|
|
base.update(newWidget);
|
|
D.assert(widget == newWidget);
|
|
_updateChild(widget.theme.label, _ChipSlot.label);
|
|
_updateChild(widget.theme.avatar, _ChipSlot.avatar);
|
|
_updateChild(widget.theme.deleteIcon, _ChipSlot.deleteIcon);
|
|
}
|
|
|
|
void _updateRenderObject(RenderObject child, _ChipSlot slot) {
|
|
switch (slot) {
|
|
case _ChipSlot.avatar:
|
|
renderObject.avatar = (RenderBox) child;
|
|
break;
|
|
case _ChipSlot.label:
|
|
renderObject.label = (RenderBox) child;
|
|
break;
|
|
case _ChipSlot.deleteIcon:
|
|
renderObject.deleteIcon = (RenderBox) child;
|
|
break;
|
|
}
|
|
}
|
|
|
|
protected override void insertChildRenderObject(RenderObject child, object slotValue) {
|
|
D.assert(child is RenderBox);
|
|
D.assert(slotValue is _ChipSlot);
|
|
_ChipSlot slot = (_ChipSlot) slotValue;
|
|
_updateRenderObject(child, slot);
|
|
D.assert(renderObject.childToSlot.ContainsKey((RenderBox) child));
|
|
D.assert(renderObject.slotToChild.ContainsKey(slot));
|
|
}
|
|
|
|
protected override void removeChildRenderObject(RenderObject child) {
|
|
D.assert(child is RenderBox);
|
|
D.assert(renderObject.childToSlot.ContainsKey((RenderBox) child));
|
|
_updateRenderObject(null, renderObject.childToSlot[(RenderBox) child]);
|
|
D.assert(!renderObject.childToSlot.ContainsKey((RenderBox) child));
|
|
D.assert(!renderObject.slotToChild.ContainsKey((_ChipSlot) slot));
|
|
}
|
|
|
|
protected override void moveChildRenderObject(RenderObject child, object slotValue) {
|
|
D.assert(false, () => "not reachable");
|
|
}
|
|
}
|
|
|
|
class _ChipRenderTheme {
|
|
public _ChipRenderTheme(
|
|
Widget avatar = null,
|
|
Widget label = null,
|
|
Widget deleteIcon = null,
|
|
Brightness? brightness = null,
|
|
EdgeInsets padding = null,
|
|
VisualDensity visualDensity = null,
|
|
EdgeInsets labelPadding = null,
|
|
bool? showAvatar = null,
|
|
bool? showCheckmark = null,
|
|
Color checkmarkColor = null,
|
|
bool? canTapBody = null
|
|
) {
|
|
this.avatar = avatar;
|
|
this.label = label;
|
|
this.deleteIcon = deleteIcon;
|
|
this.brightness = brightness;
|
|
this.padding = padding;
|
|
this.visualDensity = visualDensity;
|
|
this.labelPadding = labelPadding;
|
|
this.showAvatar = showAvatar;
|
|
this.showCheckmark = showCheckmark;
|
|
this.checkmarkColor = checkmarkColor;
|
|
this.canTapBody = canTapBody;
|
|
}
|
|
|
|
public readonly Widget avatar;
|
|
public readonly Widget label;
|
|
public readonly Widget deleteIcon;
|
|
public readonly Brightness? brightness;
|
|
public readonly EdgeInsets padding;
|
|
public readonly VisualDensity visualDensity;
|
|
public readonly EdgeInsets labelPadding;
|
|
public readonly bool? showAvatar;
|
|
public readonly bool? showCheckmark;
|
|
public readonly Color checkmarkColor;
|
|
public readonly bool? canTapBody;
|
|
|
|
public override bool Equals(object obj) {
|
|
if (ReferenceEquals(null, obj)) {
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, obj)) {
|
|
return true;
|
|
}
|
|
|
|
if (obj.GetType() != GetType()) {
|
|
return false;
|
|
}
|
|
|
|
return Equals((_ChipRenderTheme) obj);
|
|
}
|
|
|
|
public bool Equals(_ChipRenderTheme other) {
|
|
return avatar == other.avatar
|
|
&& label == other.label
|
|
&& deleteIcon == other.deleteIcon
|
|
&& brightness == other.brightness
|
|
&& padding == other.padding
|
|
&& labelPadding == other.labelPadding
|
|
&& showAvatar == other.showAvatar
|
|
&& showCheckmark == other.showCheckmark
|
|
&& checkmarkColor == other.checkmarkColor
|
|
&& canTapBody == other.canTapBody;
|
|
}
|
|
|
|
public static bool operator ==(_ChipRenderTheme left, _ChipRenderTheme right) {
|
|
return Equals(left, right);
|
|
}
|
|
|
|
public static bool operator !=(_ChipRenderTheme left, _ChipRenderTheme right) {
|
|
return !Equals(left, right);
|
|
}
|
|
|
|
public override int GetHashCode() {
|
|
var hashCode = avatar.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ label.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ deleteIcon.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ brightness.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ padding.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ labelPadding.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ showAvatar.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ showCheckmark.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ checkmarkColor.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ canTapBody.GetHashCode();
|
|
return hashCode;
|
|
}
|
|
}
|
|
|
|
class _RenderChip : RenderBox {
|
|
public _RenderChip(
|
|
_ChipRenderTheme theme = null,
|
|
TextDirection textDirection = TextDirection.ltr,
|
|
bool? value = null,
|
|
bool? isEnabled = null,
|
|
Animation<float> checkmarkAnimation = null,
|
|
Animation<float> avatarDrawerAnimation = null,
|
|
Animation<float> deleteDrawerAnimation = null,
|
|
Animation<float> enableAnimation = null,
|
|
ShapeBorder avatarBorder = null
|
|
) {
|
|
D.assert(theme != null);
|
|
_theme = theme;
|
|
this.textDirection = textDirection;
|
|
checkmarkAnimation.addListener(markNeedsPaint);
|
|
avatarDrawerAnimation.addListener(markNeedsLayout);
|
|
deleteDrawerAnimation.addListener(markNeedsLayout);
|
|
enableAnimation.addListener(markNeedsPaint);
|
|
this.value = value;
|
|
this.isEnabled = isEnabled;
|
|
this.checkmarkAnimation = checkmarkAnimation;
|
|
this.avatarDrawerAnimation = avatarDrawerAnimation;
|
|
this.deleteDrawerAnimation = deleteDrawerAnimation;
|
|
this.enableAnimation = enableAnimation;
|
|
this.avatarBorder = avatarBorder;
|
|
}
|
|
|
|
public Dictionary<_ChipSlot, RenderBox> slotToChild = new Dictionary<_ChipSlot, RenderBox> { };
|
|
public Dictionary<RenderBox, _ChipSlot> childToSlot = new Dictionary<RenderBox, _ChipSlot> { };
|
|
|
|
public bool? value;
|
|
public bool? isEnabled;
|
|
public Rect deleteButtonRect;
|
|
public Rect pressRect;
|
|
public Animation<float> checkmarkAnimation;
|
|
public Animation<float> avatarDrawerAnimation;
|
|
public Animation<float> deleteDrawerAnimation;
|
|
public Animation<float> enableAnimation;
|
|
public ShapeBorder avatarBorder;
|
|
|
|
RenderBox _updateChild(RenderBox oldChild, RenderBox newChild, _ChipSlot slot) {
|
|
if (oldChild != null) {
|
|
dropChild(oldChild);
|
|
childToSlot.Remove(oldChild);
|
|
slotToChild.Remove(slot);
|
|
}
|
|
|
|
if (newChild != null) {
|
|
childToSlot[newChild] = slot;
|
|
slotToChild[slot] = newChild;
|
|
adoptChild(newChild);
|
|
}
|
|
|
|
return newChild;
|
|
}
|
|
|
|
RenderBox _avatar;
|
|
|
|
public RenderBox avatar {
|
|
get { return _avatar; }
|
|
set { _avatar = _updateChild(_avatar, value, _ChipSlot.avatar); }
|
|
}
|
|
|
|
RenderBox _deleteIcon;
|
|
|
|
public RenderBox deleteIcon {
|
|
get { return _deleteIcon; }
|
|
set { _deleteIcon = _updateChild(_deleteIcon, value, _ChipSlot.deleteIcon); }
|
|
}
|
|
|
|
RenderBox _label;
|
|
|
|
public RenderBox label {
|
|
get { return _label; }
|
|
set { _label = _updateChild(_label, value, _ChipSlot.label); }
|
|
}
|
|
|
|
public _ChipRenderTheme theme {
|
|
get { return _theme; }
|
|
set {
|
|
if (_theme == value) {
|
|
return;
|
|
}
|
|
|
|
_theme = value;
|
|
markNeedsLayout();
|
|
}
|
|
}
|
|
|
|
_ChipRenderTheme _theme;
|
|
|
|
IEnumerable<RenderBox> _children {
|
|
get {
|
|
if (avatar != null) {
|
|
yield return avatar;
|
|
}
|
|
|
|
if (label != null) {
|
|
yield return label;
|
|
}
|
|
|
|
if (deleteIcon != null) {
|
|
yield return deleteIcon;
|
|
}
|
|
}
|
|
}
|
|
|
|
public TextDirection textDirection {
|
|
get => _textDirection;
|
|
set {
|
|
if (_textDirection == value) {
|
|
return;
|
|
}
|
|
|
|
_textDirection = value;
|
|
markNeedsLayout();
|
|
}
|
|
}
|
|
|
|
TextDirection _textDirection;
|
|
|
|
public bool isDrawingCheckmark {
|
|
get { return theme.showCheckmark == true && (!(checkmarkAnimation?.isDismissed ?? !value)) == true; }
|
|
}
|
|
|
|
public bool deleteIconShowing {
|
|
get { return !deleteDrawerAnimation.isDismissed; }
|
|
}
|
|
|
|
public override void attach(object _owner) {
|
|
PipelineOwner owner = _owner as PipelineOwner;
|
|
base.attach(owner);
|
|
foreach (RenderBox child in _children) {
|
|
child.attach(owner);
|
|
}
|
|
}
|
|
|
|
public override void detach() {
|
|
base.detach();
|
|
foreach (RenderBox child in _children) {
|
|
child.detach();
|
|
}
|
|
}
|
|
|
|
public override void redepthChildren() {
|
|
_children.Each(redepthChild);
|
|
}
|
|
|
|
public override void visitChildren(RenderObjectVisitor visitor) {
|
|
_children.Each((value) => { visitor(value); });
|
|
}
|
|
|
|
public override List<DiagnosticsNode> debugDescribeChildren() {
|
|
List<DiagnosticsNode> value = new List<DiagnosticsNode> { };
|
|
|
|
void add(RenderBox child, string name) {
|
|
if (child != null) {
|
|
value.Add(child.toDiagnosticsNode(name: name));
|
|
}
|
|
}
|
|
|
|
add(avatar, "avatar");
|
|
add(label, "label");
|
|
add(deleteIcon, "deleteIcon");
|
|
return value;
|
|
}
|
|
|
|
protected override bool sizedByParent {
|
|
get { return false; }
|
|
}
|
|
|
|
static float _minWidth(RenderBox box, float height) {
|
|
return box == null ? 0.0f : box.getMinIntrinsicWidth(height);
|
|
}
|
|
|
|
static float _maxWidth(RenderBox box, float height) {
|
|
return box == null ? 0.0f : box.getMaxIntrinsicWidth(height);
|
|
}
|
|
|
|
static float _minHeight(RenderBox box, float width) {
|
|
return box == null ? 0.0f : box.getMinIntrinsicHeight(width);
|
|
}
|
|
|
|
static Size _boxSize(RenderBox box) {
|
|
return box == null ? Size.zero : box.size;
|
|
}
|
|
|
|
static Rect _boxRect(RenderBox box) {
|
|
return box == null ? Rect.zero : _boxParentData(box).offset & box.size;
|
|
}
|
|
|
|
static BoxParentData _boxParentData(RenderBox box) {
|
|
return (BoxParentData) box.parentData;
|
|
}
|
|
|
|
protected internal override float computeMinIntrinsicWidth(float height) {
|
|
float overallPadding = theme.padding.horizontal + theme.labelPadding.horizontal;
|
|
return overallPadding +
|
|
_minWidth(avatar, height) +
|
|
_minWidth(label, height) +
|
|
_minWidth(deleteIcon, height);
|
|
}
|
|
|
|
protected internal override float computeMaxIntrinsicWidth(float height) {
|
|
float overallPadding = theme.padding.vertical + theme.labelPadding.horizontal;
|
|
return overallPadding +
|
|
_maxWidth(avatar, height) +
|
|
_maxWidth(label, height) +
|
|
_maxWidth(deleteIcon, height);
|
|
}
|
|
|
|
protected internal override float computeMinIntrinsicHeight(float width) {
|
|
return Mathf.Max(
|
|
ChipUtils._kChipHeight,
|
|
theme.padding.vertical + theme.labelPadding.vertical + _minHeight(label, width)
|
|
);
|
|
}
|
|
|
|
protected internal override float computeMaxIntrinsicHeight(float width) {
|
|
return computeMinIntrinsicHeight(width);
|
|
}
|
|
|
|
public override float? computeDistanceToActualBaseline(TextBaseline baseline) {
|
|
return label.getDistanceToActualBaseline(baseline);
|
|
}
|
|
|
|
Size _layoutLabel(float iconSizes, Size size) {
|
|
Size rawSize = _boxSize(label);
|
|
if (constraints.maxWidth.isFinite()) {
|
|
float maxWidth = Mathf.Max(
|
|
0.0f,
|
|
constraints.maxWidth
|
|
- iconSizes
|
|
- theme.labelPadding.horizontal
|
|
- theme.padding.horizontal
|
|
);
|
|
label.layout(constraints.copyWith(
|
|
minWidth: 0.0f,
|
|
maxWidth: maxWidth,
|
|
minHeight: rawSize.height,
|
|
maxHeight: size.height
|
|
),
|
|
parentUsesSize: true
|
|
);
|
|
Size updatedSize = _boxSize(label);
|
|
return new Size(
|
|
updatedSize.width + theme.labelPadding.horizontal,
|
|
updatedSize.height + theme.labelPadding.vertical
|
|
);
|
|
}
|
|
|
|
label.layout(
|
|
new BoxConstraints(
|
|
minHeight: rawSize.height,
|
|
maxHeight: size.height,
|
|
minWidth: 0.0f,
|
|
maxWidth: size.width
|
|
),
|
|
parentUsesSize: true
|
|
);
|
|
|
|
return new Size(
|
|
rawSize.width + theme.labelPadding.horizontal,
|
|
rawSize.height + theme.labelPadding.vertical
|
|
);
|
|
}
|
|
|
|
Size _layoutAvatar(BoxConstraints contentConstraints, float contentSize) {
|
|
float requestedSize = Mathf.Max(0.0f, contentSize);
|
|
BoxConstraints avatarConstraints = BoxConstraints.tightFor(
|
|
width: requestedSize,
|
|
height: requestedSize
|
|
);
|
|
avatar.layout(avatarConstraints, parentUsesSize: true);
|
|
if (theme.showCheckmark != true && theme.showAvatar != true) {
|
|
return new Size(0.0f, contentSize);
|
|
}
|
|
|
|
float avatarWidth = 0.0f;
|
|
float avatarHeight = 0.0f;
|
|
Size avatarBoxSize = _boxSize(avatar);
|
|
if (theme.showAvatar == true) {
|
|
avatarWidth += avatarDrawerAnimation.value * avatarBoxSize.width;
|
|
}
|
|
else {
|
|
avatarWidth += avatarDrawerAnimation.value * contentSize;
|
|
}
|
|
|
|
avatarHeight += avatarBoxSize.height;
|
|
return new Size(avatarWidth, avatarHeight);
|
|
}
|
|
|
|
Size _layoutDeleteIcon(BoxConstraints contentConstraints, float contentSize) {
|
|
float requestedSize = Mathf.Max(0.0f, contentSize);
|
|
BoxConstraints deleteIconConstraints = BoxConstraints.tightFor(
|
|
width: requestedSize,
|
|
height: requestedSize
|
|
);
|
|
deleteIcon.layout(deleteIconConstraints, parentUsesSize: true);
|
|
if (!deleteIconShowing) {
|
|
return new Size(0.0f, contentSize);
|
|
}
|
|
|
|
float deleteIconWidth = 0.0f;
|
|
float deleteIconHeight = 0.0f;
|
|
Size boxSize = _boxSize(deleteIcon);
|
|
deleteIconWidth += deleteDrawerAnimation.value * boxSize.width;
|
|
deleteIconHeight += boxSize.height;
|
|
return new Size(deleteIconWidth, deleteIconHeight);
|
|
}
|
|
|
|
public override bool hitTest(BoxHitTestResult result, Offset position = null) {
|
|
if (!size.contains(position)) {
|
|
return false;
|
|
}
|
|
|
|
bool tapIsOnDeleteIcon = material_._tapIsOnDeleteIcon(
|
|
hasDeleteButton: deleteIcon != null,
|
|
tapPosition: position,
|
|
chipSize: size,
|
|
textDirection: textDirection
|
|
);
|
|
RenderBox hitTestChild = tapIsOnDeleteIcon
|
|
? (deleteIcon ?? label ?? avatar)
|
|
: (label ?? avatar);
|
|
|
|
if (hitTestChild != null) {
|
|
Offset center = hitTestChild.size.center(Offset.zero);
|
|
return result.addWithRawTransform(
|
|
transform: MatrixUtils.forceToPoint(center),
|
|
position: position,
|
|
hitTest: (BoxHitTestResult _result, Offset _position) => {
|
|
D.assert(_position == center);
|
|
return hitTestChild.hitTest(_result, position: center);
|
|
}
|
|
);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
protected override void performLayout() {
|
|
BoxConstraints contentConstraints = constraints.loosen();
|
|
Offset densityAdjustment = new Offset(0.0f, theme.visualDensity.baseSizeAdjustment.dy / 2.0f);
|
|
label.layout(contentConstraints, parentUsesSize: true);
|
|
float contentSize = Mathf.Max(
|
|
ChipUtils._kChipHeight - theme.padding.vertical + theme.labelPadding.vertical,
|
|
_boxSize(label).height + theme.labelPadding.vertical
|
|
);
|
|
Size avatarSize = _layoutAvatar(contentConstraints, contentSize);
|
|
Size deleteIconSize = _layoutDeleteIcon(contentConstraints, contentSize);
|
|
Size labelSize = new Size(_boxSize(label).width, contentSize);
|
|
labelSize = _layoutLabel(avatarSize.width + deleteIconSize.width, labelSize);
|
|
|
|
Size overallSize = new Size(
|
|
avatarSize.width + labelSize.width + deleteIconSize.width,
|
|
contentSize
|
|
) + densityAdjustment;
|
|
|
|
|
|
const float left = 0.0f;
|
|
float right = overallSize.width;
|
|
|
|
Offset centerLayout(Size boxSize, float x) {
|
|
D.assert(contentSize >= boxSize.height);
|
|
Offset boxOffset = null;
|
|
switch (textDirection) {
|
|
case TextDirection.rtl:
|
|
boxOffset = new Offset(x - boxSize.width,
|
|
(contentSize - boxSize.height + densityAdjustment.dy) / 2.0f);
|
|
break;
|
|
case TextDirection.ltr:
|
|
boxOffset = new Offset(x, (contentSize - boxSize.height + densityAdjustment.dy) / 2.0f);
|
|
break;
|
|
}
|
|
|
|
return boxOffset;
|
|
}
|
|
|
|
Offset avatarOffset = Offset.zero;
|
|
Offset labelOffset = Offset.zero;
|
|
Offset deleteIconOffset = Offset.zero;
|
|
switch (textDirection) {
|
|
case TextDirection.rtl:
|
|
float startRight = right;
|
|
if ((theme.showCheckmark ?? false) || (theme.showAvatar ?? false)) {
|
|
avatarOffset = centerLayout(avatarSize, startRight);
|
|
startRight -= avatarSize.width;
|
|
}
|
|
|
|
labelOffset = centerLayout(labelSize, startRight);
|
|
startRight -= labelSize.width;
|
|
if (deleteIconShowing) {
|
|
deleteButtonRect = Rect.fromLTWH(
|
|
0.0f,
|
|
0.0f,
|
|
deleteIconSize.width + theme.padding.right,
|
|
overallSize.height + theme.padding.vertical
|
|
);
|
|
deleteIconOffset = centerLayout(deleteIconSize, startRight);
|
|
}
|
|
else {
|
|
deleteButtonRect = Rect.zero;
|
|
}
|
|
|
|
startRight -= deleteIconSize.width;
|
|
if (theme.canTapBody ?? false) {
|
|
pressRect = Rect.fromLTWH(
|
|
deleteButtonRect.width,
|
|
0.0f,
|
|
overallSize.width - deleteButtonRect.width + theme.padding.horizontal,
|
|
overallSize.height + theme.padding.vertical
|
|
);
|
|
}
|
|
else {
|
|
pressRect = Rect.zero;
|
|
}
|
|
|
|
break;
|
|
case TextDirection.ltr:
|
|
float startLeft = left;
|
|
if ((theme.showCheckmark ?? false) || (theme.showAvatar ?? false)) {
|
|
avatarOffset = centerLayout(avatarSize, startLeft - _boxSize(avatar).width + avatarSize.width);
|
|
startLeft += avatarSize.width;
|
|
}
|
|
|
|
labelOffset = centerLayout(labelSize, startLeft);
|
|
startLeft += labelSize.width;
|
|
if (theme.canTapBody ?? false) {
|
|
pressRect = Rect.fromLTWH(
|
|
0.0f,
|
|
0.0f,
|
|
deleteIconShowing
|
|
? startLeft + theme.padding.left
|
|
: overallSize.width + theme.padding.horizontal,
|
|
overallSize.height + theme.padding.vertical
|
|
);
|
|
}
|
|
else {
|
|
pressRect = Rect.zero;
|
|
}
|
|
|
|
startLeft -= _boxSize(deleteIcon).width - deleteIconSize.width;
|
|
if (deleteIconShowing) {
|
|
deleteIconOffset = centerLayout(deleteIconSize, startLeft);
|
|
deleteButtonRect = Rect.fromLTWH(
|
|
startLeft + theme.padding.left,
|
|
0.0f,
|
|
deleteIconSize.width + theme.padding.right,
|
|
overallSize.height + theme.padding.vertical
|
|
);
|
|
}
|
|
else {
|
|
deleteButtonRect = Rect.zero;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
labelOffset = labelOffset +
|
|
new Offset(
|
|
0.0f,
|
|
((labelSize.height - theme.labelPadding.vertical) - _boxSize(label).height) / 2.0f
|
|
);
|
|
_boxParentData(avatar).offset = theme.padding.topLeft + avatarOffset;
|
|
_boxParentData(label).offset =
|
|
theme.padding.topLeft + labelOffset + theme.labelPadding.topLeft;
|
|
_boxParentData(deleteIcon).offset = theme.padding.topLeft + deleteIconOffset;
|
|
Size paddedSize = new Size(
|
|
overallSize.width + theme.padding.horizontal,
|
|
overallSize.height + theme.padding.vertical
|
|
);
|
|
size = constraints.constrain(paddedSize);
|
|
D.assert(size.height == constraints.constrainHeight(paddedSize.height),
|
|
() => $"Constrained height {size.height} doesn't match expected height " +
|
|
$"{constraints.constrainWidth(paddedSize.height)}");
|
|
D.assert(size.width == constraints.constrainWidth(paddedSize.width),
|
|
() => $"Constrained width {size.width} doesn't match expected width " +
|
|
$"{constraints.constrainWidth(paddedSize.width)}");
|
|
}
|
|
|
|
static ColorTween selectionScrimTween = new ColorTween(
|
|
begin: Colors.transparent,
|
|
end: ChipUtils._kSelectScrimColor
|
|
);
|
|
|
|
Color _disabledColor {
|
|
get {
|
|
if (enableAnimation == null || enableAnimation.isCompleted) {
|
|
return Colors.white;
|
|
}
|
|
|
|
ColorTween enableTween;
|
|
switch (theme.brightness) {
|
|
case Brightness.light:
|
|
enableTween = new ColorTween(
|
|
begin: Colors.white.withAlpha(ChipUtils._kDisabledAlpha),
|
|
end: Colors.white
|
|
);
|
|
break;
|
|
case Brightness.dark:
|
|
enableTween = new ColorTween(
|
|
begin: Colors.black.withAlpha(ChipUtils._kDisabledAlpha),
|
|
end: Colors.black
|
|
);
|
|
break;
|
|
default:
|
|
throw new Exception("Unknown brightness: " + theme.brightness);
|
|
}
|
|
|
|
return enableTween.evaluate(enableAnimation);
|
|
}
|
|
}
|
|
|
|
void _paintCheck(Canvas canvas, Offset origin, float size) {
|
|
Color paintColor;
|
|
if (theme.checkmarkColor != null) {
|
|
paintColor = theme.checkmarkColor;
|
|
}
|
|
else {
|
|
switch (theme.brightness) {
|
|
case Brightness.light:
|
|
paintColor = theme.showAvatar == true
|
|
? Colors.white
|
|
: Colors.black.withAlpha(ChipUtils._kCheckmarkAlpha);
|
|
break;
|
|
case Brightness.dark:
|
|
paintColor = theme.showAvatar == true
|
|
? Colors.black
|
|
: Colors.white.withAlpha(ChipUtils._kCheckmarkAlpha);
|
|
break;
|
|
default:
|
|
throw new Exception("Unknown brightness: " + theme.brightness);
|
|
}
|
|
}
|
|
|
|
ColorTween fadeTween = new ColorTween(begin: Colors.transparent, end: paintColor);
|
|
|
|
paintColor = checkmarkAnimation.status == AnimationStatus.reverse
|
|
? fadeTween.evaluate(checkmarkAnimation)
|
|
: paintColor;
|
|
|
|
Paint paint = new Paint();
|
|
paint.color = paintColor;
|
|
paint.style = PaintingStyle.stroke;
|
|
paint.strokeWidth = ChipUtils._kCheckmarkStrokeWidth *
|
|
(avatar != null ? avatar.size.height / 24.0f : 1.0f);
|
|
float t = checkmarkAnimation.status == AnimationStatus.reverse
|
|
? 1.0f
|
|
: checkmarkAnimation.value;
|
|
if (t == 0.0f) {
|
|
return;
|
|
}
|
|
|
|
D.assert(t > 0.0f && t <= 1.0f);
|
|
Path path = new Path();
|
|
Offset start = new Offset(size * 0.15f, size * 0.45f);
|
|
Offset mid = new Offset(size * 0.4f, size * 0.7f);
|
|
Offset end = new Offset(size * 0.85f, size * 0.25f);
|
|
if (t < 0.5f) {
|
|
float strokeT = t * 2.0f;
|
|
Offset drawMid = Offset.lerp(start, mid, strokeT);
|
|
path.moveTo(origin.dx + start.dx, origin.dy + start.dy);
|
|
path.lineTo(origin.dx + drawMid.dx, origin.dy + drawMid.dy);
|
|
}
|
|
else {
|
|
float strokeT = (t - 0.5f) * 2.0f;
|
|
Offset drawEnd = Offset.lerp(mid, end, strokeT);
|
|
path.moveTo(origin.dx + start.dx, origin.dy + start.dy);
|
|
path.lineTo(origin.dx + mid.dx, origin.dy + mid.dy);
|
|
path.lineTo(origin.dx + drawEnd.dx, origin.dy + drawEnd.dy);
|
|
}
|
|
|
|
canvas.drawPath(path, paint);
|
|
}
|
|
|
|
void _paintSelectionOverlay(PaintingContext context, Offset offset) {
|
|
if (isDrawingCheckmark) {
|
|
if (theme.showAvatar == true) {
|
|
Rect avatarRect = _boxRect(avatar).shift(offset);
|
|
Paint darkenPaint = new Paint();
|
|
darkenPaint.color = selectionScrimTween.evaluate(checkmarkAnimation);
|
|
darkenPaint.blendMode = BlendMode.srcATop;
|
|
Path path = avatarBorder.getOuterPath(avatarRect);
|
|
context.canvas.drawPath(path, darkenPaint);
|
|
}
|
|
|
|
float checkSize = avatar.size.height * 0.75f;
|
|
Offset checkOffset = _boxParentData(avatar).offset +
|
|
new Offset(avatar.size.height * 0.125f, avatar.size.height * 0.125f);
|
|
_paintCheck(context.canvas, offset + checkOffset, checkSize);
|
|
}
|
|
}
|
|
|
|
void _paintAvatar(PaintingContext context, Offset offset) {
|
|
void paintWithOverlay(PaintingContext _context, Offset _offset) {
|
|
_context.paintChild(avatar, _boxParentData(avatar).offset + _offset);
|
|
_paintSelectionOverlay(_context, _offset);
|
|
}
|
|
|
|
if (theme.showAvatar == false && avatarDrawerAnimation.isDismissed) {
|
|
return;
|
|
}
|
|
|
|
Color disabledColor = _disabledColor;
|
|
int disabledColorAlpha = disabledColor.alpha;
|
|
if (needsCompositing) {
|
|
context.pushLayer(new OpacityLayer(alpha: disabledColorAlpha), paintWithOverlay, offset);
|
|
}
|
|
else {
|
|
Paint _paint = new Paint();
|
|
_paint.color = _disabledColor;
|
|
if (disabledColorAlpha != 0xff) {
|
|
context.canvas.saveLayer(_boxRect(avatar).shift(offset).inflate(20.0f), _paint);
|
|
}
|
|
|
|
paintWithOverlay(context, offset);
|
|
if (disabledColorAlpha != 0xff) {
|
|
context.canvas.restore();
|
|
}
|
|
}
|
|
}
|
|
|
|
void _paintChild(PaintingContext context, Offset offset, RenderBox child, bool isEnabled) {
|
|
if (child == null) {
|
|
return;
|
|
}
|
|
|
|
int disabledColorAlpha = _disabledColor.alpha;
|
|
if (!enableAnimation.isCompleted) {
|
|
if (needsCompositing) {
|
|
context.pushLayer(
|
|
new OpacityLayer(alpha: disabledColorAlpha),
|
|
(PaintingContext _context, Offset _offset) => {
|
|
_context.paintChild(child, _boxParentData(child).offset + _offset);
|
|
},
|
|
offset
|
|
);
|
|
}
|
|
else {
|
|
Rect childRect = _boxRect(child).shift(offset);
|
|
Paint _paint = new Paint();
|
|
_paint.color = _disabledColor;
|
|
context.canvas.saveLayer(childRect.inflate(20.0f), _paint);
|
|
context.paintChild(child, _boxParentData(child).offset + offset);
|
|
context.canvas.restore();
|
|
}
|
|
}
|
|
else {
|
|
context.paintChild(child, _boxParentData(child).offset + offset);
|
|
}
|
|
}
|
|
|
|
public override void paint(PaintingContext context, Offset offset) {
|
|
_paintAvatar(context, offset);
|
|
if (deleteIconShowing) {
|
|
_paintChild(context, offset, deleteIcon, isEnabled == true);
|
|
}
|
|
|
|
_paintChild(context, offset, label, isEnabled == true);
|
|
}
|
|
|
|
const bool _debugShowTapTargetOutlines = false;
|
|
|
|
public override void debugPaint(PaintingContext context, Offset offset) {
|
|
bool visualizeTapTargets() {
|
|
Paint outlinePaint = new Paint();
|
|
outlinePaint.color = new Color(0xff800000);
|
|
outlinePaint.strokeWidth = 1.0f;
|
|
outlinePaint.style = PaintingStyle.stroke;
|
|
if (deleteIconShowing) {
|
|
context.canvas.drawRect(deleteButtonRect.shift(offset), outlinePaint);
|
|
}
|
|
|
|
outlinePaint.color = new Color(0xff008000);
|
|
context.canvas.drawRect(pressRect.shift(offset), outlinePaint);
|
|
return true;
|
|
}
|
|
|
|
D.assert(!_debugShowTapTargetOutlines || visualizeTapTargets());
|
|
}
|
|
|
|
protected override bool hitTestSelf(Offset position) {
|
|
return deleteButtonRect.contains(position) || pressRect.contains(position);
|
|
}
|
|
}
|
|
|
|
class _LocationAwareInkRippleFactory : InteractiveInkFeatureFactory {
|
|
internal _LocationAwareInkRippleFactory(
|
|
bool? hasDeleteButton = null,
|
|
BuildContext chipContext = null,
|
|
GlobalKey deleteIconKey = null
|
|
) {
|
|
this.hasDeleteButton = hasDeleteButton;
|
|
this.chipContext = chipContext;
|
|
this.deleteIconKey = deleteIconKey;
|
|
}
|
|
|
|
public readonly bool? hasDeleteButton;
|
|
public readonly BuildContext chipContext;
|
|
public readonly GlobalKey deleteIconKey;
|
|
|
|
public override InteractiveInkFeature create(
|
|
MaterialInkController controller = null,
|
|
RenderBox referenceBox = null,
|
|
Offset position = null,
|
|
Color color = null,
|
|
TextDirection? textDirection = null,
|
|
bool containedInkWell = false,
|
|
RectCallback rectCallback = null,
|
|
BorderRadius borderRadius = null,
|
|
ShapeBorder customBorder = null,
|
|
float? radius = null,
|
|
VoidCallback onRemoved = null
|
|
) {
|
|
bool tapIsOnDeleteIcon = material_._tapIsOnDeleteIcon(
|
|
hasDeleteButton: hasDeleteButton ?? false,
|
|
tapPosition: position,
|
|
chipSize: chipContext.size,
|
|
textDirection: textDirection
|
|
);
|
|
|
|
BuildContext splashContext = tapIsOnDeleteIcon
|
|
? deleteIconKey.currentContext
|
|
: chipContext;
|
|
|
|
InteractiveInkFeatureFactory splashFactory = Theme.of(splashContext).splashFactory;
|
|
|
|
if (tapIsOnDeleteIcon) {
|
|
RenderBox currentBox = referenceBox;
|
|
referenceBox = deleteIconKey.currentContext.findRenderObject() as RenderBox;
|
|
position = referenceBox.globalToLocal(currentBox.localToGlobal(position));
|
|
containedInkWell = false;
|
|
}
|
|
|
|
return splashFactory.create(
|
|
controller: controller,
|
|
referenceBox: referenceBox,
|
|
position: position,
|
|
color: color,
|
|
textDirection: textDirection,
|
|
containedInkWell: containedInkWell,
|
|
rectCallback: rectCallback,
|
|
borderRadius: borderRadius,
|
|
customBorder: customBorder,
|
|
radius: radius,
|
|
onRemoved: onRemoved
|
|
);
|
|
}
|
|
}
|
|
|
|
public partial class material_ {
|
|
internal static bool _tapIsOnDeleteIcon(
|
|
bool hasDeleteButton = false,
|
|
Offset tapPosition = null,
|
|
Size chipSize = null,
|
|
TextDirection? textDirection = null
|
|
) {
|
|
bool tapIsOnDeleteIcon = false;
|
|
if (!hasDeleteButton) {
|
|
tapIsOnDeleteIcon = false;
|
|
}
|
|
else {
|
|
switch (textDirection) {
|
|
case TextDirection.ltr:
|
|
tapIsOnDeleteIcon = tapPosition.dx / chipSize.width > 0.66;
|
|
break;
|
|
case TextDirection.rtl:
|
|
tapIsOnDeleteIcon = tapPosition.dx / chipSize.width < 0.33;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return tapIsOnDeleteIcon;
|
|
}
|
|
}
|
|
}
|