Motivation
Flutter comes with two classes for manipulating “overlays”:
But OverlayEntry is very awkward to use. As opposed to most of the framework, OverlayEntry is not a widget (which comes with a nice and clean declarative API).
Instead, is uses an imperative API. This comes with a few drawbacks:
- a widget’s life-cycle (like
initState
) cannot add/remove synchronously an OverlayEntry.This means the first rendering of our entry is effectively one frame late. - It is difficult to align an OverlayEntry around a non-overlay widget (for example for a contextual menu).We basically have to do everything ourselves, usually needing an addPostFrameCallback which, again, makes the rendering of our overlay one frame late.
That’s where portal
comes into play.
This library is effectively a reimplementation of Overlay/OverlayEntry, under the name Portal/PortalEntry (the name that React uses for overlays) while fixing all the previously mentioned issues.
Install
First, you will need to add portal
to your pubspec.yaml
:
dependencies: flutter: sdk: flutter flutter_portal: ^0.0.1
Then, run flutter packages get
in your terminal.
Usage
To use portal
, we have to rely on two widgets:
- Portal, the equivalent of Overlay.This widget will need to be inserted above the widget that needs to render under your overlays.If you want to display your overlays on the top of everything, a good place to insert that Portal is above
MaterialApp
:Portal( child: MaterialApp( … ) );(works forCupertinoApp
too)This way Portal will render above everything. But you could place it somewhere else to change the clip behavior. - PortalEntry is the equivalent of OverlayEntry.As opposed to OverlayEntry, using
portal
then PortalEntry is a widget, and is therefore placed inside your widget tree (so thebuild
method).Consider the following OverlayEntry example:class Example extends StatefulWidget { const Example({Key key, this.title}) : super(key: key); final String title; @override _ExampleState createState() => _ExampleState(); } class _ExampleState extends State<Example> { OverlayEntry entry; @override void initState() { super.initState(); entry = OverlayEntry( builder: (context) { return Text(widget.title); }, ); SchedulerBinding.instance.addPostFrameCallback((_) { Overlay.of(context).insert(entry); }); } @override void dispose() { SchedulerBinding.instance.addPostFrameCallback((_) { entry.remove(); }); super.dispose(); } @override void didUpdateWidget(Example oldWidget) { super.didUpdateWidget(oldWidget); SchedulerBinding.instance.addPostFrameCallback((_) { entry.markNeedsBuild(); }); } @override Widget build(BuildContext context) { return const Text(‘whatever’); } }Using PortalEntry instead, we would write:class Example extends StatelessWidget { const Example({Key key, this.title}) : super(key: key); final String title; @override Widget build(BuildContext context) { return PortalEntry( portal: Align( alignment: Alignment.topLeft, child: Text(title) ), child: const Text(‘whatever’), ); } }These two examples are identical in behavior:- When mounting our
Example
widget, an overlay is added, which is later removed when the widget is removed from the tree. - the content of that overlay is a
Text
, that may change over time based on atitle
variable.
Example
widget immediatly inserts/updates the overlay, whereas using OverlayEntry the update is late by one frame. - When mounting our
Aligning the overlay around a widget
Sometimes, we want to align our overlay around another widget. PortalEntry comes with built-in support for this kind of feature.
For example, consider that we have a Text
centered in our app:
Center( child: Text('whatever'), )
If we wanted to add an overlay that is aligned on the top center of our Text
, we would write:
Center( child: PortalEntry( portalAnchor: Alignment.bottomCenter, childAnchor: Alignment.topCenter, portal: Card(child: Text('portal')), child: Text('whatever'), ), )
This will align the top-center of child
with the bottom-center of portal
, which renders the following:
Download Flutter Overlay/OverlayEntry widget source code on GitHub
https://github.com/rrousselGit/flutter_portal
Provides the list of the opensource Flutter apps collection with GitHub repository.