您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
2396 行
83 KiB
2396 行
83 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using RSG.Promises;
|
|
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; }
|
|
|
|
Color backgroundColor { get; }
|
|
|
|
EdgeInsets padding { get; }
|
|
|
|
EdgeInsets labelPadding { get; }
|
|
|
|
MaterialTapTargetSize? materialTapTargetSize { get; }
|
|
|
|
float? elevation { get; }
|
|
}
|
|
|
|
public interface DeletableChipAttributes {
|
|
Widget deleteIcon { get; }
|
|
|
|
VoidCallback onDeleted { get; }
|
|
|
|
Color deleteIconColor { get; }
|
|
|
|
string deleteButtonTooltipMessage { get; }
|
|
}
|
|
|
|
public interface SelectableChipAttributes {
|
|
bool? selected { get; }
|
|
|
|
ValueChanged<bool> onSelected { get; }
|
|
|
|
float? pressElevation { get; }
|
|
|
|
Color selectedColor { get; }
|
|
|
|
string tooltip { get; }
|
|
|
|
ShapeBorder avatarBorder { 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,
|
|
EdgeInsets labelPadding = null,
|
|
Widget deleteIcon = null,
|
|
VoidCallback onDeleted = null,
|
|
Color deleteIconColor = null,
|
|
string deleteButtonTooltipMessage = null,
|
|
ShapeBorder shape = null,
|
|
Clip clipBehavior = Clip.none,
|
|
Color backgroundColor = null,
|
|
EdgeInsets padding = null,
|
|
MaterialTapTargetSize? materialTapTargetSize = null,
|
|
float? elevation = null
|
|
) : base(key: key) {
|
|
D.assert(label != null);
|
|
D.assert(elevation == null || elevation >= 0.0f);
|
|
this._avatar = avatar;
|
|
this._label = label;
|
|
this._labelStyle = labelStyle;
|
|
this._labelPadding = labelPadding;
|
|
this._deleteIcon = deleteIcon;
|
|
this._onDeleted = onDeleted;
|
|
this._deleteIconColor = deleteIconColor;
|
|
this._deleteButtonTooltipMessage = deleteButtonTooltipMessage;
|
|
this._shape = shape;
|
|
this._clipBehavior = clipBehavior;
|
|
this._backgroundColor = backgroundColor;
|
|
this._padding = padding;
|
|
this._materialTapTargetSize = materialTapTargetSize;
|
|
this._elevation = elevation;
|
|
}
|
|
|
|
public Widget avatar {
|
|
get { return this._avatar; }
|
|
}
|
|
|
|
Widget _avatar;
|
|
|
|
public Widget label {
|
|
get { return this._label; }
|
|
}
|
|
|
|
Widget _label;
|
|
|
|
public TextStyle labelStyle {
|
|
get { return this._labelStyle; }
|
|
}
|
|
|
|
TextStyle _labelStyle;
|
|
|
|
public EdgeInsets labelPadding {
|
|
get { return this._labelPadding; }
|
|
}
|
|
|
|
EdgeInsets _labelPadding;
|
|
|
|
public ShapeBorder shape {
|
|
get { return this._shape; }
|
|
}
|
|
|
|
ShapeBorder _shape;
|
|
|
|
public Clip clipBehavior {
|
|
get { return this._clipBehavior; }
|
|
}
|
|
|
|
Clip _clipBehavior;
|
|
|
|
public Color backgroundColor {
|
|
get { return this._backgroundColor; }
|
|
}
|
|
|
|
Color _backgroundColor;
|
|
|
|
public EdgeInsets padding {
|
|
get { return this._padding; }
|
|
}
|
|
|
|
EdgeInsets _padding;
|
|
|
|
public Widget deleteIcon {
|
|
get { return this._deleteIcon; }
|
|
}
|
|
|
|
Widget _deleteIcon;
|
|
|
|
public VoidCallback onDeleted {
|
|
get { return this._onDeleted; }
|
|
}
|
|
|
|
VoidCallback _onDeleted;
|
|
|
|
public Color deleteIconColor {
|
|
get { return this._deleteIconColor; }
|
|
}
|
|
|
|
Color _deleteIconColor;
|
|
|
|
public string deleteButtonTooltipMessage {
|
|
get { return this._deleteButtonTooltipMessage; }
|
|
}
|
|
|
|
string _deleteButtonTooltipMessage;
|
|
|
|
public MaterialTapTargetSize? materialTapTargetSize {
|
|
get { return this._materialTapTargetSize; }
|
|
}
|
|
|
|
MaterialTapTargetSize? _materialTapTargetSize;
|
|
|
|
public float? elevation {
|
|
get { return this._elevation; }
|
|
}
|
|
|
|
float? _elevation;
|
|
|
|
public override Widget build(BuildContext context) {
|
|
D.assert(MaterialD.debugCheckHasMaterial(context));
|
|
return new RawChip(
|
|
avatar: this.avatar,
|
|
label: this.label,
|
|
labelStyle: this.labelStyle,
|
|
labelPadding: this.labelPadding,
|
|
deleteIcon: this.deleteIcon,
|
|
onDeleted: this.onDeleted,
|
|
deleteIconColor: this.deleteIconColor,
|
|
deleteButtonTooltipMessage: this.deleteButtonTooltipMessage,
|
|
tapEnabled: false,
|
|
shape: this.shape,
|
|
clipBehavior: this.clipBehavior,
|
|
backgroundColor: this.backgroundColor,
|
|
padding: this.padding,
|
|
materialTapTargetSize: this.materialTapTargetSize,
|
|
elevation: this.elevation,
|
|
isEnabled: true
|
|
);
|
|
}
|
|
}
|
|
|
|
public class InputChip : StatelessWidget,
|
|
ChipAttributes,
|
|
DeletableChipAttributes,
|
|
SelectableChipAttributes,
|
|
DisabledChipAttributes,
|
|
TappableChipAttributes {
|
|
public InputChip(
|
|
Key key = null,
|
|
Widget avatar = null,
|
|
Widget label = null,
|
|
TextStyle labelStyle = null,
|
|
EdgeInsets 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,
|
|
Color backgroundColor = null,
|
|
EdgeInsets padding = null,
|
|
MaterialTapTargetSize? materialTapTargetSize = null,
|
|
float? elevation = null,
|
|
Color selectedShadowColor = null,
|
|
ShapeBorder avatarBorder = null
|
|
) : base(key: key) {
|
|
D.assert(label != null);
|
|
D.assert(pressElevation == null || pressElevation >= 0.0f);
|
|
D.assert(elevation == null || elevation >= 0.0f);
|
|
this._avatarBorder = avatarBorder ?? new CircleBorder();
|
|
this._avatar = avatar;
|
|
this._label = label;
|
|
this._labelStyle = labelStyle;
|
|
this._labelPadding = labelPadding;
|
|
this._selected = selected;
|
|
this._isEnabled = isEnabled;
|
|
this._onSelected = onSelected;
|
|
this._deleteIcon = deleteIcon;
|
|
this._onDeleted = onDeleted;
|
|
this._deleteIconColor = deleteIconColor;
|
|
this._deleteButtonTooltipMessage = deleteButtonTooltipMessage;
|
|
this._onPressed = onPressed;
|
|
this._pressElevation = pressElevation;
|
|
this._disabledColor = disabledColor;
|
|
this._selectedColor = selectedColor;
|
|
this._tooltip = tooltip;
|
|
this._shape = shape;
|
|
this._clipBehavior = clipBehavior;
|
|
this._backgroundColor = backgroundColor;
|
|
this._padding = padding;
|
|
this._materialTapTargetSize = materialTapTargetSize;
|
|
this._elevation = elevation;
|
|
}
|
|
|
|
public Widget avatar {
|
|
get { return this._avatar; }
|
|
}
|
|
|
|
Widget _avatar;
|
|
|
|
public Widget label {
|
|
get { return this._label; }
|
|
}
|
|
|
|
Widget _label;
|
|
|
|
public TextStyle labelStyle {
|
|
get { return this._labelStyle; }
|
|
}
|
|
|
|
TextStyle _labelStyle;
|
|
|
|
public EdgeInsets labelPadding {
|
|
get { return this._labelPadding; }
|
|
}
|
|
|
|
EdgeInsets _labelPadding;
|
|
|
|
public bool? selected {
|
|
get { return this._selected; }
|
|
}
|
|
|
|
bool _selected;
|
|
|
|
public bool? isEnabled {
|
|
get { return this._isEnabled; }
|
|
}
|
|
|
|
bool _isEnabled;
|
|
|
|
public ValueChanged<bool> onSelected {
|
|
get { return this._onSelected; }
|
|
}
|
|
|
|
ValueChanged<bool> _onSelected;
|
|
|
|
public Widget deleteIcon {
|
|
get { return this._deleteIcon; }
|
|
}
|
|
|
|
Widget _deleteIcon;
|
|
|
|
public VoidCallback onDeleted {
|
|
get { return this._onDeleted; }
|
|
}
|
|
|
|
VoidCallback _onDeleted;
|
|
|
|
public Color deleteIconColor {
|
|
get { return this._deleteIconColor; }
|
|
}
|
|
|
|
Color _deleteIconColor;
|
|
|
|
public string deleteButtonTooltipMessage {
|
|
get { return this._deleteButtonTooltipMessage; }
|
|
}
|
|
|
|
string _deleteButtonTooltipMessage;
|
|
|
|
public VoidCallback onPressed {
|
|
get { return this._onPressed; }
|
|
}
|
|
|
|
VoidCallback _onPressed;
|
|
|
|
public float? pressElevation {
|
|
get { return this._pressElevation; }
|
|
}
|
|
|
|
float? _pressElevation;
|
|
|
|
public Color disabledColor {
|
|
get { return this._disabledColor; }
|
|
}
|
|
|
|
Color _disabledColor;
|
|
|
|
public Color selectedColor {
|
|
get { return this._selectedColor; }
|
|
}
|
|
|
|
Color _selectedColor;
|
|
|
|
public string tooltip {
|
|
get { return this._tooltip; }
|
|
}
|
|
|
|
string _tooltip;
|
|
|
|
public ShapeBorder shape {
|
|
get { return this._shape; }
|
|
}
|
|
|
|
ShapeBorder _shape;
|
|
|
|
public Clip clipBehavior {
|
|
get { return this._clipBehavior; }
|
|
}
|
|
|
|
Clip _clipBehavior;
|
|
|
|
public Color backgroundColor {
|
|
get { return this._backgroundColor; }
|
|
}
|
|
|
|
Color _backgroundColor;
|
|
|
|
public EdgeInsets padding {
|
|
get { return this._padding; }
|
|
}
|
|
|
|
EdgeInsets _padding;
|
|
|
|
public MaterialTapTargetSize? materialTapTargetSize {
|
|
get { return this._materialTapTargetSize; }
|
|
}
|
|
|
|
MaterialTapTargetSize? _materialTapTargetSize;
|
|
|
|
public float? elevation {
|
|
get { return this._elevation; }
|
|
}
|
|
|
|
float? _elevation;
|
|
|
|
public ShapeBorder avatarBorder {
|
|
get { return this._avatarBorder; }
|
|
}
|
|
|
|
ShapeBorder _avatarBorder;
|
|
|
|
public override Widget build(BuildContext context) {
|
|
D.assert(MaterialD.debugCheckHasMaterial(context));
|
|
return new RawChip(
|
|
avatar: this.avatar,
|
|
label: this.label,
|
|
labelStyle: this.labelStyle,
|
|
labelPadding: this.labelPadding,
|
|
deleteIcon: this.deleteIcon,
|
|
onDeleted: this.onDeleted,
|
|
deleteIconColor: this.deleteIconColor,
|
|
deleteButtonTooltipMessage: this.deleteButtonTooltipMessage,
|
|
onSelected: this.onSelected,
|
|
onPressed: this.onPressed,
|
|
pressElevation: this.pressElevation,
|
|
selected: this.selected,
|
|
tapEnabled: true,
|
|
disabledColor: this.disabledColor,
|
|
selectedColor: this.selectedColor,
|
|
tooltip: this.tooltip,
|
|
shape: this.shape,
|
|
clipBehavior: this.clipBehavior,
|
|
backgroundColor: this.backgroundColor,
|
|
padding: this.padding,
|
|
materialTapTargetSize: this.materialTapTargetSize,
|
|
elevation: this.elevation,
|
|
isEnabled: this.isEnabled == true &&
|
|
(this.onSelected != null || this.onDeleted != null || this.onPressed != null),
|
|
avatarBorder: this.avatarBorder
|
|
);
|
|
}
|
|
}
|
|
|
|
public class ChoiceChip : StatelessWidget,
|
|
ChipAttributes,
|
|
SelectableChipAttributes,
|
|
DisabledChipAttributes {
|
|
public ChoiceChip(
|
|
Key key,
|
|
Widget avatar = null,
|
|
Widget label = null,
|
|
TextStyle labelStyle = null,
|
|
EdgeInsets 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,
|
|
Color backgroundColor = null,
|
|
EdgeInsets padding = null,
|
|
MaterialTapTargetSize? materialTapTargetSize = null,
|
|
float? elevation = 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);
|
|
this._avatarBorder = avatarBorder ?? new CircleBorder();
|
|
this._avatar = avatar;
|
|
this._label = label;
|
|
this._labelStyle = labelStyle;
|
|
this._labelPadding = labelPadding;
|
|
this._onSelected = onSelected;
|
|
this._pressElevation = pressElevation;
|
|
this._selected = selected;
|
|
this._selectedColor = selectedColor;
|
|
this._disabledColor = disabledColor;
|
|
this._tooltip = tooltip;
|
|
this._shape = shape;
|
|
this._clipBehavior = clipBehavior;
|
|
this._backgroundColor = backgroundColor;
|
|
this._padding = padding;
|
|
this._materialTapTargetSize = materialTapTargetSize;
|
|
this._elevation = elevation;
|
|
}
|
|
|
|
public Widget avatar {
|
|
get { return this._avatar; }
|
|
}
|
|
|
|
Widget _avatar;
|
|
|
|
public Widget label {
|
|
get { return this._label; }
|
|
}
|
|
|
|
Widget _label;
|
|
|
|
public TextStyle labelStyle {
|
|
get { return this._labelStyle; }
|
|
}
|
|
|
|
TextStyle _labelStyle;
|
|
|
|
public EdgeInsets labelPadding {
|
|
get { return this._labelPadding; }
|
|
}
|
|
|
|
EdgeInsets _labelPadding;
|
|
|
|
public ValueChanged<bool> onSelected {
|
|
get { return this._onSelected; }
|
|
}
|
|
|
|
ValueChanged<bool> _onSelected;
|
|
|
|
public float? pressElevation {
|
|
get { return this._pressElevation; }
|
|
}
|
|
|
|
float? _pressElevation;
|
|
|
|
public bool? selected {
|
|
get { return this._selected; }
|
|
}
|
|
|
|
bool? _selected;
|
|
|
|
public Color disabledColor {
|
|
get { return this._disabledColor; }
|
|
}
|
|
|
|
Color _disabledColor;
|
|
|
|
public Color selectedColor {
|
|
get { return this._selectedColor; }
|
|
}
|
|
|
|
Color _selectedColor;
|
|
|
|
public string tooltip {
|
|
get { return this._tooltip; }
|
|
}
|
|
|
|
string _tooltip;
|
|
|
|
public ShapeBorder shape {
|
|
get { return this._shape; }
|
|
}
|
|
|
|
ShapeBorder _shape;
|
|
|
|
public Clip clipBehavior {
|
|
get { return this._clipBehavior; }
|
|
}
|
|
|
|
Clip _clipBehavior;
|
|
|
|
public Color backgroundColor {
|
|
get { return this._backgroundColor; }
|
|
}
|
|
|
|
Color _backgroundColor;
|
|
|
|
public EdgeInsets padding {
|
|
get { return this._padding; }
|
|
}
|
|
|
|
EdgeInsets _padding;
|
|
|
|
public MaterialTapTargetSize? materialTapTargetSize {
|
|
get { return this._materialTapTargetSize; }
|
|
}
|
|
|
|
MaterialTapTargetSize? _materialTapTargetSize;
|
|
|
|
public float? elevation {
|
|
get { return this._elevation; }
|
|
}
|
|
|
|
float? _elevation;
|
|
|
|
public ShapeBorder avatarBorder {
|
|
get { return this._avatarBorder; }
|
|
}
|
|
|
|
ShapeBorder _avatarBorder;
|
|
|
|
public bool? isEnabled {
|
|
get { return this.onSelected != null; }
|
|
}
|
|
|
|
public override Widget build(BuildContext context) {
|
|
D.assert(MaterialD.debugCheckHasMaterial(context));
|
|
ChipThemeData chipTheme = ChipTheme.of(context);
|
|
return new RawChip(
|
|
avatar: this.avatar,
|
|
label: this.label,
|
|
labelStyle: this.labelStyle ?? (this.selected == true ? chipTheme.secondaryLabelStyle : null),
|
|
labelPadding: this.labelPadding,
|
|
onSelected: this.onSelected,
|
|
pressElevation: this.pressElevation,
|
|
selected: this.selected,
|
|
showCheckmark: false,
|
|
onDeleted: null,
|
|
tooltip: this.tooltip,
|
|
shape: this.shape,
|
|
clipBehavior: this.clipBehavior,
|
|
disabledColor: this.disabledColor,
|
|
selectedColor: this.selectedColor ?? chipTheme.secondarySelectedColor,
|
|
backgroundColor: this.backgroundColor,
|
|
padding: this.padding,
|
|
isEnabled: this.isEnabled,
|
|
materialTapTargetSize: this.materialTapTargetSize,
|
|
elevation: this.elevation,
|
|
avatarBorder: this.avatarBorder
|
|
);
|
|
}
|
|
}
|
|
|
|
public class FilterChip : StatelessWidget,
|
|
ChipAttributes,
|
|
SelectableChipAttributes,
|
|
DisabledChipAttributes {
|
|
public FilterChip(
|
|
Key key = null,
|
|
Widget avatar = null,
|
|
Widget label = null,
|
|
TextStyle labelStyle = null,
|
|
EdgeInsets 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,
|
|
Color backgroundColor = null,
|
|
EdgeInsets padding = null,
|
|
MaterialTapTargetSize? materialTapTargetSize = null,
|
|
float? elevation = null,
|
|
ShapeBorder avatarBorder = null
|
|
) : base(key: key) {
|
|
D.assert(label != null);
|
|
D.assert(pressElevation == null || pressElevation >= 0.0f);
|
|
D.assert(elevation == null || elevation >= 0.0f);
|
|
this._avatarBorder = avatarBorder ?? new CircleBorder();
|
|
this._avatar = avatar;
|
|
this._label = label;
|
|
this._labelStyle = labelStyle;
|
|
this._labelPadding = labelPadding;
|
|
this._selected = selected;
|
|
this._onSelected = onSelected;
|
|
this._pressElevation = pressElevation;
|
|
this._disabledColor = disabledColor;
|
|
this._selectedColor = selectedColor;
|
|
this._tooltip = tooltip;
|
|
this._shape = shape;
|
|
this._clipBehavior = clipBehavior;
|
|
this._backgroundColor = backgroundColor;
|
|
this._padding = padding;
|
|
this._materialTapTargetSize = materialTapTargetSize;
|
|
this._elevation = elevation;
|
|
}
|
|
|
|
public Widget avatar {
|
|
get { return this._avatar; }
|
|
}
|
|
|
|
Widget _avatar;
|
|
|
|
public Widget label {
|
|
get { return this._label; }
|
|
}
|
|
|
|
Widget _label;
|
|
|
|
public TextStyle labelStyle {
|
|
get { return this._labelStyle; }
|
|
}
|
|
|
|
TextStyle _labelStyle;
|
|
|
|
public EdgeInsets labelPadding {
|
|
get { return this._labelPadding; }
|
|
}
|
|
|
|
EdgeInsets _labelPadding;
|
|
|
|
public bool? selected {
|
|
get { return this._selected; }
|
|
}
|
|
|
|
bool _selected;
|
|
|
|
public ValueChanged<bool> onSelected {
|
|
get { return this._onSelected; }
|
|
}
|
|
|
|
ValueChanged<bool> _onSelected;
|
|
|
|
public float? pressElevation {
|
|
get { return this._pressElevation; }
|
|
}
|
|
|
|
float? _pressElevation;
|
|
|
|
public Color disabledColor {
|
|
get { return this._disabledColor; }
|
|
}
|
|
|
|
Color _disabledColor;
|
|
|
|
public Color selectedColor {
|
|
get { return this._selectedColor; }
|
|
}
|
|
|
|
Color _selectedColor;
|
|
|
|
public string tooltip {
|
|
get { return this._tooltip; }
|
|
}
|
|
|
|
string _tooltip;
|
|
|
|
public ShapeBorder shape {
|
|
get { return this._shape; }
|
|
}
|
|
|
|
ShapeBorder _shape;
|
|
|
|
public Clip clipBehavior {
|
|
get { return this._clipBehavior; }
|
|
}
|
|
|
|
Clip _clipBehavior;
|
|
|
|
public Color backgroundColor {
|
|
get { return this._backgroundColor; }
|
|
}
|
|
|
|
Color _backgroundColor;
|
|
|
|
public EdgeInsets padding {
|
|
get { return this._padding; }
|
|
}
|
|
|
|
EdgeInsets _padding;
|
|
|
|
public MaterialTapTargetSize? materialTapTargetSize {
|
|
get { return this._materialTapTargetSize; }
|
|
}
|
|
|
|
MaterialTapTargetSize? _materialTapTargetSize;
|
|
|
|
public float? elevation {
|
|
get { return this._elevation; }
|
|
}
|
|
|
|
float? _elevation;
|
|
|
|
public ShapeBorder avatarBorder {
|
|
get { return this._avatarBorder; }
|
|
}
|
|
|
|
ShapeBorder _avatarBorder;
|
|
|
|
public bool? isEnabled {
|
|
get { return this.onSelected != null; }
|
|
}
|
|
|
|
public override Widget build(BuildContext context) {
|
|
D.assert(MaterialD.debugCheckHasMaterial(context));
|
|
return new RawChip(
|
|
avatar: this.avatar,
|
|
label: this.label,
|
|
labelStyle: this.labelStyle,
|
|
labelPadding: this.labelPadding,
|
|
onSelected: this.onSelected,
|
|
pressElevation: this.pressElevation,
|
|
selected: this.selected,
|
|
tooltip: this.tooltip,
|
|
shape: this.shape,
|
|
clipBehavior: this.clipBehavior,
|
|
backgroundColor: this.backgroundColor,
|
|
disabledColor: this.disabledColor,
|
|
selectedColor: this.selectedColor,
|
|
padding: this.padding,
|
|
isEnabled: this.isEnabled,
|
|
materialTapTargetSize: this.materialTapTargetSize,
|
|
elevation: this.elevation,
|
|
avatarBorder: this.avatarBorder
|
|
);
|
|
}
|
|
}
|
|
|
|
public class ActionChip : StatelessWidget, ChipAttributes, TappableChipAttributes {
|
|
public ActionChip(
|
|
Key key = null,
|
|
Widget avatar = null,
|
|
Widget label = null,
|
|
TextStyle labelStyle = null,
|
|
EdgeInsets labelPadding = null,
|
|
VoidCallback onPressed = null,
|
|
float? pressElevation = null,
|
|
string tooltip = null,
|
|
ShapeBorder shape = null,
|
|
Clip clipBehavior = Clip.none,
|
|
Color backgroundColor = null,
|
|
EdgeInsets padding = null,
|
|
MaterialTapTargetSize? materialTapTargetSize = null,
|
|
float? elevation = 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);
|
|
this._avatar = avatar;
|
|
this._label = label;
|
|
this._labelStyle = labelStyle;
|
|
this._labelPadding = labelPadding;
|
|
this._onPressed = onPressed;
|
|
this._pressElevation = pressElevation;
|
|
this._tooltip = tooltip;
|
|
this._shape = shape;
|
|
this._clipBehavior = clipBehavior;
|
|
this._backgroundColor = backgroundColor;
|
|
this._padding = padding;
|
|
this._materialTapTargetSize = materialTapTargetSize;
|
|
this._elevation = elevation;
|
|
}
|
|
|
|
|
|
public Widget avatar {
|
|
get { return this._avatar; }
|
|
}
|
|
|
|
Widget _avatar;
|
|
|
|
public Widget label {
|
|
get { return this._label; }
|
|
}
|
|
|
|
Widget _label;
|
|
|
|
public TextStyle labelStyle {
|
|
get { return this._labelStyle; }
|
|
}
|
|
|
|
TextStyle _labelStyle;
|
|
|
|
public EdgeInsets labelPadding {
|
|
get { return this._labelPadding; }
|
|
}
|
|
|
|
EdgeInsets _labelPadding;
|
|
|
|
public VoidCallback onPressed {
|
|
get { return this._onPressed; }
|
|
}
|
|
|
|
VoidCallback _onPressed;
|
|
|
|
public float? pressElevation {
|
|
get { return this._pressElevation; }
|
|
}
|
|
|
|
float? _pressElevation;
|
|
|
|
public string tooltip {
|
|
get { return this._tooltip; }
|
|
}
|
|
|
|
string _tooltip;
|
|
|
|
public ShapeBorder shape {
|
|
get { return this._shape; }
|
|
}
|
|
|
|
ShapeBorder _shape;
|
|
|
|
public Clip clipBehavior {
|
|
get { return this._clipBehavior; }
|
|
}
|
|
|
|
Clip _clipBehavior;
|
|
|
|
public Color backgroundColor {
|
|
get { return this._backgroundColor; }
|
|
}
|
|
|
|
Color _backgroundColor;
|
|
|
|
public EdgeInsets padding {
|
|
get { return this._padding; }
|
|
}
|
|
|
|
EdgeInsets _padding;
|
|
|
|
public MaterialTapTargetSize? materialTapTargetSize {
|
|
get { return this._materialTapTargetSize; }
|
|
}
|
|
|
|
MaterialTapTargetSize? _materialTapTargetSize;
|
|
|
|
public float? elevation {
|
|
get { return this._elevation; }
|
|
}
|
|
|
|
float? _elevation;
|
|
|
|
public override Widget build(BuildContext context) {
|
|
D.assert(MaterialD.debugCheckHasMaterial(context));
|
|
return new RawChip(
|
|
avatar: this.avatar,
|
|
label: this.label,
|
|
onPressed: this.onPressed,
|
|
pressElevation: this.pressElevation,
|
|
tooltip: this.tooltip,
|
|
labelStyle: this.labelStyle,
|
|
backgroundColor: this.backgroundColor,
|
|
shape: this.shape,
|
|
clipBehavior: this.clipBehavior,
|
|
padding: this.padding,
|
|
labelPadding: this.labelPadding,
|
|
isEnabled: true,
|
|
materialTapTargetSize: this.materialTapTargetSize,
|
|
elevation: this.elevation
|
|
);
|
|
}
|
|
}
|
|
|
|
public class RawChip : StatefulWidget,
|
|
ChipAttributes,
|
|
DeletableChipAttributes,
|
|
SelectableChipAttributes,
|
|
DisabledChipAttributes,
|
|
TappableChipAttributes {
|
|
public RawChip(
|
|
Key key = null,
|
|
Widget avatar = null,
|
|
Widget label = null,
|
|
TextStyle labelStyle = null,
|
|
EdgeInsets padding = null,
|
|
EdgeInsets 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 showCheckmark = true,
|
|
bool? isEnabled = true,
|
|
Color disabledColor = null,
|
|
Color selectedColor = null,
|
|
string tooltip = null,
|
|
ShapeBorder shape = null,
|
|
Clip clipBehavior = Clip.none,
|
|
Color backgroundColor = null,
|
|
MaterialTapTargetSize? materialTapTargetSize = null,
|
|
float? elevation = 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;
|
|
this._avatarBorder = avatarBorder ?? new CircleBorder();
|
|
this._avatar = avatar;
|
|
this._label = label;
|
|
this._labelStyle = labelStyle;
|
|
this._padding = padding;
|
|
this._labelPadding = labelPadding;
|
|
this._deleteIcon = deleteIcon;
|
|
this._onDeleted = onDeleted;
|
|
this._deleteIconColor = deleteIconColor;
|
|
this._deleteButtonTooltipMessage = deleteButtonTooltipMessage;
|
|
this._onPressed = onPressed;
|
|
this._onSelected = onSelected;
|
|
this._pressElevation = pressElevation;
|
|
this._tapEnabled = tapEnabled;
|
|
this._selected = selected;
|
|
this._showCheckmark = showCheckmark;
|
|
this._isEnabled = isEnabled;
|
|
this._disabledColor = disabledColor;
|
|
this._selectedColor = selectedColor;
|
|
this._tooltip = tooltip;
|
|
this._shape = shape;
|
|
this._clipBehavior = clipBehavior;
|
|
this._backgroundColor = backgroundColor;
|
|
this._materialTapTargetSize = materialTapTargetSize;
|
|
this._elevation = elevation;
|
|
}
|
|
|
|
|
|
public Widget avatar {
|
|
get { return this._avatar; }
|
|
}
|
|
|
|
Widget _avatar;
|
|
|
|
public Widget label {
|
|
get { return this._label; }
|
|
}
|
|
|
|
Widget _label;
|
|
|
|
public TextStyle labelStyle {
|
|
get { return this._labelStyle; }
|
|
}
|
|
|
|
TextStyle _labelStyle;
|
|
|
|
public EdgeInsets labelPadding {
|
|
get { return this._labelPadding; }
|
|
}
|
|
|
|
EdgeInsets _labelPadding;
|
|
|
|
public Widget deleteIcon {
|
|
get { return this._deleteIcon; }
|
|
}
|
|
|
|
Widget _deleteIcon;
|
|
|
|
public VoidCallback onDeleted {
|
|
get { return this._onDeleted; }
|
|
}
|
|
|
|
VoidCallback _onDeleted;
|
|
|
|
public Color deleteIconColor {
|
|
get { return this._deleteIconColor; }
|
|
}
|
|
|
|
Color _deleteIconColor;
|
|
|
|
public string deleteButtonTooltipMessage {
|
|
get { return this._deleteButtonTooltipMessage; }
|
|
}
|
|
|
|
string _deleteButtonTooltipMessage;
|
|
|
|
public ValueChanged<bool> onSelected {
|
|
get { return this._onSelected; }
|
|
}
|
|
|
|
ValueChanged<bool> _onSelected;
|
|
|
|
public VoidCallback onPressed {
|
|
get { return this._onPressed; }
|
|
}
|
|
|
|
VoidCallback _onPressed;
|
|
|
|
public float? pressElevation {
|
|
get { return this._pressElevation; }
|
|
}
|
|
|
|
float? _pressElevation;
|
|
|
|
public bool? selected {
|
|
get { return this._selected; }
|
|
}
|
|
|
|
bool? _selected;
|
|
|
|
public bool? isEnabled {
|
|
get { return this._isEnabled; }
|
|
}
|
|
|
|
bool? _isEnabled;
|
|
|
|
public Color disabledColor {
|
|
get { return this._disabledColor; }
|
|
}
|
|
|
|
Color _disabledColor;
|
|
|
|
public Color selectedColor {
|
|
get { return this._selectedColor; }
|
|
}
|
|
|
|
Color _selectedColor;
|
|
|
|
public string tooltip {
|
|
get { return this._tooltip; }
|
|
}
|
|
|
|
string _tooltip;
|
|
|
|
public ShapeBorder shape {
|
|
get { return this._shape; }
|
|
}
|
|
|
|
ShapeBorder _shape;
|
|
|
|
public Clip clipBehavior {
|
|
get { return this._clipBehavior; }
|
|
}
|
|
|
|
Clip _clipBehavior;
|
|
|
|
public Color backgroundColor {
|
|
get { return this._backgroundColor; }
|
|
}
|
|
|
|
Color _backgroundColor;
|
|
|
|
public EdgeInsets padding {
|
|
get { return this._padding; }
|
|
}
|
|
|
|
EdgeInsets _padding;
|
|
|
|
public MaterialTapTargetSize? materialTapTargetSize {
|
|
get { return this._materialTapTargetSize; }
|
|
}
|
|
|
|
MaterialTapTargetSize? _materialTapTargetSize;
|
|
|
|
public float? elevation {
|
|
get { return this._elevation; }
|
|
}
|
|
|
|
float? _elevation;
|
|
|
|
public ShapeBorder avatarBorder {
|
|
get { return this._avatarBorder; }
|
|
}
|
|
|
|
ShapeBorder _avatarBorder;
|
|
|
|
public bool showCheckmark {
|
|
get { return this._showCheckmark; }
|
|
}
|
|
|
|
bool _showCheckmark;
|
|
|
|
public bool? tapEnabled {
|
|
get { return this._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;
|
|
|
|
public bool hasDeleteButton {
|
|
get { return this.widget.onDeleted != null; }
|
|
}
|
|
|
|
public bool hasAvatar {
|
|
get { return this.widget.avatar != null; }
|
|
}
|
|
|
|
public bool canTap {
|
|
get {
|
|
return this.widget.isEnabled == true
|
|
&& this.widget.tapEnabled == true
|
|
&& (this.widget.onPressed != null || this.widget.onSelected != null);
|
|
}
|
|
}
|
|
|
|
bool _isTapping = false;
|
|
|
|
public bool isTapping {
|
|
get { return !this.canTap ? false : this._isTapping; }
|
|
}
|
|
|
|
public override void initState() {
|
|
D.assert(this.widget.onSelected == null || this.widget.onPressed == null);
|
|
base.initState();
|
|
this.selectController = new AnimationController(
|
|
duration: ChipUtils._kSelectDuration,
|
|
value: this.widget.selected == true ? 1.0f : 0.0f,
|
|
vsync: this
|
|
);
|
|
this.selectionFade = new CurvedAnimation(
|
|
parent: this.selectController,
|
|
curve: Curves.fastOutSlowIn
|
|
);
|
|
this.avatarDrawerController = new AnimationController(
|
|
duration: ChipUtils._kDrawerDuration,
|
|
value: this.hasAvatar || this.widget.selected == true ? 1.0f : 0.0f,
|
|
vsync: this
|
|
);
|
|
this.deleteDrawerController = new AnimationController(
|
|
duration: ChipUtils._kDrawerDuration,
|
|
value: this.hasDeleteButton ? 1.0f : 0.0f,
|
|
vsync: this
|
|
);
|
|
this.enableController = new AnimationController(
|
|
duration: ChipUtils._kDisableDuration,
|
|
value: this.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);
|
|
this.checkmarkAnimation = new CurvedAnimation(
|
|
parent: this.selectController,
|
|
curve: new Interval(1.0f - checkmarkPercentage, 1.0f, curve: Curves.fastOutSlowIn),
|
|
reverseCurve: new Interval(
|
|
1.0f - checkmarkReversePercentage,
|
|
1.0f,
|
|
curve: Curves.fastOutSlowIn
|
|
)
|
|
);
|
|
this.deleteDrawerAnimation = new CurvedAnimation(
|
|
parent: this.deleteDrawerController,
|
|
curve: Curves.fastOutSlowIn
|
|
);
|
|
this.avatarDrawerAnimation = new CurvedAnimation(
|
|
parent: this.avatarDrawerController,
|
|
curve: Curves.fastOutSlowIn,
|
|
reverseCurve: new Interval(
|
|
1.0f - avatarDrawerReversePercentage,
|
|
1.0f,
|
|
curve: Curves.fastOutSlowIn
|
|
)
|
|
);
|
|
this.enableAnimation = new CurvedAnimation(
|
|
parent: this.enableController,
|
|
curve: Curves.fastOutSlowIn
|
|
);
|
|
}
|
|
|
|
public override void dispose() {
|
|
this.selectController.dispose();
|
|
this.avatarDrawerController.dispose();
|
|
this.deleteDrawerController.dispose();
|
|
this.enableController.dispose();
|
|
base.dispose();
|
|
}
|
|
|
|
void _handleTapDown(TapDownDetails details) {
|
|
if (!this.canTap) {
|
|
return;
|
|
}
|
|
|
|
this.setState(() => { this._isTapping = true; });
|
|
}
|
|
|
|
void _handleTapCancel() {
|
|
if (!this.canTap) {
|
|
return;
|
|
}
|
|
|
|
this.setState(() => { this._isTapping = false; });
|
|
}
|
|
|
|
void _handleTap() {
|
|
if (!this.canTap) {
|
|
return;
|
|
}
|
|
|
|
this.setState(() => { this._isTapping = false; });
|
|
this.widget.onSelected?.Invoke(!this.widget.selected == true);
|
|
this.widget.onPressed?.Invoke();
|
|
}
|
|
|
|
Color getBackgroundColor(ChipThemeData theme) {
|
|
ColorTween backgroundTween = new ColorTween(
|
|
begin: this.widget.disabledColor ?? theme.disabledColor,
|
|
end: this.widget.backgroundColor ?? theme.backgroundColor
|
|
);
|
|
ColorTween selectTween = new ColorTween(
|
|
begin: backgroundTween.evaluate(this.enableController),
|
|
end: this.widget.selectedColor ?? theme.selectedColor
|
|
);
|
|
return selectTween.evaluate(this.selectionFade);
|
|
}
|
|
|
|
public override void didUpdateWidget(StatefulWidget _oldWidget) {
|
|
RawChip oldWidget = _oldWidget as RawChip;
|
|
base.didUpdateWidget(oldWidget);
|
|
if (oldWidget.isEnabled != this.widget.isEnabled) {
|
|
this.setState(() => {
|
|
if (this.widget.isEnabled == true) {
|
|
this.enableController.forward();
|
|
}
|
|
else {
|
|
this.enableController.reverse();
|
|
}
|
|
});
|
|
}
|
|
|
|
if (oldWidget.avatar != this.widget.avatar || oldWidget.selected != this.widget.selected) {
|
|
this.setState(() => {
|
|
if (this.hasAvatar || this.widget.selected == true) {
|
|
this.avatarDrawerController.forward();
|
|
}
|
|
else {
|
|
this.avatarDrawerController.reverse();
|
|
}
|
|
});
|
|
}
|
|
|
|
if (oldWidget.selected != this.widget.selected) {
|
|
this.setState(() => {
|
|
if (this.widget.selected == true) {
|
|
this.selectController.forward();
|
|
}
|
|
else {
|
|
this.selectController.reverse();
|
|
}
|
|
});
|
|
}
|
|
|
|
if (oldWidget.onDeleted != this.widget.onDeleted) {
|
|
this.setState(() => {
|
|
if (this.hasDeleteButton) {
|
|
this.deleteDrawerController.forward();
|
|
}
|
|
else {
|
|
this.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) {
|
|
if (!this.hasDeleteButton) {
|
|
return null;
|
|
}
|
|
|
|
return this._wrapWithTooltip(
|
|
this.widget.deleteButtonTooltipMessage ?? MaterialLocalizations.of(context)?.deleteButtonTooltip,
|
|
this.widget.onDeleted,
|
|
new InkResponse(
|
|
onTap: this.widget.isEnabled == true
|
|
? () => { this.widget.onDeleted(); }
|
|
: (GestureTapCallback) null,
|
|
child: new IconTheme(
|
|
data: theme.iconTheme.copyWith(
|
|
color: this.widget.deleteIconColor ?? chipTheme.deleteIconColor
|
|
),
|
|
child: this.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(MaterialD.debugCheckHasMaterial(context));
|
|
D.assert(WidgetsD.debugCheckHasMediaQuery(context));
|
|
D.assert(WidgetsD.debugCheckHasDirectionality(context));
|
|
D.assert(MaterialD.debugCheckHasMaterialLocalizations(context));
|
|
|
|
ThemeData theme = Theme.of(context);
|
|
ChipThemeData chipTheme = ChipTheme.of(context);
|
|
TextDirection textDirection = Directionality.of(context);
|
|
ShapeBorder shape = this.widget.shape ?? chipTheme.shape;
|
|
float elevation = this.widget.elevation ?? (chipTheme.elevation ?? _defaultElevation);
|
|
float pressElevation = this.widget.pressElevation ?? (chipTheme.pressElevation ?? _defaultPressElevation);
|
|
|
|
Widget result = new Material(
|
|
elevation: this.isTapping ? pressElevation : elevation,
|
|
animationDuration: pressedAnimationDuration,
|
|
shape: shape,
|
|
clipBehavior: this.widget.clipBehavior,
|
|
child: new InkResponse(
|
|
onTap: this.canTap ? this._handleTap : (GestureTapCallback) null,
|
|
onTapDown: this.canTap ? this._handleTapDown : (GestureTapDownCallback) null,
|
|
onTapCancel: this.canTap ? this._handleTapCancel : (GestureTapCallback) null,
|
|
child: new AnimatedBuilder(
|
|
animation: ListenableUtils.merge(new List<Listenable>
|
|
{this.selectController, this.enableController}),
|
|
builder: (BuildContext _context, Widget child) => {
|
|
return new Container(
|
|
decoration: new ShapeDecoration(
|
|
shape: shape,
|
|
color: this.getBackgroundColor(chipTheme)
|
|
),
|
|
child: child
|
|
);
|
|
},
|
|
child: this._wrapWithTooltip(this.widget.tooltip, this.widget.onPressed,
|
|
new _ChipRenderWidget(
|
|
theme: new _ChipRenderTheme(
|
|
label: new DefaultTextStyle(
|
|
overflow: TextOverflow.fade,
|
|
textAlign: TextAlign.left,
|
|
maxLines: 1,
|
|
softWrap: false,
|
|
style: this.widget.labelStyle ?? chipTheme.labelStyle,
|
|
child: this.widget.label
|
|
),
|
|
avatar: new AnimatedSwitcher(
|
|
child: this.widget.avatar,
|
|
duration: ChipUtils._kDrawerDuration,
|
|
switchInCurve: Curves.fastOutSlowIn
|
|
),
|
|
deleteIcon: new AnimatedSwitcher(
|
|
child: this._buildDeleteIcon(context, theme, chipTheme),
|
|
duration: ChipUtils._kDrawerDuration,
|
|
switchInCurve: Curves.fastOutSlowIn
|
|
),
|
|
brightness: chipTheme.brightness,
|
|
padding: this.widget.padding ?? chipTheme.padding,
|
|
labelPadding: this.widget.labelPadding ?? chipTheme.labelPadding,
|
|
showAvatar: this.hasAvatar,
|
|
showCheckmark: this.widget.showCheckmark,
|
|
canTapBody: this.canTap
|
|
),
|
|
value: this.widget.selected,
|
|
checkmarkAnimation: this.checkmarkAnimation,
|
|
enableAnimation: this.enableAnimation,
|
|
avatarDrawerAnimation: this.avatarDrawerAnimation,
|
|
deleteDrawerAnimation: this.deleteDrawerAnimation,
|
|
isEnabled: this.widget.isEnabled,
|
|
avatarBorder: this.widget.avatarBorder
|
|
)
|
|
)
|
|
)
|
|
)
|
|
);
|
|
BoxConstraints constraints;
|
|
switch (this.widget.materialTapTargetSize ?? theme.materialTapTargetSize) {
|
|
case MaterialTapTargetSize.padded:
|
|
constraints = new BoxConstraints(minHeight: 48.0f);
|
|
break;
|
|
case MaterialTapTargetSize.shrinkWrap:
|
|
constraints = new BoxConstraints();
|
|
break;
|
|
default:
|
|
throw new Exception("Unknown Material Tap Target Size: " + this.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(this.constraints);
|
|
}
|
|
|
|
public override void updateRenderObject(BuildContext context, RenderObject _renderObject) {
|
|
_RenderChipRedirectingHitDetection renderObject = _renderObject as _RenderChipRedirectingHitDetection;
|
|
renderObject.additionalConstraints = this.constraints;
|
|
}
|
|
}
|
|
|
|
class _RenderChipRedirectingHitDetection : RenderConstrainedBox {
|
|
public _RenderChipRedirectingHitDetection(BoxConstraints additionalConstraints) : base(
|
|
additionalConstraints: additionalConstraints) {
|
|
}
|
|
|
|
public override bool hitTest(HitTestResult result, Offset position = null) {
|
|
if (!this.size.contains(position)) {
|
|
return false;
|
|
}
|
|
|
|
return this.child.hitTest(result, position: new Offset(position.dx, this.size.height / 2));
|
|
}
|
|
}
|
|
|
|
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 = this.theme;
|
|
renderObject.value = this.value ?? false;
|
|
renderObject.isEnabled = this.isEnabled ?? false;
|
|
renderObject.checkmarkAnimation = this.checkmarkAnimation;
|
|
renderObject.avatarDrawerAnimation = this.avatarDrawerAnimation;
|
|
renderObject.deleteDrawerAnimation = this.deleteDrawerAnimation;
|
|
renderObject.enableAnimation = this.enableAnimation;
|
|
renderObject.avatarBorder = this.avatarBorder;
|
|
}
|
|
|
|
public override RenderObject createRenderObject(BuildContext context) {
|
|
return new _RenderChip(
|
|
theme: this.theme,
|
|
value: this.value,
|
|
isEnabled: this.isEnabled,
|
|
checkmarkAnimation: this.checkmarkAnimation,
|
|
avatarDrawerAnimation: this.avatarDrawerAnimation,
|
|
deleteDrawerAnimation: this.deleteDrawerAnimation,
|
|
enableAnimation: this.enableAnimation,
|
|
avatarBorder: this.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) {
|
|
this.slotToChild.Values.Each((value) => { visitor(value); });
|
|
}
|
|
|
|
protected override void forgetChild(Element child) {
|
|
D.assert(this.slotToChild.ContainsValue(child));
|
|
D.assert(this.childToSlot.ContainsKey(child));
|
|
_ChipSlot slot = this.childToSlot[child];
|
|
this.childToSlot.Remove(child);
|
|
this.slotToChild.Remove(slot);
|
|
}
|
|
|
|
void _mountChild(Widget widget, _ChipSlot slot) {
|
|
Element oldChild = this.slotToChild.getOrDefault(slot);
|
|
Element newChild = this.updateChild(oldChild, widget, slot);
|
|
if (oldChild != null) {
|
|
this.slotToChild.Remove(slot);
|
|
this.childToSlot.Remove(oldChild);
|
|
}
|
|
|
|
if (newChild != null) {
|
|
this.slotToChild[slot] = newChild;
|
|
this.childToSlot[newChild] = slot;
|
|
}
|
|
}
|
|
|
|
public override void mount(Element parent, object newSlot) {
|
|
base.mount(parent, newSlot);
|
|
this._mountChild(this.widget.theme.avatar, _ChipSlot.avatar);
|
|
this._mountChild(this.widget.theme.deleteIcon, _ChipSlot.deleteIcon);
|
|
this._mountChild(this.widget.theme.label, _ChipSlot.label);
|
|
}
|
|
|
|
void _updateChild(Widget widget, _ChipSlot slot) {
|
|
Element oldChild = this.slotToChild[slot];
|
|
Element newChild = this.updateChild(oldChild, widget, slot);
|
|
if (oldChild != null) {
|
|
this.childToSlot.Remove(oldChild);
|
|
this.slotToChild.Remove(slot);
|
|
}
|
|
|
|
if (newChild != null) {
|
|
this.slotToChild[slot] = newChild;
|
|
this.childToSlot[newChild] = slot;
|
|
}
|
|
}
|
|
|
|
public override void update(Widget _newWidget) {
|
|
_ChipRenderWidget newWidget = _newWidget as _ChipRenderWidget;
|
|
base.update(newWidget);
|
|
D.assert(this.widget == newWidget);
|
|
this._updateChild(this.widget.theme.label, _ChipSlot.label);
|
|
this._updateChild(this.widget.theme.avatar, _ChipSlot.avatar);
|
|
this._updateChild(this.widget.theme.deleteIcon, _ChipSlot.deleteIcon);
|
|
}
|
|
|
|
void _updateRenderObject(RenderObject child, _ChipSlot slot) {
|
|
switch (slot) {
|
|
case _ChipSlot.avatar:
|
|
this.renderObject.avatar = (RenderBox) child;
|
|
break;
|
|
case _ChipSlot.label:
|
|
this.renderObject.label = (RenderBox) child;
|
|
break;
|
|
case _ChipSlot.deleteIcon:
|
|
this.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;
|
|
this._updateRenderObject(child, slot);
|
|
D.assert(this.renderObject.childToSlot.ContainsKey((RenderBox) child));
|
|
D.assert(this.renderObject.slotToChild.ContainsKey(slot));
|
|
}
|
|
|
|
protected override void removeChildRenderObject(RenderObject child) {
|
|
D.assert(child is RenderBox);
|
|
D.assert(this.renderObject.childToSlot.ContainsKey((RenderBox) child));
|
|
this._updateRenderObject(null, this.renderObject.childToSlot[(RenderBox) child]);
|
|
D.assert(!this.renderObject.childToSlot.ContainsKey((RenderBox) child));
|
|
D.assert(!this.renderObject.slotToChild.ContainsKey((_ChipSlot) this.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,
|
|
EdgeInsets labelPadding = null,
|
|
bool? showAvatar = null,
|
|
bool? showCheckmark = null,
|
|
bool? canTapBody = null
|
|
) {
|
|
this.avatar = avatar;
|
|
this.label = label;
|
|
this.deleteIcon = deleteIcon;
|
|
this.brightness = brightness;
|
|
this.padding = padding;
|
|
this.labelPadding = labelPadding;
|
|
this.showAvatar = showAvatar;
|
|
this.showCheckmark = showCheckmark;
|
|
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 EdgeInsets labelPadding;
|
|
public readonly bool? showAvatar;
|
|
public readonly bool? showCheckmark;
|
|
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() != this.GetType()) {
|
|
return false;
|
|
}
|
|
|
|
return this.Equals((_ChipRenderTheme) obj);
|
|
}
|
|
|
|
public bool Equals(_ChipRenderTheme other) {
|
|
return this.avatar == other.avatar
|
|
&& this.label == other.label
|
|
&& this.deleteIcon == other.deleteIcon
|
|
&& this.brightness == other.brightness
|
|
&& this.padding == other.padding
|
|
&& this.labelPadding == other.labelPadding
|
|
&& this.showAvatar == other.showAvatar
|
|
&& this.showCheckmark == other.showCheckmark
|
|
&& this.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 = this.avatar.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ this.label.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ this.deleteIcon.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ this.brightness.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ this.padding.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ this.labelPadding.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ this.showAvatar.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ this.showCheckmark.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ this.canTapBody.GetHashCode();
|
|
return hashCode;
|
|
}
|
|
}
|
|
|
|
class _RenderChip : RenderBox {
|
|
public _RenderChip(
|
|
_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
|
|
) {
|
|
D.assert(theme != null);
|
|
this._theme = theme;
|
|
checkmarkAnimation.addListener(this.markNeedsPaint);
|
|
avatarDrawerAnimation.addListener(this.markNeedsLayout);
|
|
deleteDrawerAnimation.addListener(this.markNeedsLayout);
|
|
enableAnimation.addListener(this.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) {
|
|
this.dropChild(oldChild);
|
|
this.childToSlot.Remove(oldChild);
|
|
this.slotToChild.Remove(slot);
|
|
}
|
|
|
|
if (newChild != null) {
|
|
this.childToSlot[newChild] = slot;
|
|
this.slotToChild[slot] = newChild;
|
|
this.adoptChild(newChild);
|
|
}
|
|
|
|
return newChild;
|
|
}
|
|
|
|
RenderBox _avatar;
|
|
|
|
public RenderBox avatar {
|
|
get { return this._avatar; }
|
|
set { this._avatar = this._updateChild(this._avatar, value, _ChipSlot.avatar); }
|
|
}
|
|
|
|
RenderBox _deleteIcon;
|
|
|
|
public RenderBox deleteIcon {
|
|
get { return this._deleteIcon; }
|
|
set { this._deleteIcon = this._updateChild(this._deleteIcon, value, _ChipSlot.deleteIcon); }
|
|
}
|
|
|
|
RenderBox _label;
|
|
|
|
public RenderBox label {
|
|
get { return this._label; }
|
|
set { this._label = this._updateChild(this._label, value, _ChipSlot.label); }
|
|
}
|
|
|
|
public _ChipRenderTheme theme {
|
|
get { return this._theme; }
|
|
set {
|
|
if (this._theme == value) {
|
|
return;
|
|
}
|
|
|
|
this._theme = value;
|
|
this.markNeedsLayout();
|
|
}
|
|
}
|
|
|
|
_ChipRenderTheme _theme;
|
|
|
|
IEnumerable<RenderBox> _children {
|
|
get {
|
|
if (this.avatar != null) {
|
|
yield return this.avatar;
|
|
}
|
|
|
|
if (this.label != null) {
|
|
yield return this.label;
|
|
}
|
|
|
|
if (this.deleteIcon != null) {
|
|
yield return this.deleteIcon;
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool isDrawingCheckmark {
|
|
get { return this.theme.showCheckmark == true && (!(this.checkmarkAnimation?.isDismissed ?? !this.value)) == true; }
|
|
}
|
|
|
|
public bool deleteIconShowing {
|
|
get { return !this.deleteDrawerAnimation.isDismissed; }
|
|
}
|
|
|
|
public override void attach(object _owner) {
|
|
PipelineOwner owner = _owner as PipelineOwner;
|
|
base.attach(owner);
|
|
foreach (RenderBox child in this._children) {
|
|
child.attach(owner);
|
|
}
|
|
}
|
|
|
|
public override void detach() {
|
|
base.detach();
|
|
foreach (RenderBox child in this._children) {
|
|
child.detach();
|
|
}
|
|
}
|
|
|
|
public override void redepthChildren() {
|
|
this._children.Each(this.redepthChild);
|
|
}
|
|
|
|
public override void visitChildren(RenderObjectVisitor visitor) {
|
|
this._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(this.avatar, "avatar");
|
|
add(this.label, "label");
|
|
add(this.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 override float computeMinIntrinsicWidth(float height) {
|
|
float overallPadding = this.theme.padding.horizontal + this.theme.labelPadding.horizontal;
|
|
return overallPadding +
|
|
_minWidth(this.avatar, height) +
|
|
_minWidth(this.label, height) +
|
|
_minWidth(this.deleteIcon, height);
|
|
}
|
|
|
|
protected override float computeMaxIntrinsicWidth(float height) {
|
|
float overallPadding = this.theme.padding.vertical + this.theme.labelPadding.horizontal;
|
|
return overallPadding +
|
|
_maxWidth(this.avatar, height) +
|
|
_maxWidth(this.label, height) +
|
|
_maxWidth(this.deleteIcon, height);
|
|
}
|
|
|
|
protected override float computeMinIntrinsicHeight(float width) {
|
|
return Mathf.Max(
|
|
ChipUtils._kChipHeight,
|
|
this.theme.padding.vertical + this.theme.labelPadding.vertical + _minHeight(this.label, width)
|
|
);
|
|
}
|
|
|
|
protected override float computeMaxIntrinsicHeight(float width) {
|
|
return this.computeMinIntrinsicHeight(width);
|
|
}
|
|
|
|
protected override float? computeDistanceToActualBaseline(TextBaseline baseline) {
|
|
return this.label.getDistanceToActualBaseline(baseline);
|
|
}
|
|
|
|
Size _layoutLabel(float iconSizes, Size size) {
|
|
Size rawSize = _boxSize(this.label);
|
|
if (this.constraints.maxWidth.isFinite()) {
|
|
this.label.layout(this.constraints.copyWith(
|
|
minWidth: 0.0f,
|
|
maxWidth: Mathf.Max(
|
|
0.0f, this.constraints.maxWidth - iconSizes - this.theme.labelPadding.horizontal
|
|
),
|
|
minHeight: rawSize.height,
|
|
maxHeight: size.height
|
|
),
|
|
parentUsesSize: true
|
|
);
|
|
}
|
|
else {
|
|
this.label.layout(
|
|
new BoxConstraints(
|
|
minHeight: rawSize.height,
|
|
maxHeight: size.height,
|
|
minWidth: 0.0f,
|
|
maxWidth: size.width
|
|
),
|
|
parentUsesSize: true
|
|
);
|
|
}
|
|
|
|
return new Size(
|
|
rawSize.width + this.theme.labelPadding.horizontal,
|
|
rawSize.height + this.theme.labelPadding.vertical
|
|
);
|
|
}
|
|
|
|
Size _layoutAvatar(BoxConstraints contentConstraints, float contentSize) {
|
|
float requestedSize = Mathf.Max(0.0f, contentSize);
|
|
BoxConstraints avatarConstraints = BoxConstraints.tightFor(
|
|
width: requestedSize,
|
|
height: requestedSize
|
|
);
|
|
this.avatar.layout(avatarConstraints, parentUsesSize: true);
|
|
if (this.theme.showCheckmark != true && this.theme.showAvatar != true) {
|
|
return new Size(0.0f, contentSize);
|
|
}
|
|
|
|
float avatarWidth = 0.0f;
|
|
float avatarHeight = 0.0f;
|
|
Size avatarBoxSize = _boxSize(this.avatar);
|
|
if (this.theme.showAvatar == true) {
|
|
avatarWidth += this.avatarDrawerAnimation.value * avatarBoxSize.width;
|
|
}
|
|
else {
|
|
avatarWidth += this.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
|
|
);
|
|
this.deleteIcon.layout(deleteIconConstraints, parentUsesSize: true);
|
|
if (!this.deleteIconShowing) {
|
|
return new Size(0.0f, contentSize);
|
|
}
|
|
|
|
float deleteIconWidth = 0.0f;
|
|
float deleteIconHeight = 0.0f;
|
|
Size boxSize = _boxSize(this.deleteIcon);
|
|
deleteIconWidth += this.deleteDrawerAnimation.value * boxSize.width;
|
|
deleteIconHeight += boxSize.height;
|
|
return new Size(deleteIconWidth, deleteIconHeight);
|
|
}
|
|
|
|
public override bool hitTest(HitTestResult result, Offset position = null) {
|
|
if (!this.size.contains(position)) {
|
|
return false;
|
|
}
|
|
|
|
RenderBox hitTestChild;
|
|
if (position.dx / this.size.width > 0.66f) {
|
|
hitTestChild = this.deleteIcon ?? this.label ?? this.avatar;
|
|
}
|
|
else {
|
|
hitTestChild = this.label ?? this.avatar;
|
|
}
|
|
|
|
return hitTestChild?.hitTest(result, position: hitTestChild.size.center(Offset.zero)) ?? false;
|
|
}
|
|
|
|
protected override void performLayout() {
|
|
BoxConstraints contentConstraints = this.constraints.loosen();
|
|
this.label.layout(contentConstraints, parentUsesSize: true);
|
|
float contentSize = Mathf.Max(
|
|
ChipUtils._kChipHeight - this.theme.padding.vertical + this.theme.labelPadding.vertical,
|
|
_boxSize(this.label).height + this.theme.labelPadding.vertical
|
|
);
|
|
Size avatarSize = this._layoutAvatar(contentConstraints, contentSize);
|
|
Size deleteIconSize = this._layoutDeleteIcon(contentConstraints, contentSize);
|
|
Size labelSize = new Size(_boxSize(this.label).width, contentSize);
|
|
labelSize = this._layoutLabel(avatarSize.width + deleteIconSize.width, labelSize);
|
|
|
|
Size overallSize = new Size(
|
|
avatarSize.width + labelSize.width + deleteIconSize.width,
|
|
contentSize
|
|
);
|
|
|
|
|
|
const float left = 0.0f;
|
|
|
|
Offset centerLayout(Size boxSize, float x) {
|
|
D.assert(contentSize >= boxSize.height);
|
|
return new Offset(x, (contentSize - boxSize.height) / 2.0f);
|
|
}
|
|
|
|
Offset avatarOffset = Offset.zero;
|
|
Offset labelOffset = Offset.zero;
|
|
Offset deleteIconOffset = Offset.zero;
|
|
float start = left;
|
|
if (this.theme.showCheckmark == true || this.theme.showAvatar == true) {
|
|
avatarOffset = centerLayout(avatarSize, start - _boxSize(this.avatar).width + avatarSize.width);
|
|
start += avatarSize.width;
|
|
}
|
|
|
|
labelOffset = centerLayout(labelSize, start);
|
|
start += labelSize.width;
|
|
if (this.theme.canTapBody == true) {
|
|
this.pressRect = Rect.fromLTWH(
|
|
0.0f,
|
|
0.0f, this.deleteIconShowing
|
|
? start + this.theme.padding.left
|
|
: overallSize.width + this.theme.padding.horizontal,
|
|
overallSize.height + this.theme.padding.vertical
|
|
);
|
|
}
|
|
else {
|
|
this.pressRect = Rect.zero;
|
|
}
|
|
|
|
start -= _boxSize(this.deleteIcon).width - deleteIconSize.width;
|
|
if (this.deleteIconShowing) {
|
|
deleteIconOffset = centerLayout(deleteIconSize, start);
|
|
this.deleteButtonRect = Rect.fromLTWH(
|
|
start + this.theme.padding.left,
|
|
0.0f,
|
|
deleteIconSize.width + this.theme.padding.right,
|
|
overallSize.height + this.theme.padding.vertical
|
|
);
|
|
}
|
|
else {
|
|
this.deleteButtonRect = Rect.zero;
|
|
}
|
|
|
|
labelOffset = labelOffset +
|
|
new Offset(
|
|
0.0f,
|
|
((labelSize.height - this.theme.labelPadding.vertical) - _boxSize(this.label).height) /
|
|
2.0f
|
|
);
|
|
_boxParentData(this.avatar).offset = this.theme.padding.topLeft + avatarOffset;
|
|
_boxParentData(this.label).offset =
|
|
this.theme.padding.topLeft + labelOffset + this.theme.labelPadding.topLeft;
|
|
_boxParentData(this.deleteIcon).offset = this.theme.padding.topLeft + deleteIconOffset;
|
|
Size paddedSize = new Size(
|
|
overallSize.width + this.theme.padding.horizontal,
|
|
overallSize.height + this.theme.padding.vertical
|
|
);
|
|
this.size = this.constraints.constrain(paddedSize);
|
|
D.assert(this.size.height == this.constraints.constrainHeight(paddedSize.height),
|
|
() => $"Constrained height {this.size.height} doesn't match expected height " +
|
|
$"{this.constraints.constrainWidth(paddedSize.height)}");
|
|
D.assert(this.size.width == this.constraints.constrainWidth(paddedSize.width),
|
|
() => $"Constrained width {this.size.width} doesn't match expected width " +
|
|
$"{this.constraints.constrainWidth(paddedSize.width)}");
|
|
}
|
|
|
|
static ColorTween selectionScrimTween = new ColorTween(
|
|
begin: Colors.transparent,
|
|
end: ChipUtils._kSelectScrimColor
|
|
);
|
|
|
|
Color _disabledColor {
|
|
get {
|
|
if (this.enableAnimation == null || this.enableAnimation.isCompleted) {
|
|
return Colors.white;
|
|
}
|
|
|
|
ColorTween enableTween;
|
|
switch (this.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: " + this.theme.brightness);
|
|
}
|
|
|
|
return enableTween.evaluate(this.enableAnimation);
|
|
}
|
|
}
|
|
|
|
void _paintCheck(Canvas canvas, Offset origin, float size) {
|
|
Color paintColor;
|
|
switch (this.theme.brightness) {
|
|
case Brightness.light:
|
|
paintColor = this.theme.showAvatar == true
|
|
? Colors.white
|
|
: Colors.black.withAlpha(ChipUtils._kCheckmarkAlpha);
|
|
break;
|
|
case Brightness.dark:
|
|
paintColor = this.theme.showAvatar == true
|
|
? Colors.black
|
|
: Colors.white.withAlpha(ChipUtils._kCheckmarkAlpha);
|
|
break;
|
|
default:
|
|
throw new Exception("Unknown brightness: " + this.theme.brightness);
|
|
}
|
|
|
|
ColorTween fadeTween = new ColorTween(begin: Colors.transparent, end: paintColor);
|
|
|
|
paintColor = this.checkmarkAnimation.status == AnimationStatus.reverse
|
|
? fadeTween.evaluate(this.checkmarkAnimation)
|
|
: paintColor;
|
|
|
|
Paint paint = new Paint();
|
|
paint.color = paintColor;
|
|
paint.style = PaintingStyle.stroke;
|
|
paint.strokeWidth = ChipUtils._kCheckmarkStrokeWidth *
|
|
(this.avatar != null ? this.avatar.size.height / 24.0f : 1.0f);
|
|
float t = this.checkmarkAnimation.status == AnimationStatus.reverse
|
|
? 1.0f
|
|
: this.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 (this.isDrawingCheckmark) {
|
|
if (this.theme.showAvatar == true) {
|
|
Rect avatarRect = _boxRect(this.avatar).shift(offset);
|
|
Paint darkenPaint = new Paint();
|
|
darkenPaint.color = selectionScrimTween.evaluate(this.checkmarkAnimation);
|
|
darkenPaint.blendMode = BlendMode.srcATop;
|
|
Path path = this.avatarBorder.getOuterPath(avatarRect);
|
|
context.canvas.drawPath(path, darkenPaint);
|
|
}
|
|
|
|
float checkSize = this.avatar.size.height * 0.75f;
|
|
Offset checkOffset = _boxParentData(this.avatar).offset +
|
|
new Offset(this.avatar.size.height * 0.125f, this.avatar.size.height * 0.125f);
|
|
this._paintCheck(context.canvas, offset + checkOffset, checkSize);
|
|
}
|
|
}
|
|
|
|
void _paintAvatar(PaintingContext context, Offset offset) {
|
|
void paintWithOverlay(PaintingContext _context, Offset _offset) {
|
|
_context.paintChild(this.avatar, _boxParentData(this.avatar).offset + _offset);
|
|
this._paintSelectionOverlay(_context, _offset);
|
|
}
|
|
|
|
if (this.theme.showAvatar == false && this.avatarDrawerAnimation.isDismissed) {
|
|
return;
|
|
}
|
|
|
|
Color disabledColor = this._disabledColor;
|
|
int disabledColorAlpha = disabledColor.alpha;
|
|
if (this.needsCompositing) {
|
|
context.pushLayer(new OpacityLayer(alpha: disabledColorAlpha), paintWithOverlay, offset);
|
|
}
|
|
else {
|
|
Paint _paint = new Paint();
|
|
_paint.color = this._disabledColor;
|
|
if (disabledColorAlpha != 0xff) {
|
|
context.canvas.saveLayer(_boxRect(this.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 = this._disabledColor.alpha;
|
|
if (!this.enableAnimation.isCompleted) {
|
|
if (this.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 = this._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) {
|
|
this._paintAvatar(context, offset);
|
|
if (this.deleteIconShowing) {
|
|
this._paintChild(context, offset, this.deleteIcon, this.isEnabled == true);
|
|
}
|
|
|
|
this._paintChild(context, offset, this.label, this.isEnabled == true);
|
|
}
|
|
|
|
const bool _debugShowTapTargetOutlines = false;
|
|
|
|
public override void debugPaint(PaintingContext context, Offset offset) {
|
|
D.assert(!_debugShowTapTargetOutlines);
|
|
D.assert(() => {
|
|
Paint outlinePaint = new Paint();
|
|
outlinePaint.color = new Color(0xff800000);
|
|
outlinePaint.strokeWidth = 1.0f;
|
|
outlinePaint.style = PaintingStyle.stroke;
|
|
if (this.deleteIconShowing) {
|
|
context.canvas.drawRect(this.deleteButtonRect.shift(offset), outlinePaint);
|
|
}
|
|
|
|
outlinePaint.color = new Color(0xff008000);
|
|
context.canvas.drawRect(this.pressRect.shift(offset), outlinePaint);
|
|
return true;
|
|
});
|
|
}
|
|
|
|
protected override bool hitTestSelf(Offset position) {
|
|
return this.deleteButtonRect.contains(position) || this.pressRect.contains(position);
|
|
}
|
|
}
|
|
}
|