Flutter - Using OverlayPortal Widget Examples

This tutorial shows you how to use the OverlayPortal widget in Flutter.

Flutter allows us to float widgets on top of other widgets by using Overlay. An Overlay itself can be shown or hidden conditionally. If you want to easily show or hide a widget on an Overlay, you can use the OverlayPortal widget, which is used to render its overlay child on an Overlay. It takes a controller that can be used to programmatically manage the visibility of the Overlay. Below are the usage examples.

Using OverlayPortal

To use the OverlayPortal widget, you need to call the constructor.

  OverlayPortal({
    Key? key,
    required OverlayPortalController controller,
    required Widget Function(BuildContext) overlayChildBuilder,
    Widget? child,
  })

There are two required arguments. The first one is controller whose type is OverlayPortalController. You need to pass an instance of OverlayPortalController. It allows you to programmatically show or hide the overlay as well as getting whether it's currently shown.

The other required argument is overlayChildBuidler. You have to pass a function which has a parameter of type BuildContext. The function needs to return a widget to be displayed as the overlay. You can return any widget. The main challenge is how to render the widget on the desired position which is going to be explained later.

Beside those two required arguments, you can pass a Widget as the child argument which will be set as the child of the OverlayPortal's parent. You can also pass the Key just like most other Flutter widgets.

First, you have to create an instance of the controller. It can be done by calling the constructor. You can store the instance in a state variable.

  final OverlayPortalController _overlayPortalController = OverlayPortalController();

The controller also has an optional argument that allows you to pass a string value as the debugLabel argument.

  final OverlayPortalController _overlayPortalController = OverlayPortalController(
    debugLabel: 'My Controller',
  );

Then, create the OverlayPortal widget and pass the controller instance as the controller argument. We also have to pass a builder function that returns the overlay widget as the overlayChildBuilder argument.

In the example below, we create a button that can be used to toggle the visibility of the overlay. To check whether the overlay is currently visible, access the isShowing property of the controller. The controller has show() and hide() methods that can be used to control whether the overlay should be visible. We also pass a child argument to the OverlayPortal widget to show a text on the button.

  ElevatedButton(
    onPressed: () {
      _overlayPortalController.isShowing
        ? _overlayPortalController.hide()
        : _overlayPortalController.show();
    },
    child: OverlayPortal(
      controller: _overlayPortalController,
      overlayChildBuilder: (BuildContext context) {
        return const Text('This is overlay', style: TextStyle(color: Colors.black));
      },
      child: const Text('Show/Hide Overlay'),
    ),
  )

Below is the output:

Flutter - OverlayPortal

In the code above, we don't explicitly specify where to place the overlay widget. As you can see from the output, when visible, it's rendered at the top of the screen.

Adjust Overlay Widget Position

Unfortunately, Flutter doesn't automatically position the overlay widget according to the position of the parent widget. As a result, it may be rendered far from its parent. If you need to place the overlay widget relative to its parent, you may need to get the parent's position first. You can read our tutorial about how to get the size and position of a widget in Flutter.

Basically, you need to create a GlobalKey and set it as the key of the parent widget. Then, get the RenderBox, which allows you to get the offset of the parent widget.

  final GlobalKey _buttonKey = GlobalKey();

  Offset _getButtonOffset() {
    final RenderBox renderBox = _buttonKey.currentContext?.findRenderObject() as RenderBox;

    return renderBox.localToGlobal(Offset.zero);
  }

Below is the updated example where the offset of the overlay widget is set using a Positioned widget based on the offset of the parent widget.

  ElevatedButton(
    key: _buttonKey,
    onPressed: () {
      _overlayPortalController.isShowing
        ? _overlayPortalController.hide()
        : _overlayPortalController.show();
    },
    child: OverlayPortal(
      controller: _overlayPortalController,
      overlayChildBuilder: (BuildContext context) {
        // return const Text('This is overlay', style: TextStyle(color: Colors.black));
        return Positioned(
          top: _getButtonOffset().dy + 60,
          right: _getButtonOffset().dx,
          child: Container(
            // color: Colors.teal,
            decoration: BoxDecoration(
              color: Colors.teal,
              borderRadius: BorderRadius.circular(15.0),
            ),
            child: const Padding(
              padding: EdgeInsets.all(5.0),
              child: Text('This is overlay', style: TextStyle(color: Colors.white)),
            ),
          ),
        );
      },
      child: const Text('Show/Hide Overlay'),
    ),
  )

Output:

Flutter - OverlayPortal - Set Position

OverlayPortal Parameters

  • Key? key: The widget's key, used to control how a widget is replaced with another.
  • required OverlayPortalController controller: The controller that can be used to control the overlay widget.
  • required Widget Function(BuildContext) overlayChildBuilder: A function that returns a widget to be displayed as the overlay.
  • Widget? child: A widget that will be set as the child of the OverlayPortal's parent.

Summary

The OverlayPortal widget is suitable if you need to display an Overlay that can be shown or hidden. It depends on an OverlayPortalController which can be used to set the visibility of the Overlay. The widget that will be shown on the Overlay is built using a function passed as the overlayChildBuilder. You can also pass a widget as the child argument to be set as the child of the OverlayPortal's parent widget