loading_ui 0.1.0-dev.1 copy "loading_ui: ^0.1.0-dev.1" to clipboard
loading_ui: ^0.1.0-dev.1 copied to clipboard

A collection of 35+ beautiful, customizable loading animations for Flutter. This is an AI-created version based on the work of loading-ui.com.

example/lib/main.dart

import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:loading_ui/loading_ui.dart';
import 'package:url_launcher/url_launcher.dart';

void main() => runApp(const LoadingShowcaseApp());

// ─── App shell ────────────────────────────────────────────────────────────────

class LoadingShowcaseApp extends StatelessWidget {
  const LoadingShowcaseApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'loading_ui showcase',
      debugShowCheckedModeBanner: false,
      themeMode: ThemeMode.dark,
      theme: ThemeData(
        colorSchemeSeed: const Color(0xFF6750A4),
        useMaterial3: true,
        brightness: Brightness.light,
      ),
      darkTheme: ThemeData(
        colorSchemeSeed: const Color(0xFF6750A4),
        useMaterial3: true,
        brightness: Brightness.dark,
      ),
      home: const ShowcasePage(),
    );
  }
}

// ─── Controls state ───────────────────────────────────────────────────────────

class _Controls {
  const _Controls({
    required this.size,
    required this.durationScale,
    required this.colorIndex,
  });

  final double size;
  final double durationScale;
  final int colorIndex;

  static const colors = <Color>[
    Color(0xFF6750A4), // purple (default)
    Color(0xFF0061A4), // blue
    Color(0xFF006E2C), // green
    Color(0xFFBA1A1A), // red
    Color(0xFFE65C00), // orange
    Color(0xFF37474F), // slate
    Colors.black,
    Colors.white,
  ];

  Color get color => colors[colorIndex];

  double get speed => 1.0 / durationScale;

  Duration scale(Duration base) =>
      Duration(microseconds: (base.inMicroseconds * durationScale).round());
}

// ─── Loader catalogue ─────────────────────────────────────────────────────────

typedef _LoderEntry = ({
  String name,
  String category,
  Widget Function(_Controls) build,
});

List<_LoderEntry> _catalogue(_Controls c) => [
  // Spinners
  (
    name: 'RingLoader',
    category: 'Spinners',
    build: (_) => RingLoader(size: c.size, color: c.color),
  ),
  (
    name: 'SpokesLoader',
    category: 'Spinners',
    build: (_) => SpokesLoader(
      size: c.size,
      color: c.color,
      duration: c.scale(const Duration(milliseconds: 1200)),
    ),
  ),
  (
    name: 'ArcLoader',
    category: 'Spinners',
    build: (_) => ArcLoader(
      size: c.size,
      color: c.color,
      duration: c.scale(const Duration(milliseconds: 900)),
    ),
  ),
  (
    name: 'DualArcLoader',
    category: 'Spinners',
    build: (_) => DualArcLoader(size: c.size, color: c.color),
  ),
  (
    name: 'QuarterRingLoader',
    category: 'Spinners',
    build: (_) => QuarterRingLoader(size: c.size, color: c.color),
  ),
  (
    name: 'ClockRingLoader',
    category: 'Spinners',
    build: (_) => ClockRingLoader(size: c.size, color: c.color),
  ),
  (
    name: 'ConcentricRingLoader',
    category: 'Spinners',
    build: (_) => ConcentricRingLoader(size: c.size, color: c.color),
  ),
  (
    name: 'OrbitRingLoader',
    category: 'Spinners',
    build: (_) => OrbitRingLoader(size: c.size, color: c.color),
  ),
  (
    name: 'SatelliteRingLoader',
    category: 'Spinners',
    build: (_) => SatelliteRingLoader(size: c.size, color: c.color),
  ),
  (
    name: 'TripleDotLoader',
    category: 'Spinners',
    build: (_) => TripleDotLoader(size: c.size, color: c.color),
  ),
  (
    name: 'ClassicLoader',
    category: 'Spinners',
    build: (_) => ClassicLoader(size: c.size, color: c.color),
  ),
  (
    name: 'DotsRingLoader',
    category: 'Spinners',
    build: (_) => DotsRingLoader(size: c.size, color: c.color),
  ),
  (
    name: 'SpiralLoader',
    category: 'Spinners',
    build: (_) => SpiralLoader(size: c.size, color: c.color),
  ),
  (
    name: 'TwinOrbitLoader',
    category: 'Spinners',
    build: (_) => TwinOrbitLoader(size: c.size, color: c.color),
  ),
  (
    name: 'DashRingLoader',
    category: 'Spinners',
    build: (_) => DashRingLoader(size: c.size, color: c.color),
  ),
  (
    name: 'CometLoader',
    category: 'Spinners',
    build: (_) => CometLoader(
      size: c.size,
      color: c.color,
      duration: c.scale(const Duration(milliseconds: 1800)),
    ),
  ),
  // Pulses
  (
    name: 'PulseLoader',
    category: 'Pulses',
    build: (_) => PulseLoader(size: c.size, color: c.color),
  ),
  (
    name: 'PulseDotLoader',
    category: 'Pulses',
    build: (_) => PulseDotLoader(size: c.size, color: c.color),
  ),
  (
    name: 'RippleLoader',
    category: 'Pulses',
    build: (_) => RippleLoader(size: c.size, color: c.color),
  ),
  // Dots
  (
    name: 'DotsLoader',
    category: 'Dots',
    build: (_) => DotsLoader(
      color: c.color,
      duration: c.scale(const Duration(milliseconds: 1400)),
    ),
  ),
  (
    name: 'BouncingDotsLoader',
    category: 'Dots',
    build: (_) => BouncingDotsLoader(
      color: c.color,
      duration: c.scale(const Duration(milliseconds: 600)),
    ),
  ),
  (
    name: 'BobbingDotsLoader',
    category: 'Dots',
    build: (_) => BobbingDotsLoader(color: c.color),
  ),
  (
    name: 'PulsatingDotsLoader',
    category: 'Dots',
    build: (_) => PulsatingDotsLoader(color: c.color),
  ),
  (
    name: 'TypingLoader',
    category: 'Dots',
    build: (_) => TypingLoader(color: c.color),
  ),
  // Bars
  (
    name: 'BarsLoader',
    category: 'Bars',
    build: (_) => BarsLoader(
      color: c.color,
      duration: c.scale(const Duration(milliseconds: 1200)),
    ),
  ),
  (
    name: 'WaveLoader',
    category: 'Bars',
    build: (_) => WaveLoader(
      color: c.color,
      duration: c.scale(const Duration(milliseconds: 1200)),
    ),
  ),
  // Text
  (
    name: 'TextBlinkLoader',
    category: 'Text',
    build: (_) => TextBlinkLoader(
      duration: c.scale(const Duration(milliseconds: 2000)),
      child: Text(
        'Loading…',
        style: TextStyle(color: c.color, fontWeight: FontWeight.w600),
      ),
    ),
  ),
  (
    name: 'TextDotsLoader',
    category: 'Text',
    build: (_) => TextDotsLoader(
      child: Text(
        'Loading',
        style: TextStyle(color: c.color, fontWeight: FontWeight.w600),
      ),
    ),
  ),
  (
    name: 'TextShimmerLoader',
    category: 'Text',
    build: (_) => TextShimmerLoader(
      text: 'Loading…',
      shimmerColor: c.color,
      duration: c.scale(const Duration(milliseconds: 2000)),
    ),
  ),
  (
    name: 'TextShimmerWaveLoader',
    category: 'Text',
    build: (_) => TextShimmerWaveLoader(
      text: 'Loading…',
      shimmerColor: c.color,
      duration: c.scale(const Duration(milliseconds: 1000)),
    ),
  ),
  // Special
  (
    name: 'SkeletonLoader',
    category: 'Special',
    build: (_) =>
        SkeletonLoader(width: c.size, height: c.size, color: c.color),
  ),
  (
    name: 'TerminalLoader',
    category: 'Special',
    build: (_) => TerminalLoader(
      color: c.color,
      duration: c.scale(const Duration(milliseconds: 1000)),
    ),
  ),
  (
    name: 'InfinityLoader',
    category: 'Special',
    build: (_) => InfinityLoader(size: c.size, color: c.color),
  ),
  (
    name: 'SwirlingLoader',
    category: 'Special',
    build: (_) => SwirlingLoader(size: c.size, color: c.color),
  ),
  (
    name: 'WanderingEyesLoader',
    category: 'Special',
    build: (_) => WanderingEyesLoader(size: c.size, color: c.color),
  ),
  (
    name: 'AnalyzingImageLoader',
    category: 'Special',
    build: (_) => AnalyzingImageLoader(
      size: c.size,
      color: c.color,
      duration: c.scale(const Duration(milliseconds: 3000)),
    ),
  ),
  // Matrix — Dot Matrix loaders
  (
    name: 'NeonDrift',
    category: 'Matrix',
    build: (_) => NeonDriftLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'PulseLadder',
    category: 'Matrix',
    build: (_) => PulseLadderLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'CoreSpiral',
    category: 'Matrix',
    build: (_) => CoreSpiralLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'PrismSweep',
    category: 'Matrix',
    build: (_) => PrismSweepLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'FluxColumns',
    category: 'Matrix',
    build: (_) => FluxColumnsLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'CrtGlide',
    category: 'Matrix',
    build: (_) => CrtGlideLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'EchoRing',
    category: 'Matrix',
    build: (_) => EchoRingLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'OriginWave',
    category: 'Matrix',
    build: (_) => OriginWaveLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'CoreRotor',
    category: 'Matrix',
    build: (_) => CoreRotorLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'SoundBars',
    category: 'Matrix',
    build: (_) => SoundBarsLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'CoreSpokes',
    category: 'Matrix',
    build: (_) => CoreSpokesLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'AltitudeWave',
    category: 'Matrix',
    build: (_) => AltitudeWaveLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'CornerBounce',
    category: 'Matrix',
    build: (_) => CornerBounceLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'RowSweep',
    category: 'Matrix',
    build: (_) => RowSweepLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'ColumnRake',
    category: 'Matrix',
    build: (_) => ColumnRakeLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  // ── Phase 2: Square ───────────────────────────────────────────────────────────
  (
    name: 'TwinOrbit',
    category: 'Matrix',
    build: (_) => TwinOrbitMatrixLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'StrobeStack',
    category: 'Matrix',
    build: (_) => StrobeStackLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'GlyphPulse',
    category: 'Matrix',
    build: (_) => GlyphPulseLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'PrismBloom',
    category: 'Matrix',
    build: (_) => PrismBloomLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'HelixGlow',
    category: 'Matrix',
    build: (_) => HelixGlowLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'HelixCore',
    category: 'Matrix',
    build: (_) => HelixCoreLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'HalfHelix',
    category: 'Matrix',
    build: (_) => HalfHelixLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'InfinityRun',
    category: 'Matrix',
    build: (_) => InfinityRunLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'MobiusRun',
    category: 'Matrix',
    build: (_) => MobiusRunLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'SnakeWave',
    category: 'Matrix',
    build: (_) => SnakeWaveLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'ColWave',
    category: 'Matrix',
    build: (_) => ColWaveLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'ConcentricRing',
    category: 'Matrix',
    build: (_) => ConcentricRingMatrixLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  // ── Phase 2: Circular ───────────────────────────────────────────────────────
  (
    name: 'HaloDrift',
    category: 'Matrix',
    build: (_) => HaloDriftLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'TriOrbit',
    category: 'Matrix',
    build: (_) => TriOrbitLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'PlasmaVeil',
    category: 'Matrix',
    build: (_) => PlasmaVeilLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'RadarArc',
    category: 'Matrix',
    build: (_) => RadarArcLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'NovaWheel',
    category: 'Matrix',
    build: (_) => NovaWheelLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'PhaseOrb',
    category: 'Matrix',
    build: (_) => PhaseOrbLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'GateShift',
    category: 'Matrix',
    build: (_) => GateShiftLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'HeartPulse',
    category: 'Matrix',
    build: (_) => HeartPulseLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'StarCompass',
    category: 'Matrix',
    build: (_) => StarCompassLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'BinaryBloom',
    category: 'Matrix',
    build: (_) => BinaryBloomLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  // ── Phase 2: Triangle ──────────────────────────────────────────────────────
  (
    name: 'VertexChase',
    category: 'Matrix',
    build: (_) => VertexChaseLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'BrailleBeat',
    category: 'Matrix',
    build: (_) => BrailleBeatLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'ObliqueWeave',
    category: 'Matrix',
    build: (_) => ObliqueWeaveLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  // ── Fase 3 ──
  (
    name: 'BlockDrop',
    category: 'Matrix',
    build: (_) => BlockDropLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'LunarBreathe',
    category: 'Matrix',
    build: (_) => LunarBreatheLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'ArcBeacon',
    category: 'Matrix',
    build: (_) => ArcBeaconLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'TwinHelix',
    category: 'Matrix',
    build: (_) => TwinHelixLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'RungShift',
    category: 'Matrix',
    build: (_) => RungShiftLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'GlyphCluster',
    category: 'Matrix',
    build: (_) => GlyphClusterLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'RailScan',
    category: 'Matrix',
    build: (_) => RailScanLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'CheckerShift',
    category: 'Matrix',
    build: (_) => CheckerShiftLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'PulsePair',
    category: 'Matrix',
    build: (_) => PulsePairLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'OrbitCell',
    category: 'Matrix',
    build: (_) => OrbitCellLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'GlyphCycle',
    category: 'Matrix',
    build: (_) => GlyphCycleLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'WingMetronome',
    category: 'Matrix',
    build: (_) => WingMetronomeLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'CoronaTier',
    category: 'Matrix',
    build: (_) => CoronaTierLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'ShelfDescent',
    category: 'Matrix',
    build: (_) => ShelfDescentLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'SkewDrift',
    category: 'Matrix',
    build: (_) => SkewDriftLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  // ── Fase 4 ──
  (
    name: 'SerpentZip',
    category: 'Matrix',
    build: (_) => SerpentZipLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'PillarSweep',
    category: 'Matrix',
    build: (_) => PillarSweepLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'TripodHandoff',
    category: 'Matrix',
    build: (_) => TripodHandoffLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'Updraft',
    category: 'Matrix',
    build: (_) => UpdraftLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'InfinityTrace',
    category: 'Matrix',
    build: (_) => InfinityTraceLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'HollowShell',
    category: 'Matrix',
    build: (_) => HollowShellLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'PivotRay',
    category: 'Matrix',
    build: (_) => PivotRayLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  (
    name: 'TwinPerimeter',
    category: 'Matrix',
    build: (_) => TwinPerimeterLoader(
      size: c.size,
      color: c.color,
      speed: c.speed,
      animated: true,
    ),
  ),
  // ── Fase 5 (Icon) ──
  (
    name: 'DotMatrixIcon',
    category: 'Matrix',
    build: (_) => DotMatrixIcon(
      size: c.size,
      color: c.color,
      speed: c.speed,
    ),
  ),
];

// ─── Main page ────────────────────────────────────────────────────────────────

class ShowcasePage extends StatefulWidget {
  const ShowcasePage({super.key});

  @override
  State<ShowcasePage> createState() => _ShowcasePageState();
}

class _ShowcasePageState extends State<ShowcasePage> {
  double _size = 48;
  double _durationScale = 1.0;
  int _colorIndex = 7;
  String? _categoryFilter;

  _Controls get _controls => _Controls(
    size: _size,
    durationScale: _durationScale,
    colorIndex: _colorIndex,
  );

  static const _categories = [
    'All',
    'Spinners',
    'Pulses',
    'Dots',
    'Bars',
    'Text',
    'Special',
    'Matrix',
  ];

  @override
  Widget build(BuildContext context) {
    final scheme = Theme.of(context).colorScheme;
    final controls = _controls;
    final all = _catalogue(controls);
    final visible = _categoryFilter == null || _categoryFilter == 'All'
        ? all
        : all.where((e) => e.category == _categoryFilter).toList();

    return Scaffold(
      appBar: AppBar(
        title: Text(
          'loading_ui',
          style: TextStyle(
            fontWeight: FontWeight.w800,
            fontSize: 18,
            letterSpacing: -0.5,
            color: scheme.primary,
          ),
        ),
        centerTitle: true,
        backgroundColor: scheme.surfaceContainerHighest,
        actions: [
          IconButton(
            icon: SvgPicture.asset(
              'assets/github.svg',
              package: 'loading_ui',
              width: 24,
              height: 24,
              colorFilter: ColorFilter.mode(
                scheme.onSurface,
                BlendMode.srcIn,
              ),
            ),
            tooltip: 'Ver en GitHub',
            onPressed: () => launchUrl(
              Uri.parse(
                'https://github.com/anfeMurillo/Loading-UI-Flutter-Package.git',
              ),
            ),
          ),
          const SizedBox(width: 8),
        ],
      ),
      body: Column(
        children: [
          // ── Controls panel ──────────────────────────────────────────────────
          _ControlsPanel(
            size: _size,
            durationScale: _durationScale,
            colorIndex: _colorIndex,
            onSizeChanged: (v) => setState(() => _size = v),
            onDurationChanged: (v) => setState(() => _durationScale = v),
            onColorChanged: (i) => setState(() => _colorIndex = i),
          ),
          // ── Category filter ─────────────────────────────────────────────────
          SingleChildScrollView(
            scrollDirection: Axis.horizontal,
            padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
            child: Row(
              children: _categories.map((cat) {
                final selected =
                    (cat == 'All' && _categoryFilter == null) ||
                    cat == _categoryFilter;
                return Padding(
                  padding: const EdgeInsets.only(right: 6),
                  child: FilterChip(
                    label: Text(cat),
                    selected: selected,
                    onSelected: (_) => setState(() {
                      _categoryFilter = (cat == 'All') ? null : cat;
                    }),
                  ),
                );
              }).toList(),
            ),
          ),
          const Divider(height: 1),
          // ── Grid ────────────────────────────────────────────────────────────
          Expanded(
            child: GridView.builder(
              padding: const EdgeInsets.all(12),
              gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
                maxCrossAxisExtent: 200,
                mainAxisExtent: 160,
                crossAxisSpacing: 10,
                mainAxisSpacing: 10,
              ),
              itemCount: visible.length,
              itemBuilder: (context, i) {
                final entry = visible[i];
                return _LoaderCard(
                  name: entry.name,
                  category: entry.category,
                  child: entry.build(controls),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

// ─── Controls panel ───────────────────────────────────────────────────────────

class _ControlsPanel extends StatelessWidget {
  const _ControlsPanel({
    required this.size,
    required this.durationScale,
    required this.colorIndex,
    required this.onSizeChanged,
    required this.onDurationChanged,
    required this.onColorChanged,
  });

  final double size;
  final double durationScale;
  final int colorIndex;
  final ValueChanged<double> onSizeChanged;
  final ValueChanged<double> onDurationChanged;
  final ValueChanged<int> onColorChanged;

  @override
  Widget build(BuildContext context) {
    final scheme = Theme.of(context).colorScheme;

    return ClipRRect(
      borderRadius: const BorderRadius.vertical(bottom: Radius.circular(24)),
      child: BackdropFilter(
        filter: ImageFilter.blur(sigmaX: 12, sigmaY: 12),
        child: Container(
          decoration: BoxDecoration(
            color: scheme.surfaceContainerHighest.withValues(alpha: 0.15),
            borderRadius: const BorderRadius.vertical(bottom: Radius.circular(24)),
            border: Border(
              bottom: BorderSide(
                color: scheme.onSurface.withValues(alpha: 0.12),
                width: 1,
              ),
            ),
          ),
          padding: const EdgeInsets.fromLTRB(16, 12, 16, 10),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              // Size slider
              Row(
                children: [
                  SizedBox(
                    width: 72,
                    child: Text(
                      'Size',
                      style: TextStyle(
                        fontWeight: FontWeight.w600,
                        color: scheme.onSurface,
                      ),
                    ),
                  ),
                  Expanded(
                    child: SliderTheme(
                      data: SliderThemeData(
                        trackHeight: 4,
                        thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 8),
                        overlayShape: const RoundSliderOverlayShape(overlayRadius: 16),
                        activeTrackColor: scheme.primary.withValues(alpha: 0.8),
                        inactiveTrackColor: scheme.onSurface.withValues(alpha: 0.15),
                        thumbColor: scheme.primary,
                        overlayColor: scheme.primary.withValues(alpha: 0.2),
                      ),
                      child: Slider(
                        value: size,
                        min: 16,
                        max: 96,
                        divisions: 20,
                        label: '${size.round()} px',
                        onChanged: onSizeChanged,
                      ),
                    ),
                  ),
                  SizedBox(
                    width: 40,
                    child: Text(
                      '${size.round()}px',
                      textAlign: TextAlign.end,
                      style: TextStyle(
                        fontSize: 12,
                        color: scheme.onSurfaceVariant,
                      ),
                    ),
                  ),
                ],
              ),
              // Duration slider
              Row(
                children: [
                  SizedBox(
                    width: 72,
                    child: Text(
                      'Speed',
                      style: TextStyle(
                        fontWeight: FontWeight.w600,
                        color: scheme.onSurface,
                      ),
                    ),
                  ),
                  Expanded(
                    child: SliderTheme(
                      data: SliderThemeData(
                        trackHeight: 4,
                        thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 8),
                        overlayShape: const RoundSliderOverlayShape(overlayRadius: 16),
                        activeTrackColor: scheme.primary.withValues(alpha: 0.8),
                        inactiveTrackColor: scheme.onSurface.withValues(alpha: 0.15),
                        thumbColor: scheme.primary,
                        overlayColor: scheme.primary.withValues(alpha: 0.2),
                      ),
                      child: Slider(
                        value: durationScale,
                        min: 0.25,
                        max: 4.0,
                        divisions: 15,
                        label: '${durationScale.toStringAsFixed(2)}×',
                        onChanged: onDurationChanged,
                      ),
                    ),
                  ),
                  SizedBox(
                    width: 40,
                    child: Text(
                      '${durationScale.toStringAsFixed(2)}×',
                      textAlign: TextAlign.end,
                      style: TextStyle(
                        fontSize: 12,
                        color: scheme.onSurfaceVariant,
                      ),
                    ),
                  ),
                ],
              ),
              // Color picker
              Row(
                children: [
                  SizedBox(
                    width: 72,
                    child: Text(
                      'Color',
                      style: TextStyle(
                        fontWeight: FontWeight.w600,
                        color: scheme.onSurface,
                      ),
                    ),
                  ),
                  ..._Controls.colors.asMap().entries.map((e) {
                    final selected = e.key == colorIndex;
                    return GestureDetector(
                      onTap: () => onColorChanged(e.key),
                      child: Container(
                        margin: const EdgeInsets.only(right: 8, bottom: 4),
                        width: 28,
                        height: 28,
                        decoration: BoxDecoration(
                          color: e.value,
                          shape: BoxShape.circle,
                          border: Border.all(
                            color: selected
                                ? scheme.primary
                                : scheme.onSurface.withValues(alpha: 0.2),
                            width: selected ? 2.5 : 1,
                          ),
                          boxShadow: selected
                              ? [
                                  BoxShadow(
                                    color: e.value.withValues(alpha: 0.6),
                                    blurRadius: 8,
                                    spreadRadius: 1,
                                  ),
                                ]
                              : null,
                        ),
                        child: selected
                            ? Center(
                                child: Container(
                                  width: 8,
                                  height: 8,
                                  decoration: BoxDecoration(
                                    color: Colors.white,
                                    shape: BoxShape.circle,
                                  ),
                                ),
                              )
                            : null,
                      ),
                    );
                  }),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }
}

// ─── Loader card ──────────────────────────────────────────────────────────────

class _LoaderCard extends StatelessWidget {
  const _LoaderCard({
    required this.name,
    required this.category,
    required this.child,
  });

  final String name;
  final String category;
  final Widget child;

  @override
  Widget build(BuildContext context) {
    final scheme = Theme.of(context).colorScheme;

    return Card(
      elevation: 0,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(12),
        side: BorderSide(color: scheme.outlineVariant),
      ),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Expanded(
            child: Center(
              child: Padding(padding: const EdgeInsets.all(12), child: child),
            ),
          ),
          Container(
            width: double.infinity,
            padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
            decoration: BoxDecoration(
              color: scheme.surfaceContainerLow,
              borderRadius: const BorderRadius.vertical(
                bottom: Radius.circular(12),
              ),
            ),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              mainAxisSize: MainAxisSize.min,
              children: [
                Text(
                  name,
                  style: const TextStyle(
                    fontSize: 11,
                    fontWeight: FontWeight.w600,
                  ),
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                ),
                Text(
                  category,
                  style: TextStyle(
                    fontSize: 10,
                    color: scheme.onSurfaceVariant,
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}
2
likes
150
points
10
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A collection of 35+ beautiful, customizable loading animations for Flutter. This is an AI-created version based on the work of loading-ui.com.

Homepage
Repository (GitHub)
View/report issues

Topics

#loading #spinner #animation #ui

License

MIT (license)

Dependencies

flutter

More

Packages that depend on loading_ui