Free copy-paste Loader 002 for Flutter: a ready-to-use loader widget with full Dart source code and a live preview. Works on mobile & desktop. No external packages.
Copy the Dart code below into a new file in your Flutter project (for example lib/widgets/loader_002.dart) and use the widget anywhere in your widget tree.
import 'dart:math' as math;
import 'package:flutter/material.dart';
class Loader002 extends StatefulWidget {
const Loader002({
super.key,
this.size = 36.0,
this.color = const Color(0xFF69717D),
this.bladeWidth = 2.8,
this.bladeLength = 9.0,
this.duration = const Duration(milliseconds: 1000),
});
final double size;
final Color color;
final double bladeWidth;
final double bladeLength;
final Duration duration;
@override
State<Loader002> createState() => _Loader002State();
}
class _Loader002State extends State<Loader002>
with SingleTickerProviderStateMixin {
late final AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: widget.duration)
..repeat();
}
@override
void didUpdateWidget(covariant Loader002 oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.duration != widget.duration) {
_controller.duration = widget.duration;
}
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SizedBox(
width: widget.size,
height: widget.size,
child: AnimatedBuilder(
animation: _controller,
builder: (context, _) {
return CustomPaint(
painter: _BladePainter(
progress: _controller.value,
color: widget.color,
bladeWidth: widget.bladeWidth,
bladeLength: widget.bladeLength,
),
);
},
),
);
}
}
class _BladePainter extends CustomPainter {
const _BladePainter({
this.progress = 0.0,
this.color = const Color(0xFF69717D),
this.bladeWidth = 2.8,
this.bladeLength = 9.0,
});
final double progress;
final Color color;
final double bladeWidth;
final double bladeLength;
@override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final radius = size.width / 2;
for (int i = 0; i < 12; i++) {
final angle = i * (2 * math.pi / 12);
final delay = i / 12.0;
final t = (progress - delay) % 1.0;
final adjustedT = t < 0 ? t + 1.0 : t;
final alpha = (1.0 - adjustedT).clamp(0.0, 1.0);
final paint = Paint()
..color = color.withValues(alpha: alpha)
..strokeWidth = bladeWidth
..strokeCap = StrokeCap.round;
final startDist = radius - bladeLength;
final p1 = Offset(
center.dx + startDist * math.cos(angle),
center.dy + startDist * math.sin(angle),
);
final p2 = Offset(
center.dx + radius * math.cos(angle),
center.dy + radius * math.sin(angle),
);
canvas.drawLine(p1, p2, paint);
}
}
@override
bool shouldRepaint(_BladePainter oldDelegate) =>
oldDelegate.progress != progress;
}