new_loading_indicator

A fork of the loading_indicator package, providing a rich collection of loading animations for Flutter applications. The original package is unmaintained, has outdated dependencies, poor documentation, and was written without a single test. This fork aims to:

  • Modernize the codebase for compatibility with the latest Flutter/Dart SDK versions
  • Update outdated dependencies
  • Add comprehensive test coverage
  • Provide detailed documentation for maintainability
  • Ensure ongoing maintenance and active development

Visit the example on GitLab Pages for a live demo.

Installation

Add this to your package's pubspec.yaml file:

dependencies:
  new_loading_indicator: ^latest_version

Usage

import 'package:material_ui/material_ui.dart';
import 'package:new_loading_indicator/new_loading_indicator.dart';

// Basic usage
LoadingIndicator(
  indicatorType: Indicator.ballPulse, // Choose your animation
  colors: const [Colors.white],        // Customize colors
  strokeWidth: 2,                      // Width of rings, arcs and bars
  backgroundColor: Colors.black,       // Optional background color
);

// With custom size
SizedBox(
  width: 100,
  height: 100,
  child: LoadingIndicator(
    indicatorType: Indicator.ballSpinFadeLoader,
    colors: const [Colors.white],
  ),
);

Available Indicators

The package includes 34 indicators, each customizable to match your app's design:

  • Ball Pulse
  • Ball Grid Pulse
  • Ball Clip Rotate
  • Square Spin
  • Ball Clip Rotate Pulse
  • Ball Clip Rotate Multiple
  • Ball Pulse Rise
  • Ball Rotate
  • Cube Transition
  • Ball Zig Zag
  • Ball Zig Zag Deflect
  • Ball Triangle Path
  • Ball Triangle Path Colored
  • Ball Triangle Path Colored Filled
  • Ball Scale
  • Line Scale
  • Line Scale Party
  • Ball Scale Multiple
  • Ball Pulse Sync
  • Ball Beat
  • Line Scale Pulse Out
  • Line Scale Pulse Out Rapid
  • Ball Scale Ripple
  • Ball Scale Ripple Multiple
  • Ball Spin Fade Loader
  • Line Spin Fade Loader
  • Triangle Skew Spin
  • Pacman
  • Ball Grid Beat
  • Semi Circle Spin
  • Ball Rotate Chase
  • Orbit
  • Audio Equalizer
  • Circle Stroke Spin

Controlling playback

Pass a LoadingIndicatorController to pause, pause at a chosen point, or resume an indicator. Without one, the indicator plays continuously.

final controller = LoadingIndicatorController();

LoadingIndicator(
  indicatorType: Indicator.ballPulse,
  controller: controller,
);

controller.pause();   // Freeze on the current frame.
controller.resume();  // Carry on from the same frame.

// Keep playing until the indicator reaches the middle of its cycle, then
// pause there. The future completes once it has paused.
await controller.pauseAt(0.5);

// Or pause on that frame straight away.
await controller.pauseAt(
  0.5,
  behavior: LoadingIndicatorPauseBehavior.jumpToTarget,
);

pauseAt takes a progress value from 0.0 to 1.0 through the indicator's animation cycle. If the target is behind the current position, waitUntilTarget (the default) plays on into the next cycle to reach it. Indicators made of several animations move all of them together, so pausing at a target, whether by waiting or jumping, always shows the same frame.

A newer command replaces a pending pauseAt, and its future then completes with a LoadingIndicatorCommandCanceled error. If nothing awaits or listens to the future, the cancellation is not reported as an unhandled error.

try {
  await controller.pauseAt(0.5);
} on LoadingIndicatorCommandCanceled {
  // Resumed or paused elsewhere before the target was reached.
}

The controller is a ChangeNotifier. Listen to it to follow status (detached, playing, pausing or paused) and progress, which updates on every frame while the indicator plays and is null while the controller is detached.

ListenableBuilder(
  listenable: controller,
  builder: (context, _) => Text(
    '${controller.status.name} ${controller.progress?.toStringAsFixed(2)}',
  ),
);

A few rules:

  • A controller drives one indicator at a time. Giving it to a second indicator while the first is still in the tree is an error, reported as a FlutterError.
  • Commands given before the indicator is built are applied as soon as it is. The latest command is applied again whenever the controller is passed to a new indicator, for example after switching indicatorType.
  • Removing the controller from an indicator, or disposing of it, lets the indicator play on its own again.
  • Dispose of the controller when you no longer need it, typically in State.dispose.

Stroke width

strokeWidth sets the width of stroked shapes, such as the rings in ballClipRotate or circleStrokeSpin, and defaults to 2 logical pixels.

It also sets the width of the bars in the line indicators: lineScale, lineScaleParty, lineScalePulseOut, lineScalePulseOutRapid and lineSpinFadeLoader. When it is null, those bars stay as wide as the indicator's size allows. A bar is never drawn wider than that, whatever the value.

Migrating from 3.x

LoadingIndicator.pause has been removed. Replace the flag with a controller:

// 3.x
LoadingIndicator(indicatorType: Indicator.ballPulse, pause: isPaused);

// 4.x
final controller = LoadingIndicatorController(); // e.g. a State field

LoadingIndicator(indicatorType: Indicator.ballPulse, controller: controller);

// Where isPaused used to change:
isPaused ? controller.pause() : controller.resume();

Two indicators now look different:

  • Line indicators that were given a strokeWidth now draw their bars at that width instead of filling the space available. Remove strokeWidth to keep the old look.
  • circleStrokeSpin no longer wraps CircularProgressIndicator. It now draws a three-quarter ring that turns once every 1.2 seconds, which can be paused like every other indicator and no longer needs a package:material_ui theme.

Colors

When colors is null or empty, the indicator uses Theme.of(context).colorScheme.primary from package:material_ui. A MaterialApp from package:flutter/material.dart does not provide that theme. Indicators with several shapes cycle through the list, so [Colors.red, Colors.blue] alternates the two.

Test Coverage Report

$ flutter test --coverage && lcov --summary coverage/lcov.info
00:18 +434: All tests passed!
Reading tracefile coverage/lcov.info.
Summary coverage rate:
  source files: 38
  lines.......: 100.0% (1707 of 1707 lines)
  functions...: no data found

Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

License

This project is licensed under the Apache License 2.0. The original work is copyrighted by its respective owners and continues to be licensed under the Apache License 2.0. Refer to the NOTICE file for more details.