Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
dom
2026-01-20 17:42:40 +08:00
parent 923af32c96
commit 0b1f6c4d0e
26 changed files with 377 additions and 451 deletions

View File

@@ -7,21 +7,21 @@ class Arc extends LeafRenderObjectWidget {
super.key,
required this.size,
required this.color,
required this.sweepAngle,
required this.progress,
this.strokeWidth = 2,
});
final double size;
final Color color;
final double sweepAngle;
final double progress;
final double strokeWidth;
@override
RenderObject createRenderObject(BuildContext context) {
return RenderArc(
size: size,
preferredSize: size,
color: color,
sweepAngle: sweepAngle,
progress: progress,
strokeWidth: strokeWidth,
);
}
@@ -32,21 +32,22 @@ class Arc extends LeafRenderObjectWidget {
RenderArc renderObject,
) {
renderObject
..preferredSize = size
..color = color
..sweepAngle = sweepAngle
..progress = progress
..strokeWidth = strokeWidth;
}
}
class RenderArc extends RenderBox {
RenderArc({
required double size,
required double preferredSize,
required Color color,
required double sweepAngle,
required double progress,
required double strokeWidth,
}) : _preferredSize = Size.square(size),
}) : _preferredSize = preferredSize,
_color = color,
_sweepAngle = sweepAngle,
_progress = progress,
_strokeWidth = strokeWidth;
Color _color;
@@ -57,11 +58,11 @@ class RenderArc extends RenderBox {
markNeedsPaint();
}
double _sweepAngle;
double get sweepAngle => _sweepAngle;
set sweepAngle(double value) {
if (_sweepAngle == value) return;
_sweepAngle = value;
double _progress;
double get progress => _progress;
set progress(double value) {
if (_progress == value) return;
_progress = value;
markNeedsPaint();
}
@@ -73,8 +74,9 @@ class RenderArc extends RenderBox {
markNeedsPaint();
}
Size _preferredSize;
set preferredSize(Size value) {
double _preferredSize;
double get preferredSize => _preferredSize;
set preferredSize(double value) {
if (_preferredSize == value) return;
_preferredSize = value;
markNeedsLayout();
@@ -82,12 +84,12 @@ class RenderArc extends RenderBox {
@override
void performLayout() {
size = constraints.constrain(_preferredSize);
size = constraints.constrainDimensions(_preferredSize, _preferredSize);
}
@override
void paint(PaintingContext context, Offset offset) {
if (sweepAngle == 0) {
if (progress == 0) {
return;
}
@@ -96,15 +98,14 @@ class RenderArc extends RenderBox {
..strokeWidth = strokeWidth
..style = PaintingStyle.stroke;
final size = this.size;
final radius = size.width / 2;
final rect = Rect.fromCircle(
center: Offset(size.width / 2, size.height / 2),
radius: size.width / 2,
center: Offset(radius, radius),
radius: radius,
);
const startAngle = -pi / 2;
context.canvas.drawArc(rect, startAngle, sweepAngle, false, paint);
context.canvas.drawArc(rect, startAngle, progress * 2 * pi, false, paint);
}
@override

View File

@@ -7,14 +7,18 @@ class DisabledIcon<T extends Widget> extends SingleChildRenderObjectWidget {
const DisabledIcon({
super.key,
required T child,
this.disable = false,
this.color,
this.iconSize,
double? lineLengthScale,
StrokeCap? strokeCap,
}) : lineLengthScale = lineLengthScale ?? 0.9,
strokeCap = strokeCap ?? StrokeCap.butt,
super(child: child);
final bool disable;
final Color? color;
final double? iconSize;
final StrokeCap strokeCap;
final double lineLengthScale;
@@ -22,12 +26,18 @@ class DisabledIcon<T extends Widget> extends SingleChildRenderObjectWidget {
@override
RenderObject createRenderObject(BuildContext context) {
late final iconTheme = IconTheme.of(context);
return RenderMaskedIcon(
disable: disable,
iconSize:
iconSize ??
(child is Icon ? (child as Icon?)?.size : null) ??
iconTheme.size ??
24.0,
color:
color ??
(child is Icon
? (child as Icon).color ?? IconTheme.of(context).color!
: IconTheme.of(context).color!),
(child is Icon ? (child as Icon?)?.color : null) ??
iconTheme.color!,
strokeCap: strokeCap,
lineLengthScale: lineLengthScale,
);
@@ -35,12 +45,18 @@ class DisabledIcon<T extends Widget> extends SingleChildRenderObjectWidget {
@override
void updateRenderObject(BuildContext context, RenderMaskedIcon renderObject) {
late final iconTheme = IconTheme.of(context);
renderObject
..disable = disable
..iconSize =
iconSize ??
(child is Icon ? (child as Icon?)?.size : null) ??
iconTheme.size ??
24.0
..color =
color ??
(child is Icon
? (child as Icon).color ?? IconTheme.of(context).color!
: IconTheme.of(context).color!)
(child is Icon ? (child as Icon?)?.color : null) ??
iconTheme.color!
..strokeCap = strokeCap
..lineLengthScale = lineLengthScale;
}
@@ -48,13 +64,33 @@ class DisabledIcon<T extends Widget> extends SingleChildRenderObjectWidget {
class RenderMaskedIcon extends RenderProxyBox {
RenderMaskedIcon({
required bool disable,
required double iconSize,
required Color color,
required StrokeCap strokeCap,
required double lineLengthScale,
}) : _color = color,
}) : _disable = disable,
_iconSize = iconSize,
_color = color,
_strokeCap = strokeCap,
_lineLengthScale = lineLengthScale;
bool _disable;
bool get disable => _disable;
set disable(bool value) {
if (_disable == value) return;
_disable = value;
markNeedsPaint();
}
double _iconSize;
double get iconSize => _iconSize;
set iconSize(double value) {
if (_iconSize == value) return;
_iconSize = value;
markNeedsPaint();
}
Color _color;
Color get color => _color;
set color(Color value) {
@@ -81,23 +117,30 @@ class RenderMaskedIcon extends RenderProxyBox {
@override
void paint(PaintingContext context, Offset offset) {
final strokeWidth = size.width / 12;
if (!disable) {
return super.paint(context, offset);
}
final canvas = context.canvas;
Size size = this.size;
final exceedWidth = size.width > _iconSize;
final exceedHeight = size.height > _iconSize;
if (exceedWidth || exceedHeight) {
final dx = exceedWidth ? (size.width - _iconSize) / 2.0 : 0.0;
final dy = exceedHeight ? (size.height - _iconSize) / 2.0 : 0.0;
size = Size.square(_iconSize);
offset = Offset(dx, dy);
} else if (size.width < _iconSize && size.height < _iconSize) {
size = Size.square(_iconSize);
}
final strokeWidth = size.width / 12;
var rect = offset & size;
final sqrt2Width = strokeWidth * sqrt2; // rotate pi / 4
// final path = Path.combine(
// PathOperation.difference,
// Path()..addRect(rect),
// Path()..moveTo(rect.left, rect.top)
// ..relativeLineTo(sqrt2Width, 0)
// ..lineTo(rect.right, rect.bottom - sqrt2Width)
// ..lineTo(rect.right, rect.bottom)
// ..close(),
// );
final path = Path.combine(
PathOperation.union,
Path() // bottom
@@ -114,7 +157,7 @@ class RenderMaskedIcon extends RenderProxyBox {
canvas
..save()
..clipPath(path, doAntiAlias: false);
super.paint(context, offset);
super.paint(context, .zero);
canvas.restore();
@@ -137,8 +180,3 @@ class RenderMaskedIcon extends RenderProxyBox {
@override
bool get isRepaintBoundary => true;
}
extension DisabledIconExt on Icon {
DisabledIcon<Icon> disable([double? lineLengthScale]) =>
DisabledIcon(lineLengthScale: lineLengthScale, child: this);
}

View File

@@ -81,9 +81,7 @@ class _MouseInteractiveViewerState extends State<MouseInteractiveViewer>
final GlobalKey _parentKey = GlobalKey();
Animation<Offset>? _animation;
CurvedAnimation? _curvedAnimation;
Animation<double>? _scaleAnimation;
CurvedAnimation? _curvedScaleAnimation;
late Offset _scaleAnimationFocalPoint;
late AnimationController _controller;
late AnimationController _scaleController;
@@ -435,20 +433,15 @@ class _MouseInteractiveViewerState extends State<MouseInteractiveViewer>
details.velocity.pixelsPerSecond.distance,
widget.interactionEndFrictionCoefficient,
);
_animation =
Tween<Offset>(
begin: translation,
end: Offset(
frictionSimulationX.finalX,
frictionSimulationY.finalX,
),
).animate(
_curvedAnimation ??= CurvedAnimation(
parent: _controller,
curve: Curves.decelerate,
),
)
..addListener(_handleInertiaAnimation);
_animation = _controller.drive(
Tween<Offset>(
begin: translation,
end: Offset(
frictionSimulationX.finalX,
frictionSimulationY.finalX,
),
).chain(CurveTween(curve: Curves.decelerate)),
)..addListener(_handleInertiaAnimation);
_controller
..duration = Duration(milliseconds: (tFinal * 1000).round())
..forward();
@@ -468,17 +461,12 @@ class _MouseInteractiveViewerState extends State<MouseInteractiveViewer>
widget.interactionEndFrictionCoefficient,
effectivelyMotionless: 0.1,
);
_scaleAnimation =
Tween<double>(
begin: scale,
end: frictionSimulation.x(tFinal),
).animate(
_curvedScaleAnimation ??= CurvedAnimation(
parent: _scaleController,
curve: Curves.decelerate,
),
)
..addListener(_handleScaleAnimation);
_scaleAnimation = _scaleController.drive(
Tween<double>(
begin: scale,
end: frictionSimulation.x(tFinal),
).chain(CurveTween(curve: Curves.decelerate)),
)..addListener(_handleScaleAnimation);
_scaleController
..duration = Duration(milliseconds: (tFinal * 1000).round())
..forward();
@@ -732,9 +720,7 @@ class _MouseInteractiveViewerState extends State<MouseInteractiveViewer>
@override
void dispose() {
_scaleGestureRecognizer.dispose();
_curvedAnimation?.dispose();
_controller.dispose();
_curvedScaleAnimation?.dispose();
_scaleController.dispose();
_transformer.removeListener(_handleTransformation);
if (widget.transformationController == null) {

View File

@@ -31,18 +31,6 @@ class HeroDialogRoute<T> extends PageRoute<T> {
@override
Color? get barrierColor => null;
CurvedAnimation? _curvedAnimation;
void _setAnimation(Animation<double> animation) {
if (_curvedAnimation?.parent != animation) {
_curvedAnimation?.dispose();
_curvedAnimation = CurvedAnimation(
parent: animation,
curve: Curves.easeOut,
);
}
}
@override
Widget buildTransitions(
BuildContext context,
@@ -50,19 +38,12 @@ class HeroDialogRoute<T> extends PageRoute<T> {
Animation<double> secondaryAnimation,
Widget child,
) {
_setAnimation(animation);
return FadeTransition(
opacity: _curvedAnimation!,
opacity: animation.drive(CurveTween(curve: Curves.easeOut)),
child: child,
);
}
@override
void dispose() {
_curvedAnimation?.dispose();
super.dispose();
}
@override
Widget buildPage(
BuildContext context,

View File

@@ -503,9 +503,7 @@ class _InteractiveViewerState extends State<InteractiveViewer>
final GlobalKey _childKey = GlobalKey();
final GlobalKey _parentKey = GlobalKey();
Animation<Offset>? _animation;
CurvedAnimation? _curvedAnimation;
Animation<double>? _scaleAnimation;
CurvedAnimation? _curvedScaleAnimation;
late Offset _scaleAnimationFocalPoint;
late AnimationController _controller;
late AnimationController _scaleController;
@@ -924,19 +922,15 @@ class _InteractiveViewerState extends State<InteractiveViewer>
details.velocity.pixelsPerSecond.distance,
widget.interactionEndFrictionCoefficient,
);
_animation =
Tween<Offset>(
begin: translation,
end: Offset(
frictionSimulationX.finalX,
frictionSimulationY.finalX,
),
).animate(
_curvedAnimation ??= CurvedAnimation(
parent: _controller,
curve: Curves.decelerate,
),
);
_animation = _controller.drive(
Tween<Offset>(
begin: translation,
end: Offset(
frictionSimulationX.finalX,
frictionSimulationY.finalX,
),
).chain(CurveTween(curve: Curves.decelerate)),
);
_controller.duration = Duration(milliseconds: (tFinal * 1000).round());
_animation!.addListener(_handleInertiaAnimation);
_controller.forward();
@@ -956,16 +950,12 @@ class _InteractiveViewerState extends State<InteractiveViewer>
widget.interactionEndFrictionCoefficient,
effectivelyMotionless: 0.1,
);
_scaleAnimation =
Tween<double>(
begin: scale,
end: frictionSimulation.x(tFinal),
).animate(
_curvedScaleAnimation ??= CurvedAnimation(
parent: _scaleController,
curve: Curves.decelerate,
),
);
_scaleAnimation = _scaleController.drive(
Tween<double>(
begin: scale,
end: frictionSimulation.x(tFinal),
).chain(CurveTween(curve: Curves.decelerate)),
);
_scaleController.duration = Duration(
milliseconds: (tFinal * 1000).round(),
);
@@ -1155,9 +1145,7 @@ class _InteractiveViewerState extends State<InteractiveViewer>
@override
void dispose() {
_curvedAnimation?.dispose();
_controller.dispose();
_curvedScaleAnimation?.dispose();
_scaleController.dispose();
_transformer.removeListener(_handleTransformation);
if (widget.transformationController == null) {

View File

@@ -114,7 +114,7 @@ class InteractiveViewerBoundaryState extends State<InteractiveViewerBoundary>
_scaleAnimation = _animateController.drive(
Tween<double>(
begin: 1,
begin: 1.0,
end: 0.25,
),
);

View File

@@ -207,13 +207,12 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
if (_transformationController.value != Matrix4.identity()) {
// animate the reset for the transformation of the interactive viewer
_animation =
Matrix4Tween(
begin: _transformationController.value,
end: Matrix4.identity(),
).animate(
CurveTween(curve: Curves.easeOut).animate(_animationController),
);
_animation = _animationController.drive(
Matrix4Tween(
begin: _transformationController.value,
end: Matrix4.identity(),
).chain(CurveTween(curve: Curves.easeOut)),
);
_animationController.forward(from: 0);
}
@@ -401,13 +400,12 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
matrix.row3.w,
]);
_animation =
Matrix4Tween(
begin: _transformationController.value,
end: matrix,
).animate(
CurveTween(curve: Curves.easeOut).animate(_animationController),
);
_animation = _animationController.drive(
Matrix4Tween(
begin: _transformationController.value,
end: matrix,
).chain(CurveTween(curve: Curves.easeOut)),
);
_animationController
.forward(from: 0)
.whenComplete(() => _onScaleChanged(targetScale));

View File

@@ -1,5 +1,3 @@
import 'dart:math';
import 'package:PiliPlus/common/widgets/custom_arc.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
@@ -35,7 +33,7 @@ class LoadingWidget extends StatelessWidget {
size: 40,
color: onSurfaceVariant,
strokeWidth: 3,
sweepAngle: progress.value * 2 * pi,
progress: progress.value,
),
),
//msg

View File

@@ -10,14 +10,7 @@ import 'package:flutter/services.dart'
PointerEnterEventListener,
PointerExitEventListener;
/// The shape of the progress bar at the left and right ends.
enum BarCapShape {
/// The left and right ends of the bar are round.
round,
/// The left and right ends of the bar are square.
square,
}
/// https://github.com/suragch/audio_video_progress_bar
/// A progress bar widget to show or set the location of the currently
/// playing audio or video content.
@@ -36,7 +29,7 @@ class ProgressBar extends LeafRenderObjectWidget {
super.key,
required this.progress,
required this.total,
this.buffered,
this.buffered = .zero,
this.onSeek,
this.onDragStart,
this.onDragUpdate,
@@ -45,7 +38,6 @@ class ProgressBar extends LeafRenderObjectWidget {
required this.baseBarColor,
required this.progressBarColor,
required this.bufferedBarColor,
this.barCapShape = BarCapShape.round,
this.thumbRadius = 10.0,
required this.thumbColor,
required this.thumbGlowColor,
@@ -65,7 +57,7 @@ class ProgressBar extends LeafRenderObjectWidget {
///
/// This is useful for streamed content. If you are playing a local file
/// then you can leave this out.
final Duration? buffered;
final Duration buffered;
/// A callback when user moves the thumb.
///
@@ -142,12 +134,6 @@ class ProgressBar extends LeafRenderObjectWidget {
/// a shade darker than [baseBarColor].
final Color bufferedBarColor;
/// The shape of the bar at the left and right ends.
///
/// This affects the base bar for the total time, the current progress bar,
/// and the buffered progress bar. The default is [BarCapShape.round].
final BarCapShape barCapShape;
/// The radius of the circle for the moveable progress bar thumb.
final double thumbRadius;
@@ -189,7 +175,7 @@ class ProgressBar extends LeafRenderObjectWidget {
return RenderProgressBar(
progress: progress,
total: total,
buffered: buffered ?? Duration.zero,
buffered: buffered,
onSeek: onSeek,
onDragStart: onDragStart,
onDragUpdate: onDragUpdate,
@@ -198,7 +184,6 @@ class ProgressBar extends LeafRenderObjectWidget {
baseBarColor: baseBarColor,
progressBarColor: progressBarColor,
bufferedBarColor: bufferedBarColor,
barCapShape: barCapShape,
thumbRadius: thumbRadius,
thumbColor: thumbColor,
thumbGlowColor: thumbGlowColor,
@@ -215,7 +200,7 @@ class ProgressBar extends LeafRenderObjectWidget {
renderObject
..total = total
..progress = progress
..buffered = buffered ?? Duration.zero
..buffered = buffered
..onSeek = onSeek
..onDragStart = onDragStart
..onDragUpdate = onDragUpdate
@@ -224,7 +209,6 @@ class ProgressBar extends LeafRenderObjectWidget {
..baseBarColor = baseBarColor
..progressBarColor = progressBarColor
..bufferedBarColor = bufferedBarColor
..barCapShape = barCapShape
..thumbRadius = thumbRadius
..thumbColor = thumbColor
..thumbGlowColor = thumbGlowColor
@@ -271,7 +255,6 @@ class ProgressBar extends LeafRenderObjectWidget {
..add(ColorProperty('baseBarColor', baseBarColor))
..add(ColorProperty('progressBarColor', progressBarColor))
..add(ColorProperty('bufferedBarColor', bufferedBarColor))
..add(StringProperty('barCapShape', barCapShape.toString()))
..add(DoubleProperty('thumbRadius', thumbRadius))
..add(ColorProperty('thumbColor', thumbColor))
..add(ColorProperty('thumbGlowColor', thumbGlowColor))
@@ -348,7 +331,6 @@ class RenderProgressBar extends RenderBox implements MouseTrackerAnnotation {
required Color baseBarColor,
required Color progressBarColor,
required Color bufferedBarColor,
required BarCapShape barCapShape,
double thumbRadius = 20.0,
required Color thumbColor,
required Color thumbGlowColor,
@@ -364,7 +346,6 @@ class RenderProgressBar extends RenderBox implements MouseTrackerAnnotation {
_baseBarColor = baseBarColor,
_progressBarColor = progressBarColor,
_bufferedBarColor = bufferedBarColor,
_barCapShape = barCapShape,
_thumbRadius = thumbRadius,
_thumbColor = thumbColor,
_thumbGlowColor = thumbGlowColor,
@@ -598,14 +579,6 @@ class RenderProgressBar extends RenderBox implements MouseTrackerAnnotation {
markNeedsPaint();
}
BarCapShape get barCapShape => _barCapShape;
BarCapShape _barCapShape;
set barCapShape(BarCapShape value) {
if (_barCapShape == value) return;
_barCapShape = value;
markNeedsPaint();
}
/// The color of the moveable thumb.
Color get thumbColor => _thumbColor;
Color _thumbColor;
@@ -765,12 +738,9 @@ class RenderProgressBar extends RenderBox implements MouseTrackerAnnotation {
required double widthProportion,
required Color color,
}) {
final strokeCap = (_barCapShape == BarCapShape.round)
? StrokeCap.round
: StrokeCap.square;
final baseBarPaint = Paint()
..color = color
..strokeCap = strokeCap
..strokeCap = StrokeCap.round
..strokeWidth = _barHeight;
final capRadius = _barHeight / 2;
final adjustedWidth = availableSize.width - barHeight;

View File

@@ -862,6 +862,7 @@ class _AudioPageState extends State<AudioPage> {
src: cover,
width: 170,
height: 170,
cacheWidth: false,
),
),
),

View File

@@ -40,7 +40,6 @@ abstract class CommonDynPageState<T extends StatefulWidget> extends State<T>
final fabOffset = const Offset(0, 1);
late final AnimationController _fabAnimationCtr;
late final CurvedAnimation _curvedAnimation;
late final Animation<Offset> fabAnim;
@override
@@ -50,14 +49,12 @@ abstract class CommonDynPageState<T extends StatefulWidget> extends State<T>
vsync: this,
duration: const Duration(milliseconds: 200),
)..forward();
_curvedAnimation = CurvedAnimation(
parent: _fabAnimationCtr,
curve: Curves.easeInOut,
fabAnim = _fabAnimationCtr.drive(
Tween<Offset>(
begin: fabOffset,
end: Offset.zero,
).chain(CurveTween(curve: Curves.easeInOut)),
);
fabAnim = Tween<Offset>(
begin: fabOffset,
end: Offset.zero,
).animate(_curvedAnimation);
scrollController = ScrollController()..addListener(listener);
}
@@ -100,7 +97,6 @@ abstract class CommonDynPageState<T extends StatefulWidget> extends State<T>
scrollController
..removeListener(listener)
..dispose();
_curvedAnimation.dispose();
_fabAnimationCtr.dispose();
super.dispose();
}

View File

@@ -61,7 +61,7 @@ class PublishRoute<T> extends PopupRoute<T> {
}
return SlideTransition(
position: animation.drive(
Tween(
Tween<Offset>(
begin: const Offset(0.0, 1.0),
end: Offset.zero,
).chain(CurveTween(curve: Curves.linear)),

View File

@@ -956,7 +956,7 @@ class _BorderIndicator extends LeafRenderObjectWidget {
@override
RenderObject createRenderObject(BuildContext context) {
return RenderBorderIndicator(
return _RenderBorderIndicator(
radius: radius,
isLeft: isLeft,
);
@@ -965,7 +965,7 @@ class _BorderIndicator extends LeafRenderObjectWidget {
@override
void updateRenderObject(
BuildContext context,
RenderBorderIndicator renderObject,
_RenderBorderIndicator renderObject,
) {
renderObject
..radius = radius
@@ -973,8 +973,8 @@ class _BorderIndicator extends LeafRenderObjectWidget {
}
}
class RenderBorderIndicator extends RenderBox {
RenderBorderIndicator({
class _RenderBorderIndicator extends RenderBox {
_RenderBorderIndicator({
required Radius radius,
required bool isLeft,
}) : _radius = radius,

View File

@@ -17,7 +17,6 @@ class MainReplyController extends ReplyController<MainListReply>
bool _showFab = true;
late final AnimationController _fabAnimationCtr;
late final CurvedAnimation _curvedAnimation;
late final Animation<Offset> fabAnim;
@override
@@ -27,15 +26,12 @@ class MainReplyController extends ReplyController<MainListReply>
vsync: this,
duration: const Duration(milliseconds: 300),
)..forward();
_curvedAnimation = CurvedAnimation(
parent: _fabAnimationCtr,
curve: Curves.easeInOut,
fabAnim = _fabAnimationCtr.drive(
Tween<Offset>(
begin: const Offset(0.0, 2.0),
end: Offset.zero,
).chain(CurveTween(curve: Curves.easeInOut)),
);
fabAnim = Tween<Offset>(
begin: const Offset(0, 2),
end: Offset.zero,
).animate(_curvedAnimation);
final args = Get.arguments;
oid = args['oid'];
replyType = args['replyType'];
@@ -71,7 +67,6 @@ class MainReplyController extends ReplyController<MainListReply>
@override
void onClose() {
_curvedAnimation.dispose();
_fabAnimationCtr.dispose();
super.onClose();
}

View File

@@ -53,12 +53,7 @@ class SavePanel extends StatefulWidget {
transitionDuration: const Duration(milliseconds: 255),
transitionBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(
opacity: animation.drive(
Tween<double>(
begin: 0,
end: 1,
).chain(CurveTween(curve: Curves.easeInOut)),
),
opacity: animation.drive(CurveTween(curve: Curves.easeInOut)),
child: child,
);
},

View File

@@ -312,9 +312,14 @@ class _SearchPageState extends State<SearchPage> {
child: IconButton(
iconSize: 22,
tooltip: enable ? '记录搜索' : '无痕搜索',
icon: enable
? historyIcon(theme)
: historyIcon(theme).disable(),
icon: DisabledIcon(
disable: !enable,
child: Icon(
Icons.history,
color: theme.colorScheme.onSurfaceVariant
.withValues(alpha: 0.8),
),
),
style: IconButton.styleFrom(
padding: EdgeInsets.zero,
),
@@ -406,11 +411,6 @@ class _SearchPageState extends State<SearchPage> {
),
);
Icon historyIcon(ThemeData theme) => Icon(
Icons.history,
color: theme.colorScheme.onSurfaceVariant.withValues(alpha: 0.8),
);
Widget _buildHotKey(
LoadingState<SearchRcmdData> loadingState,
bool isTrending,

View File

@@ -941,10 +941,12 @@ class VideoDetailController extends GetxController
return Align(
alignment: Alignment.centerLeft,
child: SlideTransition(
position: Tween<Offset>(
begin: const Offset(-1, 0),
end: Offset.zero,
).animate(animation),
position: animation.drive(
Tween<Offset>(
begin: const Offset(-1.0, 0.0),
end: Offset.zero,
),
),
child: Padding(
padding: const EdgeInsets.only(top: 5),
child: GestureDetector(

View File

@@ -56,7 +56,7 @@ class ActionItem extends StatelessWidget {
builder: (context, child) => Arc(
size: 28,
color: primary,
sweepAngle: animation!.value,
progress: -animation!.value,
),
),
child,

View File

@@ -1,5 +1,4 @@
import 'dart:async';
import 'dart:math' show pi;
import 'package:PiliPlus/utils/platform_utils.dart';
import 'package:flutter/material.dart';
@@ -24,7 +23,6 @@ mixin TripleMixin on GetxController, TickerProvider {
// no need for pugv
AnimationController? _tripleAnimCtr;
CurvedAnimation? _curvedAnimation;
Animation<double>? _tripleAnimation;
AnimationController get tripleAnimCtr =>
@@ -34,15 +32,8 @@ mixin TripleMixin on GetxController, TickerProvider {
reverseDuration: const Duration(milliseconds: 400),
);
CurvedAnimation get curvedAnimation => _curvedAnimation ??= CurvedAnimation(
parent: tripleAnimCtr,
curve: Curves.easeInOut,
);
Animation<double> get tripleAnimation => _tripleAnimation ??= Tween<double>(
begin: 0,
end: -2 * pi,
).animate(curvedAnimation);
Animation<double> get tripleAnimation => _tripleAnimation ??= tripleAnimCtr
.drive(CurveTween(curve: Curves.easeInOut));
Timer? _timer;
@@ -84,7 +75,6 @@ mixin TripleMixin on GetxController, TickerProvider {
@override
void onClose() {
_cancelTimer();
_curvedAnimation?.dispose();
_tripleAnimCtr?.dispose();
super.onClose();
}

View File

@@ -46,12 +46,7 @@ class PayCoinsPage extends StatefulWidget {
transitionDuration: const Duration(milliseconds: 225),
transitionBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(
opacity: animation.drive(
Tween<double>(
begin: 0.0,
end: 1.0,
).chain(CurveTween(curve: Curves.linear)),
),
opacity: animation,
child: child,
);
},
@@ -73,9 +68,8 @@ class _PayCoinsPageState extends State<PayCoinsPage>
late final Animation<Offset> _slide22Anim;
late final AnimationController _scale22Controller;
late final Animation<double> _scale22Anim;
late final AnimationController _coinSlideController;
late final AnimationController _coinController;
late final Animation<Offset> _coinSlideAnim;
late final AnimationController _coinFadeController;
late final Animation<double> _coinFadeAnim;
late final AnimationController _boxAnimController;
late final Animation<Offset> _boxAnim;
@@ -155,7 +149,7 @@ class _PayCoinsPageState extends State<PayCoinsPage>
duration: const Duration(milliseconds: 50),
);
_slide22Anim = _slide22Controller.drive(
Tween(
Tween<Offset>(
begin: Offset.zero,
end: const Offset(0.0, -0.2),
),
@@ -165,32 +159,30 @@ class _PayCoinsPageState extends State<PayCoinsPage>
duration: const Duration(milliseconds: 50),
);
_scale22Anim = _scale22Controller.drive(
Tween(begin: 1, end: 1.1),
Tween<double>(begin: 1.0, end: 1.1),
);
_coinSlideController = AnimationController(
_coinController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 200),
duration: const Duration(milliseconds: 300),
);
_coinSlideAnim = _coinSlideController.drive(
Tween(
_coinSlideAnim = _coinController.drive(
Tween<Offset>(
begin: Offset.zero,
end: const Offset(0.0, -2),
),
end: const Offset(0.0, -2.0),
).chain(CurveTween(curve: const Interval(0.0, 2 / 3))),
);
_coinFadeController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 100),
_coinFadeAnim = _coinController.drive(
Tween<double>(
begin: 1.0,
end: 0.0,
).chain(CurveTween(curve: const Interval(2 / 3, 1.0))),
);
_coinFadeAnim = Tween<double>(
begin: 1,
end: 0,
).animate(_coinFadeController);
_boxAnimController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 50),
);
_boxAnim = _boxAnimController.drive(
Tween(
Tween<Offset>(
begin: Offset.zero,
end: const Offset(0.0, -0.2),
),
@@ -204,8 +196,7 @@ class _PayCoinsPageState extends State<PayCoinsPage>
_cancelTimer();
_slide22Controller.dispose();
_scale22Controller.dispose();
_coinSlideController.dispose();
_coinFadeController.dispose();
_coinController.dispose();
_boxAnimController.dispose();
_controller?.dispose();
super.dispose();
@@ -570,11 +561,9 @@ class _PayCoinsPageState extends State<PayCoinsPage>
});
}
_boxAnimController.forward().whenComplete(_boxAnimController.reverse);
_coinSlideController.forward().whenComplete(() {
_coinFadeController.forward().whenComplete(() {
Get.back();
widget.onPayCoin(_pageIndex.value + 1, _coinWithLike.value);
});
_coinController.forward().whenComplete(() {
Get.back();
widget.onPayCoin(_pageIndex.value + 1, _coinWithLike.value);
});
});
});

View File

@@ -28,7 +28,6 @@ class VideoReplyController extends ReplyController<MainListReply>
bool _isFabVisible = true;
late final AnimationController _fabAnimationCtr;
late final CurvedAnimation _curvedAnimation;
late final Animation<Offset> animation;
@override
@@ -38,14 +37,12 @@ class VideoReplyController extends ReplyController<MainListReply>
vsync: this,
duration: const Duration(milliseconds: 100),
)..forward();
_curvedAnimation = CurvedAnimation(
parent: _fabAnimationCtr,
curve: Curves.easeInOut,
animation = _fabAnimationCtr.drive(
Tween<Offset>(
begin: const Offset(0.0, 2.0),
end: Offset.zero,
).chain(CurveTween(curve: Curves.easeInOut)),
);
animation = Tween<Offset>(
begin: const Offset(0, 2),
end: Offset.zero,
).animate(_curvedAnimation);
}
void showFab() {
@@ -78,7 +75,6 @@ class VideoReplyController extends ReplyController<MainListReply>
@override
void onClose() {
_curvedAnimation.dispose();
_fabAnimationCtr.dispose();
super.onClose();
}

View File

@@ -94,7 +94,6 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel>
with SingleTickerProviderStateMixin, CommonSlideMixin {
late VideoReplyReplyController _controller;
late final _tag = Utils.makeHeroTag('${widget.rpid}${widget.dialog}');
CurvedAnimation? _curvedAnimation;
Animation<Color?>? _colorAnimation;
late final bool isDialogue = widget.dialog != null;
@@ -129,7 +128,6 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel>
@override
void dispose() {
_curvedAnimation?.dispose();
Get.delete<VideoReplyReplyController>(tag: _tag);
super.dispose();
}
@@ -334,16 +332,16 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel>
final child = _replyItem(context, response[index], index);
if (jumpIndex == index) {
return AnimatedBuilder(
animation: _colorAnimation ??=
ColorTween(
begin: theme.colorScheme.onInverseSurface,
end: theme.colorScheme.surface,
).animate(
_curvedAnimation ??= CurvedAnimation(
parent: _controller.animController,
curve: const Interval(0.8, 1.0), // 前0.8s不变, 后0.2s开始动画
),
animation: _colorAnimation ??= _controller.animController.drive(
ColorTween(
begin: theme.colorScheme.onInverseSurface,
end: theme.colorScheme.surface,
).chain(
CurveTween(
curve: const Interval(0.8, 1.0), // 前0.8s不变, 后0.2s开始动画
),
),
),
child: child,
builder: (context, child) {
return ColoredBox(

View File

@@ -6,6 +6,7 @@ import 'dart:ui' as ui;
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/widgets/cropped_image.dart';
import 'package:PiliPlus/common/widgets/custom_icon.dart';
import 'package:PiliPlus/common/widgets/disabled_icon.dart';
import 'package:PiliPlus/common/widgets/gesture/immediate_tap_gesture_recognizer.dart';
import 'package:PiliPlus/common/widgets/gesture/mouse_interactive_viewer.dart';
import 'package:PiliPlus/common/widgets/loading_widget.dart';
@@ -414,34 +415,20 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
() {
final list = videoDetailController.dmTrend.value?.dataOrNull;
if (list != null && list.isNotEmpty) {
final show = videoDetailController.showDmTrendChart.value;
return ComBtn(
width: widgetWidth,
height: 30,
tooltip: '高能进度条',
icon: videoDetailController.showDmTrendChart.value
? const Icon(
Icons.show_chart,
size: 22,
color: Colors.white,
)
: const Stack(
clipBehavior: Clip.none,
alignment: Alignment.center,
children: [
Icon(
Icons.show_chart,
size: 22,
color: Colors.white,
),
Icon(
Icons.hide_source,
size: 22,
color: Colors.white,
),
],
),
onTap: () => videoDetailController.showDmTrendChart.value =
!videoDetailController.showDmTrendChart.value,
icon: DisabledIcon(
disable: !show,
child: const Icon(
Icons.show_chart,
size: 22,
color: Colors.white,
),
),
onTap: () => videoDetailController.showDmTrendChart.value = !show,
);
}
return const SizedBox.shrink();
@@ -450,64 +437,76 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
/// 超分辨率
BottomControlType.superResolution => Obx(
() => PopupMenuButton<SuperResolutionType>(
tooltip: '超分辨率',
requestFocus: false,
initialValue: plPlayerController.superResolutionType.value,
color: Colors.black.withValues(alpha: 0.8),
itemBuilder: (context) {
return SuperResolutionType.values
.map(
(type) => PopupMenuItem<SuperResolutionType>(
height: 35,
padding: const EdgeInsets.only(left: 30),
value: type,
onTap: () => plPlayerController.setShader(type),
child: Text(
type.title,
style: const TextStyle(color: Colors.white, fontSize: 13),
() {
final type = plPlayerController.superResolutionType.value;
return PopupMenuButton<SuperResolutionType>(
tooltip: '超分辨率',
requestFocus: false,
initialValue: type,
color: Colors.black.withValues(alpha: 0.8),
itemBuilder: (context) {
return SuperResolutionType.values
.map(
(type) => PopupMenuItem<SuperResolutionType>(
height: 35,
padding: const EdgeInsets.only(left: 30),
value: type,
onTap: () => plPlayerController.setShader(type),
child: Text(
type.title,
style: const TextStyle(
color: Colors.white,
fontSize: 13,
),
),
),
),
)
.toList();
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text(
plPlayerController.superResolutionType.value.title,
style: const TextStyle(color: Colors.white, fontSize: 13),
)
.toList();
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text(
type.title,
style: const TextStyle(color: Colors.white, fontSize: 13),
),
),
),
),
);
},
),
/// 分段信息
BottomControlType.viewPoints => Obx(
() => videoDetailController.viewPointList.isEmpty
? const SizedBox.shrink()
: ComBtn(
width: widgetWidth,
height: 30,
tooltip: '分段信息',
icon: Transform.rotate(
() {
if (videoDetailController.viewPointList.isNotEmpty) {
final show = videoDetailController.showVP.value;
return ComBtn(
width: widgetWidth,
height: 30,
tooltip: '分段信息',
icon: DisabledIcon(
iconSize: 22,
disable: !show,
child: Transform.rotate(
angle: math.pi / 2,
child: const Icon(
MdiIcons.viewHeadline,
Icons.reorder,
size: 22,
color: Colors.white,
),
),
onTap: widget.showViewPoints,
onLongPress: () {
Feedback.forLongPress(context);
videoDetailController.showVP.value =
!videoDetailController.showVP.value;
},
onSecondaryTap: PlatformUtils.isMobile
? null
: () => videoDetailController.showVP.value =
!videoDetailController.showVP.value,
),
onTap: widget.showViewPoints,
onLongPress: () {
Feedback.forLongPress(context);
videoDetailController.showVP.value = !show;
},
onSecondaryTap: PlatformUtils.isMobile
? null
: () => videoDetailController.showVP.value = !show,
);
}
return const SizedBox.shrink();
},
),
/// 选集
@@ -567,35 +566,41 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
/// 画面比例
BottomControlType.fit => Obx(
() => PopupMenuButton<VideoFitType>(
tooltip: '画面比例',
requestFocus: false,
initialValue: plPlayerController.videoFit.value,
color: Colors.black.withValues(alpha: 0.8),
itemBuilder: (context) {
return VideoFitType.values
.map(
(boxFit) => PopupMenuItem<VideoFitType>(
height: 35,
padding: const EdgeInsets.only(left: 30),
value: boxFit,
onTap: () => plPlayerController.toggleVideoFit(boxFit),
child: Text(
boxFit.desc,
style: const TextStyle(color: Colors.white, fontSize: 13),
() {
final fit = plPlayerController.videoFit.value;
return PopupMenuButton<VideoFitType>(
tooltip: '画面比例',
requestFocus: false,
initialValue: fit,
color: Colors.black.withValues(alpha: 0.8),
itemBuilder: (context) {
return VideoFitType.values
.map(
(boxFit) => PopupMenuItem<VideoFitType>(
height: 35,
padding: const EdgeInsets.only(left: 30),
value: boxFit,
onTap: () => plPlayerController.toggleVideoFit(boxFit),
child: Text(
boxFit.desc,
style: const TextStyle(
color: Colors.white,
fontSize: 13,
),
),
),
),
)
.toList();
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text(
plPlayerController.videoFit.value.desc,
style: const TextStyle(color: Colors.white, fontSize: 13),
)
.toList();
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text(
fit.desc,
style: const TextStyle(color: Colors.white, fontSize: 13),
),
),
),
),
);
},
),
BottomControlType.aiTranslate => Obx(
@@ -654,66 +659,65 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
/// 字幕
BottomControlType.subtitle => Obx(
() => videoDetailController.subtitles.isEmpty
? const SizedBox.shrink()
: PopupMenuButton<int>(
tooltip: '字幕',
requestFocus: false,
initialValue: videoDetailController.vttSubtitlesIndex.value
.clamp(
0,
videoDetailController.subtitles.length,
() {
if (videoDetailController.subtitles.isNotEmpty) {
final val = videoDetailController.vttSubtitlesIndex.value;
return PopupMenuButton<int>(
tooltip: '字幕',
requestFocus: false,
initialValue: val,
color: Colors.black.withValues(alpha: 0.8),
itemBuilder: (context) {
return [
PopupMenuItem<int>(
value: 0,
height: 35,
onTap: () => videoDetailController.setSubtitle(0),
child: const Text(
"关闭字幕",
style: TextStyle(
color: Colors.white,
fontSize: 13,
),
),
color: Colors.black.withValues(alpha: 0.8),
itemBuilder: (context) {
return [
PopupMenuItem<int>(
value: 0,
),
...videoDetailController.subtitles.indexed.map((e) {
return PopupMenuItem<int>(
value: e.$1 + 1,
height: 35,
onTap: () => videoDetailController.setSubtitle(0),
child: const Text(
"关闭字幕",
style: TextStyle(
onTap: () => videoDetailController.setSubtitle(e.$1 + 1),
child: Text(
"${e.$2.lanDoc}",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: Colors.white,
fontSize: 13,
),
),
),
...videoDetailController.subtitles.indexed.map((e) {
return PopupMenuItem<int>(
value: e.$1 + 1,
height: 35,
onTap: () =>
videoDetailController.setSubtitle(e.$1 + 1),
child: Text(
"${e.$2.lanDoc}",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: Colors.white,
fontSize: 13,
),
),
);
}),
];
},
child: SizedBox(
width: widgetWidth,
height: 30,
child: videoDetailController.vttSubtitlesIndex.value == 0
? const Icon(
Icons.closed_caption_off_outlined,
size: 22,
color: Colors.white,
)
: const Icon(
Icons.closed_caption_off_rounded,
size: 22,
color: Colors.white,
),
),
);
}),
];
},
child: SizedBox(
width: widgetWidth,
height: 30,
child: val == 0
? const Icon(
Icons.closed_caption_off_outlined,
size: 22,
color: Colors.white,
)
: const Icon(
Icons.closed_caption_off_rounded,
size: 22,
color: Colors.white,
),
),
);
}
return const SizedBox.shrink();
},
),
/// 播放速度
@@ -1701,15 +1705,12 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
vsync: this,
duration: const Duration(milliseconds: 255),
);
final anim =
Matrix4Tween(
begin: transformationController.value,
end: Matrix4.identity(),
).animate(
CurveTween(
curve: Curves.easeOut,
).animate(animController),
);
final anim = animController.drive(
Matrix4Tween(
begin: transformationController.value,
end: Matrix4.identity(),
).chain(CurveTween(curve: Curves.easeOut)),
);
void listener() {
transformationController.value = anim.value;
}
@@ -2748,17 +2749,20 @@ class _DanmakuTip extends SingleChildRenderObjectWidget {
@override
RenderObject createRenderObject(BuildContext context) {
return RenderDanmakuTip(offset: offset);
return _RenderDanmakuTip(offset: offset);
}
@override
void updateRenderObject(BuildContext context, RenderDanmakuTip renderObject) {
void updateRenderObject(
BuildContext context,
_RenderDanmakuTip renderObject,
) {
renderObject.offset = offset;
}
}
class RenderDanmakuTip extends RenderProxyBox {
RenderDanmakuTip({
class _RenderDanmakuTip extends RenderProxyBox {
_RenderDanmakuTip({
required double offset,
}) : _offset = offset;

View File

@@ -16,7 +16,7 @@ class AppBarAni extends StatelessWidget {
final bool isFullScreen;
static final _topPos = Tween<Offset>(
begin: const Offset(0, -1),
begin: const Offset(0.0, -1.0),
end: Offset.zero,
);
@@ -49,8 +49,8 @@ class AppBarAni extends StatelessWidget {
Widget build(BuildContext context) {
return SlideTransition(
position: isTop
? _topPos.animate(controller)
: _bottomPos.animate(controller),
? controller.drive(_topPos)
: controller.drive(_bottomPos),
child: DecoratedBox(
decoration: BoxDecoration(
gradient: isTop ? _topDecoration : _bottomDecoration,

View File

@@ -699,7 +699,7 @@ abstract final class PageUtils {
: const Offset(1.0, 0.0);
return SlideTransition(
position: animation.drive(
Tween(
Tween<Offset>(
begin: begin,
end: Offset.zero,
).chain(CurveTween(curve: Curves.easeInOut)),

View File

@@ -528,8 +528,8 @@ abstract final class RequestUtils {
token: captchaData.token,
validate: captchaData.validate,
);
if (res case Success(:final response)) {
if (response != null && response['is_valid'] == 1) {
if (res case Success(:final response?)) {
if (response['is_valid'] == 1) {
final griskId = response['grisk_id'];
if (griskId is String) {
onSuccess(griskId);