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

380 行
15 KiB

using System;
using System.Collections.Generic;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.async;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
namespace Unity.UIWidgets.cupertino {
class CupertinoScrollbarUtils {
public const float _kScrollbarMinLength = 36.0f;
public const float _kScrollbarMinOverscrollLength = 8.0f;
public static readonly TimeSpan _kScrollbarTimeToFade = TimeSpan.FromMilliseconds(1200);
public static readonly TimeSpan _kScrollbarFadeDuration = TimeSpan.FromMilliseconds(250);
public static readonly TimeSpan _kScrollbarResizeDuration = TimeSpan.FromMilliseconds(100);
public static readonly Color _kScrollbarColor = CupertinoDynamicColor.withBrightness(
color: new Color(0x59000000),
darkColor: new Color(0x80FFFFFF)
);
public const float _kScrollbarThickness = 3f;
public const float _kScrollbarThicknessDragging = 8.0f;
public static Radius _kScrollbarRadius = Radius.circular(1.5f);
public static Radius _kScrollbarRadiusDragging = Radius.circular(4.0f);
public const float _kScrollbarMainAxisMargin = 3.0f;
public const float _kScrollbarCrossAxisMargin = 3.0f;
public static bool _hitTestInteractive(GlobalKey customPaintKey, Offset offset) {
if (customPaintKey.currentContext == null) {
return false;
}
CustomPaint customPaint = customPaintKey.currentContext.widget as CustomPaint;
ScrollbarPainter painter = customPaint.foregroundPainter as ScrollbarPainter;
RenderBox renderBox = customPaintKey.currentContext.findRenderObject() as RenderBox;
Offset localOffset = renderBox.globalToLocal(offset);
return painter.hitTestInteractive(localOffset);
}
}
public class CupertinoScrollbar : StatefulWidget {
public CupertinoScrollbar(
Key key = null,
ScrollController controller = null,
bool isAlwaysShown = false,
Widget child = null
) : base(key: key) {
this.child = child;
this.controller = controller;
this.isAlwaysShown = isAlwaysShown;
}
public readonly Widget child;
public readonly ScrollController controller;
public readonly bool isAlwaysShown;
public override State createState() {
return new _CupertinoScrollbarState();
}
}
class _CupertinoScrollbarState : TickerProviderStateMixin<CupertinoScrollbar> {
GlobalKey _customPaintKey = GlobalKey.key();
ScrollbarPainter _painter;
TextDirection _textDirection;
AnimationController _fadeoutAnimationController;
Animation<float> _fadeoutOpacityAnimation;
AnimationController _thicknessAnimationController;
float _dragScrollbarPositionY;
Timer _fadeoutTimer;
Drag _drag;
float _thickness {
get {
return CupertinoScrollbarUtils._kScrollbarThickness + _thicknessAnimationController.value * (CupertinoScrollbarUtils._kScrollbarThicknessDragging - CupertinoScrollbarUtils._kScrollbarThickness);
}
}
Radius _radius {
get {
return Radius.lerp(CupertinoScrollbarUtils._kScrollbarRadius, CupertinoScrollbarUtils._kScrollbarRadiusDragging, _thicknessAnimationController.value);
}
}
ScrollController _currentController;
ScrollController _controller {
get {
return widget.controller ?? PrimaryScrollController.of(context);
}
}
public override void initState() {
base.initState();
_fadeoutAnimationController = new AnimationController(
vsync: this,
duration: CupertinoScrollbarUtils._kScrollbarFadeDuration
);
_fadeoutOpacityAnimation = new CurvedAnimation(
parent: _fadeoutAnimationController,
curve: Curves.fastOutSlowIn
);
_thicknessAnimationController = new AnimationController(
vsync: this,
duration: CupertinoScrollbarUtils._kScrollbarResizeDuration
);
_thicknessAnimationController.addListener(() => {
_painter.updateThickness(_thickness, _radius);
});
}
public override void didChangeDependencies() {
base.didChangeDependencies();
if (_painter == null) {
_painter = _buildCupertinoScrollbarPainter(context);
} else {
_painter.textDirection = Directionality.of(context);
_painter.color = CupertinoDynamicColor.resolve(CupertinoScrollbarUtils._kScrollbarColor, context);
_painter.padding = MediaQuery.of(context).padding;
}
WidgetsBinding.instance.addPostFrameCallback((TimeSpan duration)=> {
if (widget.isAlwaysShown) {
D.assert(widget.controller != null);
widget.controller.position.didUpdateScrollPositionBy(0);
}
});
}
public override void didUpdateWidget(StatefulWidget oldWidget) {
oldWidget = (CupertinoScrollbar) oldWidget;
base.didUpdateWidget(oldWidget);
if (widget.isAlwaysShown != ((CupertinoScrollbar)oldWidget).isAlwaysShown) {
if (widget.isAlwaysShown == true) {
D.assert(widget.controller != null);
_fadeoutAnimationController.animateTo(1.0f);
} else {
_fadeoutAnimationController.reverse();
}
}
}
public ScrollbarPainter _buildCupertinoScrollbarPainter(BuildContext context) {
return new ScrollbarPainter(
color: CupertinoDynamicColor.resolve(CupertinoScrollbarUtils._kScrollbarColor, context),
textDirection: Directionality.of(context),
thickness: _thickness,
fadeoutOpacityAnimation: _fadeoutOpacityAnimation,
mainAxisMargin: CupertinoScrollbarUtils._kScrollbarMainAxisMargin,
crossAxisMargin: CupertinoScrollbarUtils._kScrollbarCrossAxisMargin,
radius: _radius,
padding: MediaQuery.of(context).padding,
minLength: CupertinoScrollbarUtils._kScrollbarMinLength,
minOverscrollLength: CupertinoScrollbarUtils._kScrollbarMinOverscrollLength
);
}
void _dragScrollbar(float primaryDelta) {
D.assert(_currentController != null);
float scrollOffsetLocal = _painter.getTrackToScroll(primaryDelta);
float scrollOffsetGlobal = scrollOffsetLocal + _currentController.position.pixels;
if (_drag == null) {
_drag = _currentController.position.drag(
new DragStartDetails(
globalPosition: new Offset(0.0f, scrollOffsetGlobal)
),
() =>{}
);
} else {
_drag.update(
new DragUpdateDetails(
delta: new Offset(0.0f, -scrollOffsetLocal),
primaryDelta: (float?) -1f * scrollOffsetLocal,
globalPosition: new Offset(0.0f, scrollOffsetGlobal)
)
);
}
}
void _startFadeoutTimer() {
if (!widget.isAlwaysShown) {
_fadeoutTimer?.cancel();
_fadeoutTimer = Timer.create(CupertinoScrollbarUtils._kScrollbarTimeToFade, () => {
_fadeoutAnimationController.reverse();
_fadeoutTimer = null;
});
}
}
bool _checkVertical() {
return _currentController.position.axis() == Axis.vertical;
}
float _pressStartY = 0.0f;
void _handleLongPressStart(LongPressStartDetails details) {
_currentController = _controller;
if (!_checkVertical()) {
return;
}
_pressStartY = details.localPosition.dy;
_fadeoutTimer?.cancel();
_fadeoutAnimationController.forward();
_dragScrollbar(details.localPosition.dy);
_dragScrollbarPositionY = details.localPosition.dy;
}
void _handleLongPress() {
if (!_checkVertical()) {
return;
}
_fadeoutTimer?.cancel();
_thicknessAnimationController.forward().then(
(_) => { return; }
);
}
void _handleLongPressMoveUpdate(LongPressMoveUpdateDetails details) {
if (!_checkVertical()) {
return;
}
_dragScrollbar(details.localPosition.dy - _dragScrollbarPositionY);
_dragScrollbarPositionY = details.localPosition.dy;
}
void _handleLongPressEnd(LongPressEndDetails details) {
if (!_checkVertical()) {
return;
}
_handleDragScrollEnd(details.velocity.pixelsPerSecond.dy);
if (details.velocity.pixelsPerSecond.dy.abs() < 10 &&
(details.localPosition.dy - _pressStartY).abs() > 0) {
//HapticFeedback.mediumImpact();
}
_currentController = null;
}
void _handleDragScrollEnd(float trackVelocityY) {
_startFadeoutTimer();
_thicknessAnimationController.reverse();
_dragScrollbarPositionY = 0.0f;
float scrollVelocityY = _painter.getTrackToScroll(trackVelocityY);
_drag?.end(new DragEndDetails(
primaryVelocity: -scrollVelocityY,
velocity: new Velocity(
pixelsPerSecond: new Offset(
0.0f,
-scrollVelocityY
)
)
));
_drag = null;
}
bool _handleScrollNotification(ScrollNotification notification) {
ScrollMetrics metrics = notification.metrics;
if (metrics.maxScrollExtent <= metrics.minScrollExtent) {
return false;
}
if (notification is ScrollUpdateNotification ||
notification is OverscrollNotification) {
if (_fadeoutAnimationController.status != AnimationStatus.forward) {
_fadeoutAnimationController.forward();
}
_fadeoutTimer?.cancel();
_painter.update(notification.metrics, notification.metrics.axisDirection);
} else if (notification is ScrollEndNotification) {
if (_dragScrollbarPositionY == null) {
_startFadeoutTimer();
}
}
return false;
}
Dictionary<Type, GestureRecognizerFactory> _gestures {
get {
Dictionary<Type, GestureRecognizerFactory> gestures =
new Dictionary<Type, GestureRecognizerFactory>();
gestures[typeof(_ThumbPressGestureRecognizer)] =
new GestureRecognizerFactoryWithHandlers<_ThumbPressGestureRecognizer>(
() => new _ThumbPressGestureRecognizer(
debugOwner: this,
customPaintKey: _customPaintKey
),
(_ThumbPressGestureRecognizer instance)=> {
instance.onLongPressStart = _handleLongPressStart;
instance.onLongPress = _handleLongPress;
instance.onLongPressMoveUpdate = _handleLongPressMoveUpdate;
instance.onLongPressEnd = _handleLongPressEnd;
}
);
return gestures;
}
}
public override void dispose() {
_fadeoutAnimationController.dispose();
_thicknessAnimationController.dispose();
_fadeoutTimer?.cancel();
_painter.dispose();
base.dispose();
}
ScrollbarPainter _buildCupertinoScrollbarPainter() {
return new ScrollbarPainter(
color: CupertinoScrollbarUtils._kScrollbarColor,
textDirection: _textDirection,
thickness: CupertinoScrollbarUtils._kScrollbarThickness,
fadeoutOpacityAnimation: _fadeoutOpacityAnimation,
mainAxisMargin: CupertinoScrollbarUtils._kScrollbarMainAxisMargin,
crossAxisMargin: CupertinoScrollbarUtils._kScrollbarCrossAxisMargin,
radius: CupertinoScrollbarUtils._kScrollbarRadius,
minLength: CupertinoScrollbarUtils._kScrollbarMinLength,
minOverscrollLength: CupertinoScrollbarUtils._kScrollbarMinOverscrollLength
);
}
public override Widget build(BuildContext context) {
return new NotificationListener<ScrollNotification>(
onNotification: _handleScrollNotification,
child: new RepaintBoundary(
child: new RawGestureDetector(
gestures: _gestures,
child: new CustomPaint(
key: _customPaintKey,
foregroundPainter: _painter,
child: new RepaintBoundary(child: widget.child)
)
)
)
);
}
}
public class _ThumbPressGestureRecognizer : LongPressGestureRecognizer {
public _ThumbPressGestureRecognizer(
float? postAcceptSlopTolerance = null,
PointerDeviceKind kind = default,
object debugOwner = null,
GlobalKey customPaintKey = null
) : base(
postAcceptSlopTolerance: postAcceptSlopTolerance,
kind: kind,
debugOwner: debugOwner,
duration: TimeSpan.FromMilliseconds(100)
) {
_customPaintKey = customPaintKey;
}
public readonly GlobalKey _customPaintKey;
protected override bool isPointerAllowed(PointerDownEvent _event) {
if (!CupertinoScrollbarUtils._hitTestInteractive(_customPaintKey, _event.position)) {
return false;
}
return base.isPointerAllowed(_event);
}
}
}