Free copy-paste Loader 004 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_004.dart) and use the widget anywhere in your widget tree.
import 'dart:math' as math;
import 'package:flutter/material.dart';
class Loader004 extends StatefulWidget {
const Loader004({
super.key,
this.size = 48.0,
this.dotSize = 18.0,
this.color = const Color(0xFF333333),
this.duration = const Duration(milliseconds: 1000),
});
final double size;
final double dotSize;
final Color color;
final Duration duration;
@override
State<Loader004> createState() => _Loader004State();
}
class _Loader004State extends State<Loader004>
with SingleTickerProviderStateMixin {
late final AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: widget.duration)
..repeat();
}
@override
void didUpdateWidget(covariant Loader004 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) {
final s = widget.size;
final d = widget.dotSize;
return AnimatedBuilder(
animation: _controller,
builder: (context, _) {
final t = _controller.value;
final angle = t * 2 * math.pi;
final scale = 1.0 + 0.3 * math.sin(t * 2 * math.pi).abs();
return Transform.scale(
scale: scale,
child: Transform.rotate(
angle: angle,
child: SizedBox(
width: s,
height: s,
child: Stack(
children: [
Positioned(
top: 0,
left: 0,
child: _Dot(size: d, color: widget.color),
),
Positioned(
top: 0,
right: 0,
child: _Dot(size: d, color: widget.color),
),
Positioned(
bottom: 0,
left: 0,
child: _Dot(size: d, color: widget.color),
),
Positioned(
bottom: 0,
right: 0,
child: _Dot(size: d, color: widget.color),
),
],
),
),
),
);
},
);
}
}
class _Dot extends StatelessWidget {
const _Dot({this.size = 18.0, this.color = const Color(0xFF333333)});
final double size;
final Color color;
@override
Widget build(BuildContext context) {
return Container(
width: size,
height: size,
decoration: BoxDecoration(color: color, shape: BoxShape.circle),
);
}
}