Loader 003 — Flutter Loader Widget

Free copy-paste Loader 003 for Flutter: a ready-to-use loader widget with full Dart source code and a live preview. Works on mobile & desktop. No external packages.

  • Category: Loadings
  • Platforms: mobile, desktop
  • Flutter SDK only — no external packages
  • Free to copy & use

How to use Loader 003 in Flutter

Copy the Dart code below into a new file in your Flutter project (for example lib/widgets/loader_003.dart) and use the widget anywhere in your widget tree.

Loader 003 – Dart source code

import 'package:flutter/material.dart';

class Loader003 extends StatefulWidget {
  const Loader003({
    super.key,
    this.width = 64.0,
    this.height = 48.0,
    this.color = const Color(0xFFFF4D4F),
    this.trackColor = const Color(0x33FF4D50),
    this.strokeWidth = 3.0,
    this.duration = const Duration(milliseconds: 1000),
  });

  final double width;
  final double height;
  final Color color;
  final Color trackColor;
  final double strokeWidth;
  final Duration duration;

  @override
  State<Loader003> createState() => _Loader003State();
}

class _Loader003State extends State<Loader003>
    with SingleTickerProviderStateMixin {
  late final AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this, duration: widget.duration)
      ..repeat();
  }

  @override
  void didUpdateWidget(covariant Loader003 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.width,
      height: widget.height,
      child: AnimatedBuilder(
        animation: _controller,
        builder: (context, _) {
          return CustomPaint(
            painter: _PulsePainter(
              progress: _controller.value,
              color: widget.color,
              trackColor: widget.trackColor,
              strokeWidth: widget.strokeWidth,
            ),
          );
        },
      ),
    );
  }
}

class _PulsePainter extends CustomPainter {
  const _PulsePainter({
    this.progress = 0.0,
    this.color = const Color(0xFFFF4D4F),
    this.trackColor = const Color(0x33FF4D50),
    this.strokeWidth = 3.0,
  });

  final double progress;
  final Color color;
  final Color trackColor;
  final double strokeWidth;

  @override
  void paint(Canvas canvas, Size size) {
    final sx = size.width / 64.0;
    final sy = size.height / 48.0;

    final path = Path()
      ..moveTo(0.157 * sx, 23.954 * sy)
      ..lineTo(14.0 * sx, 23.954 * sy)
      ..lineTo(21.843 * sx, 48.0 * sy)
      ..lineTo(43.0 * sx, 0.0 * sy)
      ..lineTo(50.0 * sx, 24.0 * sy)
      ..lineTo(64.0 * sx, 24.0 * sy);

    final trackPaint = Paint()
      ..color = trackColor
      ..style = PaintingStyle.stroke
      ..strokeWidth = strokeWidth
      ..strokeCap = StrokeCap.round
      ..strokeJoin = StrokeJoin.round;

    canvas.drawPath(path, trackPaint);

    final metrics = path.computeMetrics().toList();
    if (metrics.isEmpty) return;

    final metric = metrics.first;
    final totalLen = metric.length;
    final beamLen = totalLen * 0.35;

    final cycleLen = totalLen + beamLen;
    final head = progress * cycleLen;
    final start = (head - beamLen).clamp(0.0, totalLen);
    final end = head.clamp(0.0, totalLen);

    if (end > start) {
      final pulsePath = metric.extractPath(start, end);
      final alpha = progress > 0.82 ? ((1.0 - progress) / 0.18) : 1.0;

      final pulsePaint = Paint()
        ..color = color.withValues(alpha: alpha.clamp(0.0, 1.0))
        ..style = PaintingStyle.stroke
        ..strokeWidth = strokeWidth
        ..strokeCap = StrokeCap.round
        ..strokeJoin = StrokeJoin.round
        ..maskFilter = const MaskFilter.blur(BlurStyle.solid, 1.5);

      canvas.drawPath(pulsePath, pulsePaint);
    }
  }

  @override
  bool shouldRepaint(_PulsePainter oldDelegate) =>
      oldDelegate.progress != progress;
}

More Flutter Loaders & Loading Animations