Overlays in Flutter: Getting Started

Learn Flutter’s own way to display overlays like popUps, modals and dialog boxes with the help of popUpRoutes. By Michael Malak.

4 (1) · 1 Review

Download materials
Save for later
Share
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

Adding a Note as an Overlay

When you select add icon in the app bar, you want to push SaveNotePage on top of the displayed list of notes as an overlay. Since you want to control the overlay and add transition animations, you’ll create a custom route instead of relying on Flutter’s MaterialPageRoute.

Creating a Custom PopupRoute

In lib/service/router_service/router_service.dart, append the following class at the end of the file:

// 1
class CustomPopupRoute extends PopupRoute {
  // 2
  CustomPopupRoute({
    required this.builder,
    RouteSettings? settings,
  }) : super(settings: settings);

  final WidgetBuilder builder;
  
  // 3
  @override
  Color get barrierColor => Colors.black54.withAlpha(100);
  @override
  bool get barrierDismissible => true;
  @override
  String get barrierLabel => 'customPopupRoute';

  // 4
  @override
  Duration get transitionDuration => const Duration(milliseconds: 300);

  // 5
  @override
  Widget buildPage(
    BuildContext context,
    Animation<double> animation,
    Animation<double> secondaryAnimation,
  ) => builder(context);

  // 6
  @override
  Widget buildTransitions(
    BuildContext context,
    Animation<double> animation,
    Animation<double> secondaryAnimation,
    Widget child,
  ) {
    return ScaleTransition(
      scale: animation,
      child: FadeTransition(
        opacity: animation,
        child: child,
      ),
    );
  }
}

Here’s a detailed breakdown of the code above:

  1. You created your custom route that extends PopupRoute.
  2. By passing settings, a RouteSettings object to super in the constructor, you pass it to the base PopupRoute class. This ties the custom route to a page in case the settings is a subclass of Page. A page-based route, as opposed to a pageless route, is created from Page.createRoute during Navigator.pages updates.
  3. When defining a route, you implement barrier-specific overrides. barrierColor is the color of the barrier between the route and the previous one. In this case, you made it slightly transparent. barrierDismissible is a Boolean responsible if the route can be dismissed, by clicking outside of its defined bounded box. barrierLabel is the semantic label used for a dismissible barrier.
  4. The transitionDuration is the animation duration of opening the custom route.
  5. Override the buildPage to return the passed builder with the newly created context. This builder contains the widget that you want to add as an overlay.
  6. Specify ScaleTransition and FadeTransition as the animation transitions when opening and closing the custom route.

Now that you created a CustomPopupRoute, you’re ready to use it with SaveNotePage screen.

Using Custom PopupRoute when Adding a Note

While you’re at lib/service/router_service/router_service.dart, replace the implementation at # TODO 3: Create then use CustomPopupRoute with:

case SaveNotePage.route:
  // 1
  return CustomPopupRoute(
    // 2
    builder: (_) => SaveNotePage(
      onNoteSaved: settings.arguments as NoteCallback,
    ),
    // 3
    settings: settings,
  );

Here’s what you did:

  1. Switched the return to CustomPopupRoute instead of MaterialPageRoute. Now you’ll be using your CustomPopupRoute that you created before.
  2. Returned SaveNotePage for the builder property of the route and passed onNoteSaved as the callback with type NoteCallBack from the RouteSettings arguments.
  3. CustomPopupRoute takes a RouteSetting parameter as settings. Provide the settings value as the RouteSettings property to the CustomPopupRoute.

Build and run; it functions as you’d expect when you click the add icon.

Todo 3 Result

Understanding Overlay Widgets

As previously mentioned, it’s most common to use overlays created by the Navigator. The Navigator will manage the visual appearance of its routes, including PopupRoutes, if they’re an overlay. However, Flutter allows you to create an Overlay directly.

Overlay widget is a StatefulWidget that stacks independent child widgets and allows them to float on top of your widget tree. It manages each of its OverlayEntry children very similarly to how the Stack widget works. You use OverlayState to insert OverlayEntrys into the Overlay widget using the insert and insertAll functions.

Note: Overlay‘s main use case is related to being able to insert widgets on top of the pages in an app, as opposed to Stack, that simply displays a stack of widgets.

Opening Note Details Overlay

You want to edit a note as an overlay by tapping on it without pushing the SaveNotePage to the navigation stack. Instead, you’ll use the Overlay widget. First, you’ll create a generic implementation to manage Overlay widget. Then, you’ll use this implementation to display note details as an overlay.

Creating Overlay mixin

You’ll create a shared mixin that will add and remove overlays in Flutter for a cleaner approach.

Note: Mixins are a way to reuse methods or variables among otherwise unrelated classes. To learn more you can check out the Dart Apprentice – Chapter 9: Advanced Classes.

Create a new dart file in lib/ui/_shared/mixin/ called overlay_mixin.dart, and add the following code to it:

import 'package:flutter/material.dart';
import '../utils/app_colors.dart';

// 1
mixin OverlayStateMixin<T extends StatefulWidget> on State<T> {
  // 2
  OverlayEntry? _overlayEntry;

  // 3
  void removeOverlay() {
    _overlayEntry?.remove();
    _overlayEntry = null;
  }

  // 4
  Widget _dismissibleOverlay(Widget child) => Stack(
        children: [
          Positioned.fill(
            child: ColoredBox(
              color: AppColors.barrierColor,
              child: GestureDetector(
                onTap: removeOverlay,
              ),
            ),
          ),
          child,
        ],
      );

  // 5
  void _insertOverlay(Widget child) {

   // 6
   _overlayEntry = OverlayEntry(
      builder: (_) => _dismissibleOverlay(child),
    );

    // 7
    Overlay.of(context)?.insert(_overlayEntry!);
  }

}

This is what the code does:

  1. Creates a new mixin named OverlaysStateMixin that you’ll use with the State class of StatefulWidgets.
  2. Adds a single private nullable variable OverlayEntry. You’ll use this variable to manage the overlay entries. In this mixin, you’ll only manage one overlay.
  3. A void function named removeOverlay that removes the overlayEntry by calling _overlayEntry?.remove() function assigned in the OverlayEntry before setting it to null.
  4. _dismissibleOverlay is a private function that allows for calling removeOverlay() when clicking outside the child boundaries.
  5. A private function you use to display a Widget as an overlay.
  6. You assign OverlayEntry with the dismissable child.
  7. Overlay.of(context) yields OverlayState object which you use to insert your newly assigned OverlayEntry object.

Now that you can insert and remove an overlay using the mixin, you want to expose a public function to toggle it. Add the following to OverlayStateMixin:

// 1
bool get isOverlayShown => _overlayEntry != null;

// 2
void toggleOverlay(Widget child) =>
    isOverlayShown ? removeOverlay() : _insertOverlay(child);

Here you:

  1. Implement a getter method to check if the OverlayEntry is visible by checking if it was not null.
  2. Expose a public function to toggle viewing the single _overlayEntry.

Since you’ll use OverlayStateMixin with StatefulWidgets, you can override some methods to remove the overlay in certain senarios. You add the following to OverlayStateMixin:

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

@override
void didChangeDependencies() {
  removeOverlay();
  super.didChangeDependencies();
}

You override state functions dispose and didChangeDependencies to remove the displayed overlay when they trigger.

Now that you created a shared mixin to manage a single OverlayEntry, you’ll use it to display an overlay that allows editing a note item.