Initial commit
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user