1  comments

Snackbars and toasts are the most used ways of notifying users about something within an app. The problem is that snackbars are not very modifiable and toasts, coming from native Android, are not even present in Flutter by default.

Flash is a highly customizable, powerful and easy-to-use alerting package for Flutter. It offers snackbars, toasts and even dialogs so that you can manage everything using just one API.

Since the Flash package can be used to show toasts, snackbars and dialogs... We're going to build an app that showcases it all! There's also some starter code that needs to be put in place before we start with the tutorial, so feel free to grab it from above if you want to follow along.

Before we begin with the Dart code, let's add a dependency to the pubspec. Aside from the version we're going to use now, there's also 1.4.0-nullsafety, so you're covered for the future when null-safety hits stable.

pubspec.yaml

dependencies:
  flutter:
    sdk: flutter
  flash: ^1.3.1

Toasts

Toasts are short message popups that come from native Android, where they can be shown and remain visible even the app itself is not in the foreground.  In the case of the Flash library, toasts are just regular Flutter widgets that have nothing to do with the native platform and they're visible only as long as the app is visible too.

There are packages that allow you to display the native toasts from the Flutter code. One of them is  fluttertoast.

Let's now write the code that's going to show a toast in the "show toast" button's onPressed callback method that's already created for you in the starter project.

No matter what kind of a popup you're trying to show with the Flash package, you always call showToast method which can take in a bunch of arguments, for example, a duration and also a builder for the widget that'll be displayed.

main.dart

showFlash(
  context: context,
  duration: const Duration(seconds: 4),
  builder: (context, controller) {
    // The displayed toast widget will go here
  },
);

The builder should always return a Flash widget. I'd recommend that you always use the named constructors - Flash.dialog or Flash.bar. They're almost identical but they omit certain constructor parameters that just don't make sense for dialogs or snackbars respectively.

If you think about it, a toast behaves more like a dialog than a snackbar, so let's write the following for the builder:

main.dart

return Flash.dialog(
  controller: controller,
  borderRadius: const BorderRadius.all(Radius.circular(8)),
  backgroundGradient: LinearGradient(
    colors: [Colors.indigo, Colors.deepPurple],
  ),
  // Alignment is only available for the "dialog" named constructor
  alignment: Alignment.bottomCenter,
  margin: const EdgeInsets.only(bottom: 48),
  // Child can be any widget you like, this one just displays a padded text
  child: Padding(
    padding: const EdgeInsets.all(8.0),
    child: Text(
      'This is a toast',
      style: const TextStyle(
        color: Colors.white,
        fontSize: 16,
      ),
    ),
  ),
);

Background, border radius, alignment and even more properties of the "container" that holds the content of the toast/dialog/snackbar are configured in the Flash widget. The actual content, which in this case is just a Padding and a Text is then passed as the child argument.

Showing only one toast at a time

The toast is now successfully displayed but when you tap the button multiple times in rapid succession, you get overlapping toasts! How can we ensure that only one of them is shown at any given moment?

Some libraries come with this functionality built in and even offer something called a message queue which remembers the toasts you wanted to show and then displays them only if the previous one is already hidden. When it comes to the Flash package, we need to implement this functionality on our own.

While we're only going to take a look at not overlapping multiple toasts, you can find a working implementation of a message queue with the Flash library in the official example project.

Showing only one toast at a time is simple. Just create a boolean field _isToastShown in a State and then just don't call showFlash from the button press when the field is true. This is possible because showFlash returns a Future that completes once it's dismissed.

main.dart

class _FlashPageState extends State<FlashPage> {
  bool _isToastShown = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            RaisedButton(
              child: Text('Show Toast'),
              onPressed: () async {
                if (_isToastShown) {
                  return;
                }

                _isToastShown = true;

                await showFlash(
                  ...
                );

                _isToastShown = false;
              },
            ),

            ...

Snackbars

Showing snackbars is almost identical to showing toasts. The main difference is that you use the Flash.bar named constructor so that you can configure properties specific to snackbars. Inside the "show snackbar" button's onPressed callback:

main.dart

showFlash(
  context: context,
  duration: const Duration(seconds: 4),
  builder: (context, controller) {
    return Flash.bar(
      controller: controller,
      backgroundGradient: LinearGradient(
        colors: [Colors.yellow, Colors.amber],
      ),
      // Position is only available for the "bar" named constructor and can be bottom/top.
      position: FlashPosition.bottom,
      // Allow dismissal by dragging down.
      enableDrag: true,
      // Allow dismissal by dragging to the side (and specify direction).
      horizontalDismissDirection:
          HorizontalDismissDirection.startToEnd,
      margin: const EdgeInsets.all(8),
      borderRadius: const BorderRadius.all(Radius.circular(8)),
      // Make the animation lively by experimenting with different curves.
      forwardAnimationCurve: Curves.easeOutBack,
      reverseAnimationCurve: Curves.slowMiddle,
      // While it's possible to use any widget you like as the child,
      // the FlashBar widget looks good without any effort on your side.
      child: FlashBar(
        title: Text(
          'You are seeing a snackbar!',
          style: Theme.of(context).textTheme.headline6,
        ),
        message: Text('This is something'),
        primaryAction: IconButton(
          // This icon's color is by default re-themed to have the primary color
          // from the material theme - blue by default.
          icon: Icon(Icons.ac_unit),
          onPressed: () {},
        ),
        icon: Icon(
          Icons.info,
          // This color is also pulled from the theme. Let's change it to black.
          color: Colors.black,
        ),
        shouldIconPulse: false,
        showProgressIndicator: true,
      ),
    );
  },
);

The child widget could again by anything you want but FlashBar looks good out-of-the-box, so we use it.

Popping the snackbar

As of now, navigating to the previous screen by tapping the back button is possible even while the snackbar is shown.

In order for the snackbar (or any other widget shown by calling showFlash) to be affected by calling Navigator.pop(), we need to set the persistent parameter to false.

main.dart

showFlash(
  context: context,
  duration: const Duration(seconds: 4),
  persistent: false,
  builder: (context, controller) {
    ...
  },
);

Doing only this, however, is not enough. If you tried to show the snackbar now, you'd get the following error message: "overlay can't be the root overlay when persistent is false". Hmmmm.

You see, individual "flashes" are shown as an OverlayEntry within an Overlay (learn more here) so that they can be displayed on top of other widgets. The MaterialApp widget which is at the root of our app contains such an Overlay.  To be able to pop the snackbar though, we need to have a route-specific Overlay, not just the one at the root of the app.

How can we do that? Easy! One way is to wrap the FlashPage (from the starter project) in an Overlay as an OverlayEntry.

main.dart

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: RaisedButton(
          child: Text('Go to the next page'),
          onPressed: () {
            Navigator.of(context).push(
              MaterialPageRoute(
                builder: (context) => Overlay(
                  initialEntries: [
                    OverlayEntry(
                      builder: (context) => FlashPage(),
                    ),
                  ],
                ),
              ),
            );
          },
        ),
      ),
    );
  }
}

Dialogs

It's also possible to show regular-looking dialogs using Flash. In my opinion, you don't gain much from using showFlash compared to calling the default showDialog but you may want to keep everything streamlined using just one library for all of your in-app popup needs.

Whatever the case may be, showing dialogs is rather simple. Call showFlash with persistent set to false (dialogs must be poppable, otherwise, they're really just oversized weird toasts), you also probably don't want to specify any duration to make the dialog indefinite.

The builder should return a Flash constructed with the dialog named constructor.  Since persistent is false, the Flash will figure out that it should display a barrier that will dim the background. 

So, inside onPressed of the "show dialog" button:

main.dart

showFlash(
  context: context,
  // A dialog cannot be persistent - must be poppable.
  persistent: false,
  builder: (context, controller) {
    return Flash.dialog(
      controller: controller,
      borderRadius: const BorderRadius.all(Radius.circular(8)),
      margin: const EdgeInsets.all(8),
      // Again, FlashBar is a perfect candidate for the child widget.
      child: FlashBar(
        message:
            Text('This FlashBar looks like an AlertDialog.'),
        actions: [
          FlatButton(
            onPressed: () {
              controller.dismiss();
            },
            child: Text('OK'),
          ),
        ],
      ),
    );
  },
);

This is how you can display toasts, snackbars and dialogs using the Flash package. The great thing is that they good really good without much effort on your part. No matter what you want to use it for, this package is a good choice.

About the author 

Matt Rešetár

Matt is an app developer with a knack for teaching others. Working as a Flutter freelancer and most importantly developer educator, he doesn't have a lot of free time 😅 Yet he still manages to squeeze in tough workouts 💪

You may also like

Search Bar in Flutter – Logic & Material UI

Flutter Integration Test Tutorial + Firebase Test Lab & Codemagic

  • Thanks a lot!

    Wonder if you have some MVvM & BLOC/RIVERPOD/GETX integration tutorial…
    I’m kind of new to flutter looking for that, not really sure if it’s possible or maybe i’m wrong…

    Greetings from Mexico!

  • {"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}
    >