ashlarui

Spinner

A turning ring that says something is working.

Spinner, as it looks in an app Spinner, as it looks in an app, dark
import 'package:flutter/widgets.dart';

import 'ui/badge.dart';
import 'ui/button.dart';
import 'ui/spinner.dart';

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

  @override
  Widget build(BuildContext context) => const Wrap(
    spacing: 16,
    crossAxisAlignment: WrapCrossAlignment.center,
    children: [
      Spinner(),
      Button(onPressed: null, leading: Spinner(), child: Text('Saving')),
      Badge(variant: BadgeVariant.secondary, leading: Spinner(), child: Text('Syncing')),
    ],
  );
}

Add it

In your Flutter project:

$ ashlar add spinner

It writes spinner.dart into lib/ui, keeping any you already have, and adds ashlar_icons to pubspec.yaml. It needs ashlar_icons, which come with it.

Examples

Other ways to use it, each running live, with its code.

In a button

While it works.

In a button, as it looks in an app In a button, as it looks in an app, dark
// In a button: While it works.
import 'package:flutter/widgets.dart';

import 'ui/button.dart';
import 'ui/spinner.dart';

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

  @override
  Widget build(BuildContext context) =>
      const Button(onPressed: null, leading: Spinner(size: 16), child: Text('Saving…'));
}

API

Read from the source above: each parameter with its type, its default, and what it is for.

Spinnerclass

A turning ring that says something is working. Its colour and size come from the IconTheme, so inside a Button it matches the label.

Parameter Type Default What it is for
size double? Its width and height; the icon size around it when not given.
color Color? Its colour; the icon colour around it when not given.
semanticLabel String 'Loading' What a screen reader calls it when its visible text does not say, as for an icon alone.

Source

What lands in your app. It is yours from then on: change it as you would your own code, and ashlar add will not write over it.

lib/ui/spinner.dart 61 lines
// Ashlar UI: Spinner. Copied into your app by `ashlar add spinner`; it is yours to change.

import 'package:ashlar_icons/ashlar_icons.dart';
import 'package:flutter/widgets.dart';

/// A turning ring that says something is working. Its colour and size come from the
/// IconTheme, so inside a Button it matches the label.
///
/// ```dart
/// Button(onPressed: null, leading: Spinner(), child: Text('Saving'))
/// ```
class Spinner extends StatefulWidget {
  const Spinner({
    super.key,
    this.size,
    this.color,
    this.semanticLabel = 'Loading',
  });

  /// Its width and height; the icon size around it when not given.
  final double? size;

  /// Its colour; the icon colour around it when not given.
  final Color? color;

  /// What a screen reader calls it when its visible text does not say, as for an icon
  /// alone.
  final String semanticLabel;

  @override
  State<Spinner> createState() => _SpinnerState();
}

class _SpinnerState extends State<Spinner> with SingleTickerProviderStateMixin {
  late final _turn = AnimationController(
    vsync: this,
    duration: const Duration(seconds: 1),
  )..repeat();

  @override
  void dispose() {
    _turn.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) => Semantics(
    label: widget.semanticLabel,
    liveRegion: true,
    child: ExcludeSemantics(
      child: RotationTransition(
        turns: _turn,
        child: AshlarIcon(
          AshlarIconsRoundedStroke.loaderCircle,
          size: widget.size,
          color: widget.color,
        ),
      ),
    ),
  );
}