Free copy-paste Loader 005 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_005.dart) and use the widget anywhere in your widget tree.
import 'package:flutter/material.dart';
class Loader005 extends StatefulWidget {
const Loader005({
super.key,
this.width = 240.0,
this.height = 130.0,
this.cardColor = const Color(0xFFE3E3E3),
this.borderColor = const Color(0xFFD3D3D3),
this.blockColor = const Color(0xFFCACACA),
this.duration = const Duration(milliseconds: 1000),
});
final double width;
final double height;
final Color cardColor;
final Color borderColor;
final Color blockColor;
final Duration duration;
@override
State<Loader005> createState() => _Loader005State();
}
class _Loader005State extends State<Loader005>
with SingleTickerProviderStateMixin {
late final AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: widget.duration)
..repeat();
}
@override
void didUpdateWidget(covariant Loader005 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 Container(
width: widget.width,
height: widget.height,
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
color: widget.cardColor,
border: Border.all(color: widget.borderColor),
borderRadius: BorderRadius.circular(4),
),
padding: const EdgeInsets.all(15),
child: Stack(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: widget.blockColor,
shape: BoxShape.circle,
),
),
const SizedBox(width: 8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 10,
width: 100,
decoration: BoxDecoration(
color: widget.blockColor,
borderRadius: BorderRadius.circular(2),
),
),
const SizedBox(height: 12),
Container(
height: 10,
width: double.infinity,
decoration: BoxDecoration(
color: widget.blockColor,
borderRadius: BorderRadius.circular(2),
),
),
],
),
),
],
),
const SizedBox(height: 13),
Container(
height: 10,
width: double.infinity,
decoration: BoxDecoration(
color: widget.blockColor,
borderRadius: BorderRadius.circular(2),
),
),
const SizedBox(height: 13),
FractionallySizedBox(
widthFactor: 0.92,
child: Container(
height: 10,
decoration: BoxDecoration(
color: widget.blockColor,
borderRadius: BorderRadius.circular(2),
),
),
),
],
),
Positioned.fill(
child: AnimatedBuilder(
animation: _controller,
builder: (context, _) {
return CustomPaint(
painter: _ShimmerPainter(progress: _controller.value),
);
},
),
),
],
),
);
}
}
class _ShimmerPainter extends CustomPainter {
const _ShimmerPainter({this.progress = 0.0});
final double progress;
@override
void paint(Canvas canvas, Size size) {
final double sweepWidth = size.width * 2.0;
final double dx = -size.width + progress * sweepWidth;
final rect = Rect.fromLTWH(0, 0, size.width, size.height);
final paint = Paint()
..shader = LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: const [
Colors.transparent,
Color(0x33FFFFFF),
Color(0x66FFFFFF),
Color(0x33FFFFFF),
Colors.transparent,
],
stops: const [0.0, 0.40, 0.50, 0.60, 1.0],
transform: _SlideGradientTransform(dx / size.width),
).createShader(rect);
canvas.drawRect(rect, paint);
}
@override
bool shouldRepaint(_ShimmerPainter oldDelegate) =>
oldDelegate.progress != progress;
}
class _SlideGradientTransform extends GradientTransform {
const _SlideGradientTransform(this.slidePercent);
final double slidePercent;
@override
Matrix4? transform(Rect bounds, {TextDirection? textDirection}) {
return Matrix4.translationValues(bounds.width * slidePercent, 0.0, 0.0);
}
}