您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

865 行
33 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.cupertino;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.widgets;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.physics;
using Unity.UIWidgets.rendering;
using UnityEngine;
using Color = Unity.UIWidgets.ui.Color;
using Rect = Unity.UIWidgets.ui.Rect;
using TextStyle = Unity.UIWidgets.painting.TextStyle;
namespace Unity.UIWidgets.cupertino {
public class CupertinoSlidingSegmentedControlsUtils {
public static readonly EdgeInsetsGeometry _kHorizontalItemPadding = EdgeInsets.symmetric(vertical: 2, horizontal: 3);
public static readonly Radius _kThumbRadius = Radius.circular(6.93f);
public static readonly EdgeInsets _kThumbInsets = EdgeInsets.symmetric(horizontal: 1);
public const float _kMinSegmentedControlHeight = 28.0f;
public static readonly Color _kSeparatorColor = new Color(0x4D8E8E93);
public static readonly CupertinoDynamicColor _kThumbColor = CupertinoDynamicColor.withBrightness(
color: new Color(0xFFFFFFFF),
darkColor: new Color(0xFF636366)
);
public static readonly EdgeInsets _kSeparatorInset = EdgeInsets.symmetric(vertical: 6);
public const float _kSeparatorWidth = 1;
public static readonly Radius _kSeparatorRadius = Radius.circular(_kSeparatorWidth/2);
public const float _kMinThumbScale = 0.95f;
public const float _kSegmentMinPadding = 9.25f;
public const float _kTouchYDistanceThreshold = 50.0f * 50.0f;
public const float _kCornerRadius = 8;
public static readonly SpringSimulation _kThumbSpringAnimationSimulation = new SpringSimulation(
new SpringDescription(mass: 1, stiffness: 503.551f, damping: 44.8799f),
0,
1,
0 // Everytime a new spring animation starts the previous animation stops.
);
public static readonly TimeSpan _kSpringAnimationDuration= TimeSpan.FromMilliseconds(412);
public static readonly TimeSpan _kOpacityAnimationDuration = TimeSpan.FromMilliseconds(470);
public static readonly TimeSpan _kHighlightAnimationDuration = TimeSpan.FromMilliseconds(200);
}
class _FontWeightTween : Tween<FontWeight> {
public _FontWeightTween(FontWeight begin = null, FontWeight end = null) : base(begin: begin, end: end) {
}
public override FontWeight lerp(float t) => FontWeight.lerp(begin, end, t);
}
public class CupertinoSlidingSegmentedControl<T> : StatefulWidget {
public CupertinoSlidingSegmentedControl(
Key key = null,
Dictionary<T, Widget> children = null,
ValueChanged<T> onValueChanged = null,
T groupValue = default,
Color thumbColor = null,
EdgeInsetsGeometry padding = null,
Color backgroundColor = null
) :base(key: key)
{
D.assert(children != null);
D.assert(children.Count >= 2);
D.assert(onValueChanged != null);
D.assert(
groupValue == null || children.Keys.Contains(groupValue),()=>
"The groupValue must be either null or one of the keys in the children map."
);
this.children = children;
this.onValueChanged = onValueChanged;
this.groupValue = groupValue;
this.thumbColor = thumbColor ?? CupertinoSlidingSegmentedControlsUtils._kThumbColor;
this.padding = padding ??CupertinoSlidingSegmentedControlsUtils._kHorizontalItemPadding;
this.backgroundColor = backgroundColor ?? CupertinoColors.tertiarySystemFill;
}
public readonly Dictionary<T, Widget> children;
public readonly T groupValue;
public readonly ValueChanged<T> onValueChanged;
public readonly Color backgroundColor;
public readonly Color thumbColor;
public readonly EdgeInsetsGeometry padding;
public override State createState() {
return new _SlidingSegmentedControlState<T>();
}
}
public class _SlidingSegmentedControlState<T> : TickerProviderStateMixin<CupertinoSlidingSegmentedControl<T>> {
public readonly Dictionary<T, AnimationController> _highlightControllers = new Dictionary<T, AnimationController>();
public readonly Tween<FontWeight> _highlightTween = new _FontWeightTween(begin: FontWeight.normal, end: FontWeight.w500);
public readonly Dictionary<T, AnimationController> _pressControllers = new Dictionary<T, AnimationController>();
public readonly Tween<float> _pressTween = new FloatTween(begin: 1, end: 0.2f);
List<T> keys;
public AnimationController thumbController;
public AnimationController separatorOpacityController;
public AnimationController thumbScaleController;
public readonly TapGestureRecognizer tap = new TapGestureRecognizer();
public readonly HorizontalDragGestureRecognizer drag = new HorizontalDragGestureRecognizer();
public readonly LongPressGestureRecognizer longPress = new LongPressGestureRecognizer();
AnimationController _createHighlightAnimationController( bool isCompleted = false ) {
return new AnimationController(
duration: CupertinoSlidingSegmentedControlsUtils._kHighlightAnimationDuration,
value: isCompleted ? 1 : 0,
vsync: this
);
}
AnimationController _createFadeoutAnimationController() {
return new AnimationController(
duration:CupertinoSlidingSegmentedControlsUtils._kOpacityAnimationDuration,
vsync: this
);
}
public override void initState() {
base.initState();
GestureArenaTeam team = new GestureArenaTeam();
longPress.team = team;
drag.team = team;
team.captain = drag;
_highlighted = widget.groupValue;
thumbController = new AnimationController(
duration: CupertinoSlidingSegmentedControlsUtils._kSpringAnimationDuration,
value: 0,
vsync: this
);
thumbScaleController = new AnimationController(
duration: CupertinoSlidingSegmentedControlsUtils._kSpringAnimationDuration,
value: 1,
vsync: this
);
separatorOpacityController = new AnimationController(
duration: CupertinoSlidingSegmentedControlsUtils._kSpringAnimationDuration,
value: 0,
vsync: this
);
foreach (T currentKey in widget.children.Keys) {
_highlightControllers[currentKey] = _createHighlightAnimationController(
isCompleted: currentKey.Equals(widget.groupValue) // Highlight the current selection.
);
_pressControllers[currentKey] = _createFadeoutAnimationController();
}
}
public override void didUpdateWidget(StatefulWidget oldWidget) {
oldWidget = (CupertinoSlidingSegmentedControl<T>)oldWidget;
base.didUpdateWidget(oldWidget);
// Update animation controllers.
foreach( T oldKey in ((CupertinoSlidingSegmentedControl<T>)oldWidget).children.Keys) {
if (!widget.children.ContainsKey(oldKey)) {
_highlightControllers[oldKey].dispose();
_pressControllers[oldKey].dispose();
_highlightControllers.Remove(oldKey);
_pressControllers.Remove(oldKey);
}
}
foreach( T newKey in widget.children.Keys) {
if (!_highlightControllers.Keys.Contains(newKey)) {
_highlightControllers[newKey] = _createHighlightAnimationController();
_pressControllers[newKey] = _createFadeoutAnimationController();
}
}
highlighted = widget.groupValue;
}
public override void dispose() {
foreach( AnimationController animationController in _highlightControllers.Values) {
animationController.dispose();
}
foreach( AnimationController animationController in _pressControllers.Values) {
animationController.dispose();
}
thumbScaleController.dispose();
thumbController.dispose();
separatorOpacityController.dispose();
drag.dispose();
tap.dispose();
longPress.dispose();
base.dispose();
}
void _animateHighlightController( T at = default, bool forward = false) {
if (at == null)
return;
AnimationController controller = _highlightControllers[at];
D.assert(!forward || controller != null);
controller?.animateTo(forward ? 1 : 0, duration: CupertinoSlidingSegmentedControlsUtils._kHighlightAnimationDuration, curve: Curves.ease);
}
T _highlighted;
public T highlighted{
set {
if (_highlighted.Equals(value))
return;
_animateHighlightController(at: value, forward: true);
_animateHighlightController(at: _highlighted, forward: false);
_highlighted = value;
}
}
T _pressed;
public T pressed{
set {
if (_pressed.Equals(value))
return;
if (_pressed != null) {
_pressControllers[_pressed]?.animateTo(0, duration: CupertinoSlidingSegmentedControlsUtils._kOpacityAnimationDuration, curve: Curves.ease);
}
if (!value.Equals(_highlighted)&& value != null) {
_pressControllers[value].animateTo(1, duration: CupertinoSlidingSegmentedControlsUtils._kOpacityAnimationDuration, curve: Curves.ease);
}
_pressed = value;
}
}
public void didChangeSelectedViaGesture() {
widget.onValueChanged(_highlighted);
}
public T indexToKey(int index) {
return index == null ? default : keys[index];
}
public override Widget build(BuildContext context) {
WidgetsD.debugCheckHasDirectionality(context);
switch (Directionality.of(context)) {
case TextDirection.ltr:
keys = widget.children.Keys.ToList();
break;
case TextDirection.rtl:
widget.children.Keys.ToList().Reverse();
keys = widget.children.Keys.ToList();
break;
}
List<Listenable> results = new List<Listenable>();
results.AddRange(_highlightControllers.Values);
results.AddRange(_pressControllers.Values);
return new AnimatedBuilder(
animation: ListenableUtils.merge(results),
builder: (BuildContext context1, Widget child1) =>{
List<Widget> children = new List<Widget>();
foreach( T currentKey in keys){
TextStyle textStyle = DefaultTextStyle.of(context1).style.copyWith(
fontWeight: _highlightTween.evaluate(_highlightControllers[currentKey])
);
Widget child = new DefaultTextStyle(
style: textStyle,
child:
new Opacity(
opacity: _pressTween.evaluate(_pressControllers[currentKey]),
child: new MetaData(
behavior: HitTestBehavior.opaque,
child: new Center(child: widget.children[currentKey])
)
)
);
children.Add(child);
}
int selectedIndex = widget.groupValue.Equals(default) ? 0 : keys.IndexOf(widget.groupValue);
Widget box = new _SlidingSegmentedControlRenderWidget<T>(
children: children,
selectedIndex: selectedIndex,
thumbColor: CupertinoDynamicColor.resolve(widget.thumbColor, context),
state: this
);
return new UnconstrainedBox(
constrainedAxis: Axis.horizontal,
child: new Container(
padding: widget.padding.resolve(Directionality.of(context)),
decoration: new BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(CupertinoSlidingSegmentedControlsUtils._kCornerRadius)),
color: CupertinoDynamicColor.resolve(widget.backgroundColor, context)
),
child: box
)
);
}
);
}
}
public class _SlidingSegmentedControlRenderWidget<T> : MultiChildRenderObjectWidget {
public _SlidingSegmentedControlRenderWidget(
Key key = null,
List<Widget> children = null,
int? selectedIndex = null,
Color thumbColor = null,
_SlidingSegmentedControlState<T> state = null
) : base(key: key, children: children ?? new List<Widget>()) {
this.selectedIndex = selectedIndex;
this.thumbColor = thumbColor;
this.state = state;
}
public readonly int? selectedIndex;
public readonly Color thumbColor;
public readonly _SlidingSegmentedControlState<T> state;
public override RenderObject createRenderObject(BuildContext context) {
return new _RenderSlidingSegmentedControl<T>(
selectedIndex: selectedIndex,
thumbColor: CupertinoDynamicColor.resolve(thumbColor, context),
state: state
);
}
public override void updateRenderObject(BuildContext context,RenderObject renderObject) {
renderObject = (_RenderSlidingSegmentedControl<T>) renderObject;
((_RenderSlidingSegmentedControl<T>)renderObject).thumbColor = CupertinoDynamicColor.resolve(thumbColor, context);
((_RenderSlidingSegmentedControl<T>)renderObject).guardedSetHighlightedIndex(selectedIndex ?? 0);
}
}
public class _ChildAnimationManifest {
public _ChildAnimationManifest(
float opacity = 1f,
float separatorOpacity = 0f) {
D.assert(separatorOpacity != null);
D.assert(opacity != null);
separatorTween = new FloatTween(begin: separatorOpacity, end: separatorOpacity);
opacityTween = new FloatTween(begin: opacity, end: opacity);
}
float opacity;
Tween<float> opacityTween;
public float separatorOpacity;
public Tween<float> separatorTween;
}
class _SlidingSegmentedControlContainerBoxParentData : ContainerBoxParentData<RenderBox> { }
public class _RenderSlidingSegmentedControl<T> : RenderBoxContainerDefaultsMixinContainerRenderObjectMixinRenderBox<RenderBox, ContainerBoxParentData<RenderBox>> {
public _RenderSlidingSegmentedControl(
int? selectedIndex = null,
Color thumbColor = null,
_SlidingSegmentedControlState<T> state = null
) {
D.assert(state != null);
_highlightedIndex = selectedIndex;
_thumbColor = thumbColor;
this.state = state;
this.state.drag.onDown = _onDown;
this.state.drag.onUpdate = _onUpdate;
this.state.drag.onEnd = _onEnd;
this.state.drag.onCancel = _onCancel;
this.state.tap.onTapUp = _onTapUp;
this.state.longPress.onLongPress = ()=> { };
}
public _SlidingSegmentedControlState<T> state;
Dictionary<RenderBox, _ChildAnimationManifest> _childAnimations = new Dictionary<RenderBox, _ChildAnimationManifest>{};
Rect currentThumbRect;
Tween<Rect> _currentThumbTween;
Tween<float> _thumbScaleTween = new FloatTween(begin: CupertinoSlidingSegmentedControlsUtils._kMinThumbScale, end: 1);
float currentThumbScale = 1;
Offset _localDragOffset;
bool _startedOnSelectedSegment;
public override void insert(RenderBox child, RenderBox after = null) {
base.insert(child, after: after);
if (_childAnimations == null)
return;
D.assert(_childAnimations.getOrDefault(child) == null);
_childAnimations[child] = new _ChildAnimationManifest(separatorOpacity: 1);
}
public override void remove(RenderBox child) {
base.remove(child);
_childAnimations?.Remove(child);
}
public override void attach(object owner) {
owner = (PipelineOwner) owner;
base.attach(owner);
state.thumbController.addListener(markNeedsPaint);
state.thumbScaleController.addListener(markNeedsPaint);
state.separatorOpacityController.addListener(markNeedsPaint);
}
public override void detach() {
state.thumbController.removeListener(markNeedsPaint);
state.thumbScaleController.removeListener(markNeedsPaint);
state.separatorOpacityController.removeListener(markNeedsPaint);
base.detach();
}
bool _needsThumbAnimationUpdate = false;
int? highlightedIndex {
get {
return _highlightedIndex;
}
set {
if (_highlightedIndex == value) {
return;
}
_needsThumbAnimationUpdate = true;
_highlightedIndex = value;
state.thumbController.animateWith(CupertinoSlidingSegmentedControlsUtils._kThumbSpringAnimationSimulation);
state.separatorOpacityController.reset();
state.separatorOpacityController.animateTo(
1,
duration: CupertinoSlidingSegmentedControlsUtils._kSpringAnimationDuration,
curve: Curves.ease
);
state.highlighted = state.indexToKey(value??0);
markNeedsPaint();
//markNeedsSemanticsUpdate();
}
}
int? _highlightedIndex;
public void guardedSetHighlightedIndex(int value) {
// Ignore set highlightedIndex when the user is dragging the thumb around.
if (_startedOnSelectedSegment == true)
return;
highlightedIndex = value;
}
int? pressedIndex {
get {
return _pressedIndex;
}
set {
if (_pressedIndex == value) {
return;
}
D.assert(value == null || (value >= 0 && value < childCount));
_pressedIndex = value;
state.pressed = state.indexToKey(value ?? 0);
}
}
int? _pressedIndex;
public Color thumbColor {
get {
return _thumbColor;
}
set {
if (_thumbColor == value) {
return;
}
_thumbColor = value;
markNeedsPaint();
}
}
Color _thumbColor;
float totalSeparatorWidth {
get {
return (CupertinoSlidingSegmentedControlsUtils._kSeparatorInset.horizontal + CupertinoSlidingSegmentedControlsUtils._kSeparatorWidth) * (childCount - 1);
}
}
public override void handleEvent(PointerEvent _event, HitTestEntry entry) {
entry = (BoxHitTestEntry) entry;
D.assert(debugHandleEvent(_event, entry));
if (_event is PointerDownEvent) {
state.tap.addPointer((PointerDownEvent)_event);
state.longPress.addPointer((PointerDownEvent)_event);
state.drag.addPointer((PointerDownEvent)_event);
}
}
int? indexFromLocation(Offset location) {
return childCount == 0
? (int?) null
// This assumes all children have the same width.
: (int)((location.dx / (size.width / childCount))
.floor()
.clamp(0, childCount - 1) );
}
void _onTapUp(TapUpDetails details) {
highlightedIndex = indexFromLocation(details.localPosition);
state.didChangeSelectedViaGesture();
}
void _onDown(DragDownDetails details) {
D.assert(size.contains(details.localPosition));
_localDragOffset = details.localPosition;
int? index = indexFromLocation(_localDragOffset);
_startedOnSelectedSegment = index == highlightedIndex;
pressedIndex = index;
if (_startedOnSelectedSegment) {
_playThumbScaleAnimation(isExpanding: false);
}
}
void _onUpdate(DragUpdateDetails details) {
_localDragOffset = details.localPosition;
int? newIndex = indexFromLocation(_localDragOffset);
if (_startedOnSelectedSegment) {
highlightedIndex = newIndex;
pressedIndex = newIndex;
} else {
pressedIndex = _hasDraggedTooFar(details) ? 0 : newIndex;
}
}
void _onEnd(DragEndDetails details) {
if (_startedOnSelectedSegment) {
_playThumbScaleAnimation(isExpanding: true);
state.didChangeSelectedViaGesture();
}
if (pressedIndex != null) {
highlightedIndex = pressedIndex;
state.didChangeSelectedViaGesture();
}
pressedIndex = null;
_localDragOffset = null;
_startedOnSelectedSegment = false;
}
void _onCancel() {
if (_startedOnSelectedSegment) {
_playThumbScaleAnimation(isExpanding: true);
}
_localDragOffset = null;
pressedIndex = null;
_startedOnSelectedSegment = false;
}
void _playThumbScaleAnimation(bool isExpanding = false) {
D.assert(isExpanding != null);
_thumbScaleTween = new FloatTween(begin: currentThumbScale, end: isExpanding ? 1 : CupertinoSlidingSegmentedControlsUtils._kMinThumbScale);
state.thumbScaleController.animateWith(CupertinoSlidingSegmentedControlsUtils._kThumbSpringAnimationSimulation);
}
bool _hasDraggedTooFar(DragUpdateDetails details) {
Offset offCenter = details.localPosition - new Offset(size.width/2, size.height/2);
return Mathf.Pow(Mathf.Max(0, offCenter.dx.abs() - size.width/2), 2) + Mathf.Pow(Mathf.Max(0, offCenter.dy.abs() - size.height/2), 2) > CupertinoSlidingSegmentedControlsUtils._kTouchYDistanceThreshold;
}
protected internal override float computeMinIntrinsicWidth(float height) {
RenderBox child = firstChild;
float maxMinChildWidth = 0;
while (child != null) {
_SlidingSegmentedControlContainerBoxParentData childParentData =
child.parentData as _SlidingSegmentedControlContainerBoxParentData;
float childWidth = child.getMinIntrinsicWidth(height);
maxMinChildWidth = Math.Max(maxMinChildWidth, childWidth);
child = childParentData.nextSibling;
}
return (maxMinChildWidth + 2 * CupertinoSlidingSegmentedControlsUtils._kSegmentMinPadding) * childCount + totalSeparatorWidth;
}
protected internal override float computeMaxIntrinsicWidth(float height) {
RenderBox child = firstChild;
float maxMaxChildWidth = 0;
while (child != null) {
_SlidingSegmentedControlContainerBoxParentData childParentData =
child.parentData as _SlidingSegmentedControlContainerBoxParentData;
float childWidth = child.getMaxIntrinsicWidth(height);
maxMaxChildWidth = Mathf.Max(maxMaxChildWidth, childWidth);
child = childParentData.nextSibling;
}
return (maxMaxChildWidth + 2 * CupertinoSlidingSegmentedControlsUtils._kSegmentMinPadding) * childCount + totalSeparatorWidth;
}
protected internal override float computeMinIntrinsicHeight(float width) {
RenderBox child = firstChild;
float maxMinChildHeight = 0;
while (child != null) {
_SlidingSegmentedControlContainerBoxParentData childParentData =
child.parentData as _SlidingSegmentedControlContainerBoxParentData;
float childHeight = child.getMinIntrinsicHeight(width);
maxMinChildHeight = Mathf.Max(maxMinChildHeight, childHeight);
child = childParentData.nextSibling;
}
return maxMinChildHeight;
}
protected internal override float computeMaxIntrinsicHeight(float width) {
RenderBox child = firstChild;
float maxMaxChildHeight = 0;
while (child != null) {
_SlidingSegmentedControlContainerBoxParentData childParentData =
child.parentData as _SlidingSegmentedControlContainerBoxParentData;
float childHeight = child.getMaxIntrinsicHeight(width);
maxMaxChildHeight = Mathf.Max(maxMaxChildHeight, childHeight);
child = childParentData.nextSibling;
}
return maxMaxChildHeight;
}
public override float? computeDistanceToActualBaseline(TextBaseline baseline) {
return defaultComputeDistanceToHighestActualBaseline(baseline);
}
public override void setupParentData(RenderObject child) {
child = (RenderBox) child;
if (!(child.parentData is _SlidingSegmentedControlContainerBoxParentData)) {
child.parentData = new _SlidingSegmentedControlContainerBoxParentData();
}
}
protected override void performLayout() {
BoxConstraints constraints = this.constraints;
float childWidth = (constraints.minWidth - totalSeparatorWidth) / childCount;
float maxHeight = CupertinoSlidingSegmentedControlsUtils._kMinSegmentedControlHeight;
foreach( RenderBox child1 in getChildrenAsList()) {
childWidth = Mathf.Max(childWidth, child1.getMaxIntrinsicWidth(float.PositiveInfinity) + 2 * CupertinoSlidingSegmentedControlsUtils._kSegmentMinPadding);
}
childWidth = Mathf.Min(
childWidth,
(constraints.maxWidth - totalSeparatorWidth) / childCount
);
RenderBox child = firstChild;
while (child != null) {
float boxHeight = child.getMaxIntrinsicHeight(childWidth);
maxHeight = Mathf.Max(maxHeight, boxHeight);
child = childAfter(child);
}
constraints.constrainHeight(maxHeight);
BoxConstraints childConstraints = BoxConstraints.tightFor(
width: childWidth,
height: maxHeight
);
// Layout children.
child = firstChild;
while (child != null) {
child.layout(childConstraints, parentUsesSize: true);
child = childAfter(child);
}
float start = 0;
child = firstChild;
while (child != null) {
_SlidingSegmentedControlContainerBoxParentData childParentData =
child.parentData as _SlidingSegmentedControlContainerBoxParentData;
Offset childOffset = new Offset(start, 0);
childParentData.offset = childOffset;
start += child.size.width + CupertinoSlidingSegmentedControlsUtils._kSeparatorWidth + CupertinoSlidingSegmentedControlsUtils._kSeparatorInset.horizontal;
child = childAfter(child);
}
size = constraints.constrain(new Size(childWidth * childCount + totalSeparatorWidth, maxHeight));
}
public override void paint(PaintingContext context, Offset offset) {
List<RenderBox> children = getChildrenAsList();
// Paint thumb if highlightedIndex is not null.
if (highlightedIndex != null) {
if (_childAnimations == null) {
_childAnimations = new Dictionary<RenderBox, _ChildAnimationManifest>();//<RenderBox, _ChildAnimationManifest> { };
for (int i = 0; i < childCount - 1; i += 1) {
bool shouldFadeOut = i == highlightedIndex || i == highlightedIndex - 1;
RenderBox child = children[i];
_childAnimations[child] = new _ChildAnimationManifest(separatorOpacity: shouldFadeOut ? 0 : 1);
}
}
RenderBox selectedChild = children[highlightedIndex ?? 0];
_SlidingSegmentedControlContainerBoxParentData childParentData =
selectedChild.parentData as _SlidingSegmentedControlContainerBoxParentData;
Rect unscaledThumbTargetRect = CupertinoSlidingSegmentedControlsUtils._kThumbInsets.inflateRect(childParentData.offset & selectedChild.size);
// Update related Tweens before animation update phase.
if (_needsThumbAnimationUpdate) {
// Needs to ensure _currentThumbRect is valid.
_currentThumbTween = new RectTween(begin: currentThumbRect ?? unscaledThumbTargetRect, end: unscaledThumbTargetRect);
for (int i = 0; i < childCount - 1; i += 1) {
bool shouldFadeOut = i == highlightedIndex || i == highlightedIndex - 1;
RenderBox child = children[i];
_ChildAnimationManifest manifest = _childAnimations[child];
D.assert(manifest != null);
manifest.separatorTween = new FloatTween(
begin: manifest.separatorOpacity,
end: shouldFadeOut ? 0 : 1
);
}
_needsThumbAnimationUpdate = false;
} else if (_currentThumbTween != null && unscaledThumbTargetRect != _currentThumbTween.begin) {
_currentThumbTween = new RectTween(begin: _currentThumbTween.begin, end: unscaledThumbTargetRect);
}
for (int index = 0; index < childCount - 1; index += 1) {
_paintSeparator(context, offset, children[index]);
}
currentThumbRect = _currentThumbTween?.evaluate(state.thumbController)
?? unscaledThumbTargetRect;
currentThumbScale = _thumbScaleTween.evaluate(state.thumbScaleController);
Rect thumbRect = Rect.fromCenter(
center: currentThumbRect.center,
width: currentThumbRect.width * currentThumbScale,
height: currentThumbRect.height * currentThumbScale
);
_paintThumb(context, offset, thumbRect);
} else {
// Reset all animations when there"s no thumb.
currentThumbRect = null;
_childAnimations = null;
for (int index = 0; index < childCount - 1; index += 1) {
_paintSeparator(context, offset, children[index]);
}
}
for (int index = 0; index < children.Count; index++) {
_paintChild(context, offset, children[index], index);
}
}
// Paint the separator to the right of the given child.
void _paintSeparator(PaintingContext context, Offset offset, RenderBox child) {
D.assert(child != null);
_SlidingSegmentedControlContainerBoxParentData childParentData =
child.parentData as _SlidingSegmentedControlContainerBoxParentData;
Paint paint = new Paint();
_ChildAnimationManifest manifest = _childAnimations == null ? null : _childAnimations[child];
float opacity = manifest?.separatorTween?.evaluate(state.separatorOpacityController) ?? 1;
if(manifest != null)
manifest.separatorOpacity = opacity;
paint.color = CupertinoSlidingSegmentedControlsUtils._kSeparatorColor.withOpacity(CupertinoSlidingSegmentedControlsUtils._kSeparatorColor.opacity * opacity);
Rect childRect = (childParentData.offset + offset) & child.size;
Rect separatorRect = CupertinoSlidingSegmentedControlsUtils._kSeparatorInset.deflateRect(
childRect.topRight & new Size(CupertinoSlidingSegmentedControlsUtils._kSeparatorInset.horizontal + CupertinoSlidingSegmentedControlsUtils._kSeparatorWidth,
child.size.height)
);
context.canvas.drawRRect(
RRect.fromRectAndRadius(separatorRect, CupertinoSlidingSegmentedControlsUtils._kSeparatorRadius),
paint
);
}
void _paintChild(PaintingContext context, Offset offset, RenderBox child, int childIndex) {
D.assert(child != null);
_SlidingSegmentedControlContainerBoxParentData childParentData =
child.parentData as _SlidingSegmentedControlContainerBoxParentData;
context.paintChild(child, childParentData.offset + offset);
}
void _paintThumb(PaintingContext context, Offset offset, Rect thumbRect) {
List<BoxShadow> thumbShadow = new List<BoxShadow> {
new BoxShadow(
color: new Color(0x1F000000),
offset: new Offset(0, 3),
blurRadius: 8
),
new BoxShadow(
color: new Color(0x0A000000),
offset: new Offset(0, 3),
blurRadius: 1
)
};
RRect thumbRRect = RRect.fromRectAndRadius(thumbRect.shift(offset), CupertinoSlidingSegmentedControlsUtils._kThumbRadius);
foreach( BoxShadow shadow in thumbShadow) {
context.canvas.drawRRect(thumbRRect.shift(shadow.offset), shadow.toPaint());
}
context.canvas.drawRRect(
thumbRRect.inflate(0.5f),
new Paint(){color = new Color(0x0A000000)}
);
context.canvas.drawRRect(
thumbRRect,
new Paint(){color = thumbColor}
);
}
protected override bool hitTestChildren(BoxHitTestResult result, Offset position ) {
D.assert(position != null);
RenderBox child = lastChild;
while (child != null) {
_SlidingSegmentedControlContainerBoxParentData childParentData =
child.parentData as _SlidingSegmentedControlContainerBoxParentData;
if ((childParentData.offset & child.size).contains(position)) {
Offset center = (Offset.zero & child.size).center;
return result.addWithRawTransform(
transform: MatrixUtils.forceToPoint(center),
position: center,
hitTest: (BoxHitTestResult result1, Offset position1)=> {
D.assert(position1 == center);
return child.hitTest(result1, position: center);
}
);
}
child = childParentData.previousSibling;
}
return false;
}
}
}