Files
sm-emby-share/lib/shared/widgets/app_loading_ring.dart
T

115 lines
2.9 KiB
Dart
Raw Normal View History

2026-07-14 11:11:36 +08:00
import 'dart:math' as math;
import 'package:flutter/material.dart';
class AppLoadingRing extends StatefulWidget {
const AppLoadingRing({super.key, this.size = 44, this.color});
final double size;
final Color? color;
@override
State<AppLoadingRing> createState() => _AppLoadingRingState();
}
class _AppLoadingRingState extends State<AppLoadingRing>
with SingleTickerProviderStateMixin {
late final AnimationController _rotationController;
@override
void initState() {
super.initState();
_rotationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 1100),
)..repeat();
}
@override
void dispose() {
_rotationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final ringColor =
widget.color ??
IconTheme.of(context).color ??
Theme.of(context).colorScheme.onSurface;
return TweenAnimationBuilder<double>(
tween: Tween<double>(begin: 0, end: 1),
duration: const Duration(milliseconds: 200),
curve: Curves.easeOut,
builder: (context, fadeInOpacity, child) =>
Opacity(opacity: fadeInOpacity, child: child),
child: RepaintBoundary(
child: SizedBox(
width: widget.size,
height: widget.size,
child: AnimatedBuilder(
animation: _rotationController,
builder: (context, _) => CustomPaint(
painter: _LoadingRingPainter(
rotationTurns: _rotationController.value,
color: ringColor,
),
),
),
),
),
);
}
}
class _LoadingRingPainter extends CustomPainter {
_LoadingRingPainter({required this.rotationTurns, required this.color});
final double rotationTurns;
final Color color;
static const double strokeWidth = 3;
static const double _sweepAngle = math.pi * 5 / 3;
@override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final radius = (math.min(size.width, size.height) - strokeWidth) / 2;
final arcRect = Rect.fromCircle(center: center, radius: radius);
final rotationAngle = rotationTurns * 2 * math.pi;
final gradient = SweepGradient(
startAngle: 0,
endAngle: 2 * math.pi,
transform: GradientRotation(rotationAngle),
colors: [
color.withValues(alpha: 0.0),
color.withValues(alpha: 0.25),
color,
],
stops: const [0.0, 0.5, _sweepAngle / (2 * math.pi)],
);
final arcPaint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = strokeWidth
..strokeCap = StrokeCap.round
..shader = gradient.createShader(arcRect);
canvas.drawArc(arcRect, rotationAngle, _sweepAngle, false, arcPaint);
}
@override
bool shouldRepaint(_LoadingRingPainter oldDelegate) =>
oldDelegate.rotationTurns != rotationTurns || oldDelegate.color != color;
}