CLI tool
nslogx/flutter_easyloading avatar
nslogx/flutter_easyloading

flutter_easyloading: a context-free loading and toast overlay for Flutter

A clean and lightweight loading/toast widget for Flutter, easy to use without context, support iOS Android and Web.

1,339 stars249 forksDartMIT

At a glance

What is it?
Flutter EasyLoading puts loading spinners, determinate progress, result states and toasts behind global calls that do not need a BuildContext. The 4.0 line raises the Dart and Flutter floors and ships a migration guide.
Who is it for?
Adopt flutter_easyloading if you want one global overlay API and you are on Dart 3.6.0 or later with Flutter 3.27.0 or later. Do not adopt it if you are still on 3.x and cannot absorb the migration, or if you need several independent overlays on screen at once, because the API exposes a single shared instance and a single isShow flag.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 62 days ago.
What is it written in?
Mainly Dart, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem: showing a spinner from code that has no BuildContext

Most Flutter overlay APIs want a BuildContext. That is fine inside a widget's build method, and awkward everywhere else: an API client, a repository, a background sync routine, a callback that has already outlived the widget that created it. The usual workarounds are a GlobalKey<NavigatorState>, a context stored in a singleton, or passing a context down through layers that have no business knowing about the UI.

Flutter EasyLoading removes that argument. The README states that display calls do not require a BuildContext, and the quick start backs it up: you install one Host at the root of the application, then call EasyLoading.show, EasyLoading.showSuccess or EasyLoading.showToast from anywhere. The audience is Flutter application developers who already have a MaterialApp or CupertinoApp and want a single overlay layer for loading, progress, result and toast states without wiring a context through their data layer.

How the Host and the shared instance work together

There are two halves. The Host is a widget installed once at the application root through EasyLoading.init(), which returns a TransitionBuilder for the app's builder parameter. The README notes that FlutterEasyLoading(child: child) creates the Host directly, but that most applications should use EasyLoading.init() instead. The other half is a shared configuration instance reached through EasyLoading() or EasyLoading.instance.

Display calls go to that shared instance and are routed to the mounted Host. Because there is one instance, there is one overlay state: EasyLoading.isShow reports whether an overlay is active, and EasyLoading.dismiss() closes it. Per-call parameters such as status, indicator, maskType, dismissOnTap, duration, options and toastPosition override the global defaults for that call only, while the instance properties set the defaults once at startup.

The lifecycle is observable. EasyLoading.addStatusCallback receives an EasyLoadingStatus of show or dismiss, and EasyLoading.addDismissCallback receives an EasyLoadingDismissReason. The README lists four reasons: programmatic, tap, timeout, or hostDetached. That last one is the interesting case, since it is how the library tells you the Host went away while an overlay was pending. Callbacks can be removed individually with removeCallback and removeDismissCallback, or all at once with removeAllCallbacks and removeAllDismissCallbacks, and the README's own example removes them when their owner is disposed. Treat that as the intended pattern, not an optional nicety.

Installing flutter_easyloading and showing your first overlay

The README gives two install routes. The command form adds the dependency and updates pubspec.yaml for you. The manual form pins the constraint, which is what you want in a team repository where the pubspec is reviewed. The 4.0.2 release is the current one in the changelog.

bash
flutter pub add flutter_easyloading

If you prefer to edit the manifest yourself, the README shows this entry, and the import line that goes with it:

yaml
dependencies:
  flutter_easyloading: ^4.0.2
dart
import 'package:flutter_easyloading/flutter_easyloading.dart';

Next, install the Host. This is the only structural change to your app: pass EasyLoading.init() as the builder of your root MaterialApp or CupertinoApp. After the Host is mounted, display calls work from anywhere.

dart
MaterialApp(
  builder: EasyLoading.init(),
  home: const HomePage(),
);

If your app already has a root builder, do not replace it. The README shows composing it through init's builder parameter, so the existing wrapper stays in the tree:

dart
dart
MaterialApp(
  builder: EasyLoading.init(
    builder: (context, child) => ExistingRoot(child: child),
  ),
  home: const HomePage(),
);

With the Host in place, the quick start covers the whole surface in a few lines. Every display and dismissal method returns Future<void> and can be awaited, which matters when you want a success message to finish before navigating away.

dart
await EasyLoading.show(status: 'Loading...');
await EasyLoading.showProgress(0.5, status: 'Downloading...');

await EasyLoading.showSuccess('Completed');
await EasyLoading.showError('Request failed');
await EasyLoading.showInfo('Update available');
await EasyLoading.showToast('Saved');

await EasyLoading.dismiss();

Finally, set the global defaults once during startup rather than repeating them at every call site. The README's configuration example uses the cascade operator on EasyLoading.instance. Note that the defaults shown in the README are not the same as the defaults in the property table: the example sets maskType to none, toastPosition to bottom and displayDuration to two seconds, while the table lists center and 2000 ms as the out-of-the-box values. Read both before assuming what a bare EasyLoading.show() will look like.

dart
EasyLoading.instance
  ..loadingStyle = EasyLoadingStyle.dark
  ..indicatorType = EasyLoadingIndicatorType.fadingCircle
  ..maskType = EasyLoadingMaskType.none
  ..toastPosition = EasyLoadingToastPosition.bottom
  ..displayDuration = const Duration(seconds: 2)
  ..animationDuration = const Duration(milliseconds: 200);

One overlay at a time, and a mask that is off by default

The single shared instance is the design, and it is also the main constraint. There is one EasyLoading.isShow flag and one dismiss method, so a second show call updates the overlay that is already on screen rather than stacking a new one. If your screen needs a blocking spinner and an independent toast visible at the same time, this library does not give you two independent slots. You would be sequencing them, or reaching for a different mechanism for one of the two.

The default mask is the second thing to check. The property table lists maskType as EasyLoadingMaskType.none, which means the application below the overlay keeps receiving input unless you change it. A loading indicator that does not block taps is a real failure mode: a user can start the same request twice while the first spinner is still on screen. Set maskType globally or per call if the operation must not be interrupted, and use the userInteractions property when you need to override whether input reaches the app below.

The default durations are short. displayDuration defaults to 2000 ms and animationDuration to 200 ms according to the table, so a result message disappears quickly unless you pass duration. If your status text is long or localized into a language with longer strings, the panel's textAlign defaults to center and the font size to 15, with contentPadding of 15 vertical and 20 horizontal, so test the layout with your longest real string rather than a placeholder.

Version constraints are the last hard boundary. The README states Dart 3.6.0 or later and below Dart 4.0.0, and Flutter 3.27.0 or later. If you are on an older SDK, the 4.0 line is not available to you, and the README directs you to MIGRATION.md before upgrading from 3.x. The README does not document rollback, so plan the upgrade as a one-way step in a branch.

Compared with the built-in showDialog and SnackBar approach

The alternative most Flutter teams already have is the framework's own primitives: showDialog for a blocking loading state and ScaffoldMessenger with SnackBar for messages. The difference is not visual, it is about where the call has to live. showDialog and SnackBar both take a BuildContext, so calling them from a service class means holding a context or a navigator key somewhere outside the widget tree. Flutter EasyLoading trades that for a single Host at the root and a global instance, which is why the README can say display calls do not require a BuildContext.

The trade is control. With showDialog you get a real route, its own lifecycle and the full dialog API; with Flutter EasyLoading you get one overlay whose appearance is described by a fixed set of properties (loadingStyle, indicatorType, animationStyle, radius, indicatorSize and so on) plus a showCustom method for arbitrary widget content when those properties are not enough. If your loading UI is genuinely bespoke, showCustom or a hand-rolled overlay will fit better than bending the built-in styles. If it is a spinner, a progress bar, three result states and a toast, the global API removes a class of context plumbing that otherwise spreads through the codebase.

Maintenance, licensing and the cost of the 4.0 upgrade

The repository is not archived, and the last push was on 2026-07-30, the same day as the 4.0.2 release. The 4.0 line arrived quickly: 4.0.0 on 2026-07-29, then 4.0.1 and 4.0.2 the following day. Three releases in two days is a normal stabilization pattern after a major bump, and it also means the 4.0 line is young. Budget time for the migration guide rather than assuming a drop-in replacement.

The licence is MIT, which is permissive and places few obligations on how you redistribute the package inside an application. That is a statement about the licence identifier in the repository, not legal advice; check how your own distribution model interacts with attribution requirements.

Upgrade cost is mostly the SDK floor. Dart 3.6.0 or later and Flutter 3.27.0 or later are prerequisites for 4.x, so the real work is usually the surrounding toolchain, not the calls themselves. The API surface in the README is compact (show, showProgress, showSuccess, showError, showInfo, showToast, showCustom, dismiss, plus the callback registration methods), so an application that stayed close to that surface has a small diff. Applications that reached into the configuration instance from many files will have a wider one, since the defaults and the property names are what the migration guide exists to cover.

Editorial conclusion

Adopt flutter_easyloading if you want one global overlay API and you are on Dart 3.6.0 or later with Flutter 3.27.0 or later. Do not adopt it if you are still on 3.x and cannot absorb the migration, or if you need several independent overlays on screen at once, because the API exposes a single shared instance and a single isShow flag. Before committing, read MIGRATION.md, check the pubspec constraint for the 4.0 line, and confirm your root builder can be composed through EasyLoading.init(builder: ...).

Frequently asked questions

How do I install flutter_easyloading?

Run flutter pub add flutter_easyloading, or add flutter_easyloading: ^4.0.2 under dependencies in pubspec.yaml and import package:flutter_easyloading/flutter_easyloading.dart. The README also requires Dart 3.6.0 or later and Flutter 3.27.0 or later.

Can I use flutter_easyloading without a BuildContext?

Yes. The README states that display calls do not require a BuildContext, provided you install the Host once at the root through EasyLoading.init() as the builder of your MaterialApp or CupertinoApp.

How do I know when a flutter_easyloading overlay is dismissed?

Register a dismissal callback with EasyLoading.addDismissCallback and read the EasyLoadingDismissReason, which the README lists as programmatic, tap, timeout, or hostDetached. Remove the callback with EasyLoading.removeDismissCallback when its owner is disposed.

Does flutter_easyloading block taps on the app below the overlay?

Not by default. The property table lists maskType as EasyLoadingMaskType.none, so input reaches the application below unless you change maskType globally or per call, or set userInteractions.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/nslogx-flutter-easyloading.svg)](https://hysenlabs.com/projects/nslogx-flutter-easyloading)