unicode_spinner 1.0.0 copy "unicode_spinner: ^1.0.0" to clipboard
unicode_spinner: ^1.0.0 copied to clipboard

ASCII and Unicode animated spinners for Flutter. One widget, 25+ built-in frame sequences — braille (Claude-style), classic pipe, dots, arrows, block bars, moon phases, clock, emoji and more. Fully cu [...]

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:unicode_spinner/unicode_spinner.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'unicode_spinner',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(colorSchemeSeed: Colors.indigo, useMaterial3: true),
      darkTheme: ThemeData.dark(useMaterial3: true).copyWith(
        colorScheme: ColorScheme.fromSeed(
          seedColor: Colors.indigo,
          brightness: Brightness.dark,
        ),
      ),
      themeMode: ThemeMode.system,
      home: const DemoPage(),
    );
  }
}

class _Section {
  const _Section(this.label, this.spinners);
  final String label;
  final List<(String, UnicodeSpinner)> spinners;
}

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

  static const _mono = TextStyle(fontSize: 22, fontFamily: 'monospace');
  static const _monoBig = TextStyle(fontSize: 28, fontFamily: 'monospace');
  static const _emoji = TextStyle(fontSize: 24);

  static final _sections = <_Section>[
    _Section('Braille', [
      ('braille', UnicodeSpinner.braille(style: _mono)),
      ('brailleFull', UnicodeSpinner.brailleFull(style: _mono)),
      ('brailleQuarter', UnicodeSpinner.brailleQuarter(style: _mono)),
      ('snake', UnicodeSpinner.snake(style: _mono)),
    ]),
    _Section('Classic ASCII', [
      ('line  | / - \\', UnicodeSpinner.line(style: _monoBig)),
      ('pipe', UnicodeSpinner.pipe(style: _monoBig)),
    ]),
    _Section('Dots', [
      ('dots', UnicodeSpinner.dots(style: _mono)),
      ('dotsBounce', UnicodeSpinner.dotsBounce(style: _mono)),
      ('dot', UnicodeSpinner.dot(style: _monoBig)),
    ]),
    _Section('Geometric', [
      ('circle', UnicodeSpinner.circle(style: _monoBig)),
      ('triangle', UnicodeSpinner.triangle(style: _monoBig)),
      ('box', UnicodeSpinner.box(style: _monoBig)),
      ('square', UnicodeSpinner.square(style: _monoBig)),
      ('bowtie', UnicodeSpinner.bowtie(style: _monoBig)),
      ('hourglass', UnicodeSpinner.hourglass(style: _monoBig)),
    ]),
    _Section('Arrows', [
      ('arrows', UnicodeSpinner.arrows(style: _monoBig)),
      ('arrowsBold', UnicodeSpinner.arrowsBold(style: _monoBig)),
    ]),
    _Section('Bar / Block', [
      ('bar', UnicodeSpinner.bar(style: _monoBig)),
      ('grow', UnicodeSpinner.grow(style: _monoBig)),
      ('pulse', UnicodeSpinner.pulse(style: _monoBig)),
    ]),
    _Section('Emoji', [
      ('moon', UnicodeSpinner.moon(style: _emoji)),
      ('clock', UnicodeSpinner.clock(style: _emoji)),
      ('earth', UnicodeSpinner.earth(style: _emoji)),
    ]),
    _Section('Misc', [
      ('star', UnicodeSpinner.star(style: _monoBig)),
    ]),
  ];

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

    final sliverItems = <Widget>[];
    for (final section in _sections) {
      sliverItems.add(_SectionHeader(label: section.label));
      for (final (label, spinner) in section.spinners) {
        sliverItems.add(_SpinnerTile(label: label, spinner: spinner));
      }
    }

    return Scaffold(
      body: CustomScrollView(
        slivers: [
          SliverAppBar(
            expandedHeight: 200,
            pinned: true,
            backgroundColor: cs.primary,
            flexibleSpace: FlexibleSpaceBar(
              centerTitle: true,
              title: Text(
                'unicode_spinner',
                style: TextStyle(
                  fontFamily: 'monospace',
                  fontWeight: FontWeight.bold,
                  fontSize: 15,
                  color: cs.onPrimary,
                ),
              ),
              background: Container(
                decoration: BoxDecoration(
                  gradient: LinearGradient(
                    begin: Alignment.topLeft,
                    end: Alignment.bottomRight,
                    colors: [cs.primary, cs.tertiary],
                  ),
                ),
                child: Align(
                  alignment: const Alignment(0, -0.25),
                  child: Column(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      UnicodeSpinner.braille(
                        style: TextStyle(fontSize: 52, color: cs.onPrimary),
                      ),
                      const SizedBox(height: 6),
                      Text(
                        '25+ animated spinners  ✦',
                        style: TextStyle(
                          fontFamily: 'monospace',
                          fontSize: 12,
                          color: cs.onPrimary.withValues(alpha: 0.75),
                          letterSpacing: 0.5,
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ),
          ),
          SliverPadding(
            padding: const EdgeInsets.fromLTRB(12, 0, 12, 32),
            sliver: SliverList.list(children: sliverItems),
          ),
        ],
      ),
    );
  }
}

class _SectionHeader extends StatelessWidget {
  const _SectionHeader({required this.label});
  final String label;

  @override
  Widget build(BuildContext context) {
    final cs = Theme.of(context).colorScheme;
    return Padding(
      padding: const EdgeInsets.fromLTRB(4, 28, 4, 6),
      child: Row(
        children: [
          Text(
            label.toUpperCase(),
            style: TextStyle(
              fontSize: 10,
              fontWeight: FontWeight.w700,
              letterSpacing: 1.4,
              color: cs.primary,
              fontFamily: 'monospace',
            ),
          ),
          const SizedBox(width: 10),
          Expanded(
            child: Divider(
              color: cs.outlineVariant,
              height: 1,
              thickness: 1,
            ),
          ),
        ],
      ),
    );
  }
}

class _SpinnerTile extends StatelessWidget {
  const _SpinnerTile({required this.label, required this.spinner});
  final String label;
  final UnicodeSpinner spinner;

  @override
  Widget build(BuildContext context) {
    final cs = Theme.of(context).colorScheme;
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 3),
      child: Material(
        color: cs.surfaceContainerLow,
        borderRadius: BorderRadius.circular(14),
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 13),
          child: Row(
            children: [
              Expanded(
                child: Text(
                  label,
                  style: TextStyle(
                    fontFamily: 'monospace',
                    fontSize: 13,
                    color: cs.onSurfaceVariant,
                  ),
                ),
              ),
              spinner,
            ],
          ),
        ),
      ),
    );
  }
}
0
likes
150
points
91
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

ASCII and Unicode animated spinners for Flutter. One widget, 25+ built-in frame sequences — braille (Claude-style), classic pipe, dots, arrows, block bars, moon phases, clock, emoji and more. Fully customizable.

Repository (GitHub)
View/report issues

Topics

#spinner #loading #animation #unicode #indicator

License

MIT (license)

Dependencies

flutter

More

Packages that depend on unicode_spinner