Overlays in Flutter: Getting Started [FREE]
source link: https://www.raywenderlich.com/33302203-overlays-in-flutter-getting-started
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Overlays in Flutter: Getting Started
Learn Flutter’s own way to display overlays like popUps, modals and dialog boxes with the help of popUpRoutes.
Version
As a developer, you’re always looking for ways to build visually appealing apps that provide an excellent experience for your users. At some point, you’ll have to work with overlays in Flutter that display on top of your widget tree, like modals, dialogs, or popups. In this article, you’ll learn how to show overlays in Flutter.
Flutter has different types of routes; you already use PageRoute
s to transition between pages. In this tutorial, you’ll learn more about PopupRoute
s and how to display overlays in flutter, like dialogs and modals.
You’ll learn more about ready-to-use public functions that Flutter provides to show dialogs and menus. These functions show route-aware overlays, so you dismiss them by pressing the system back button. However, you’ll also learn how to create your own custom way of showing your overlays when you want to have more control.
By the end of this tutorial, you’ll learn:
- What overlays are and how they work.
- When to use different types of overlays in Flutter.
- How to work with different types of overlays.
Getting Started
Download the starter project by clicking the Download Materials button at the top or bottom of the tutorial.
You’ll work on the Simply Noted app, a single-page app that displays a list of notes. Here’s what you’ll be able to do using overlays in flutter:
- Sort notes: View a popup to sort notes by their date of creation.
- Create and modify a note: Open an overlay to create or modify a note.
- Confirm deleting a note: View a confirmation dialog before deleting a note.
- Confirm editing a note: Show a confirmation overlay to confirm editing a note.
Here’s what the app will look like when you’re done:
Now, it’s time to take a look at the starter project.
Setting up the Starter Project
The starter project already contains the logic to save, edit the notes in the cache and retrieve them from the cache.
This tutorial uses Android Studio 2021.1.1. Some of the screenshots are specific to it, but you can also follow along with Visual Studio Code or IntelliJ IDEA.
Open Android Studio and select Open an Existing Project. Then, select the starter folder from the downloaded materials.
Open pubspec.yaml and click the Pub get tab that appears in your IDE, or run the command flutter pub get
in the terminal to get the packages:
For this tutorial, the most important files in the project are:
- lib/ui/notes/widgets/sort_notes_icon_widget.dart: The IconButton widget that displays the sort icon.
- lib/ui/notes/widgets/delete_note_icon_widget.dart: The IconButton widget that displays the delete icon.
-
lib/service/router_service/router_service.dart: The service responsible for routing and generating
MaterialPageRoute
s. - lib/ui/notes/widgets/note_item_widget.dart: The widget class representing a note in the note list.
Now open lib/main.dart. Then build and run to see the app on your target emulator or device. The app launches with a screen of populated notes:
Now that you know what the starter project contains, you’ll take a deeper look at what overlays are and how you’ll display overlays in Flutter.
Understanding Overlays
Overlays, simply put, are floating widgets on top of other widget children in your app. Flutter has different ways of showing overlays depending on your needs.
The base class for all the routes in Flutter is Route
. It’s an abstract class that defines an interface for the Navigator
. Route
s define methods for handling state change, changes in the stack, and callbacks for when the route is popped.
For Dialog
s and Modal
s, it’s better to keep them consistent and aware of the route. Flutter will then allow you to push an implementation for an abstract PopupRoute
to the Navigator
using global accessible methods like showDialog
, showMenu
and showModalBottomSheet
.
Viewing the Pop-up Menu to Sort Notes
Instead of having two icons on the app bar for sorting the notes, you want to have one sort icon that displays a popup menu. Flutter provides you with PopupMenuButton
; which is a widget that uses showMenu
under the hood.
Start by going to lib/ui/notes/widgets/sort_notes_icon_widget.dart and add the following import at the top of the file:
import '../../../data/models/sort_type_model.dart';
Now, you can replace the body of the build
method with:
// 1 return PopupMenuButton<SortType>( // 2 icon: const Icon(Icons.sort), // 3 onSelected: (sortType) { // 4 switch (sortType) { case SortType.oldestFirst: sortNotesByOldestFirst(); break; case SortType.newestFirst: sortNotesByNewestFirst(); break; } }, // 5 itemBuilder: (context) => [ const PopupMenuItem( child: Text('Newest first'), value: SortType.newestFirst, ), const PopupMenuItem( child: Text('Oldest first'), value: SortType.oldestFirst, ), ], );
Here’s the breakdown of the code:
- The build method returns the
PopupMenuButton
widget which has a type ofSortType
.SortType
is anenum
specifying different types of sorting present in the file that you imported at the top. - You provide a sort icon for the
PopupMenuButton
to display. -
onSelected
is acallback
function that you pass, and is triggered when you click the button. It returns an object of typeSortType
. - Implement a switch case for the
onSelected
callback which takes asortType
as a parameter. In case of having aSortType.oldestFirst
, you sort the notes by the oldest, and in case of having aSortType.newestFirst
, you sort by the newest. - A builder that builds the items inside of the popup menu. You display a list of two
PopupMenuItem
s, and provide each with a different value ofSortType
.
Build and run; you’ll find that the two sort icons on the app bar have been replaced with the PopupMenuButton
.
Now you know how to use PopupMenuButton
. Next, you’ll dig a bit deeper and use showDialog
directly.
Showing Delete Confirmation Dialog
When deleting a note, you want to show a confirmation dialog to make sure the user wants to delete the note. To do that, head to lib/ui/notes/widgets/delete_note_icon_widget.dart. Add the following code after the build
method to DeleteNoteIconWidget
:
Widget buildConfirmationDialog(BuildContext context) { // 1 final cancelIcon = IconButton( icon: const Icon(Icons.close), onPressed: () => Navigator.pop(context), ); // 2 final confirmDeleteIcon = IconButton( icon: const Icon(Icons.delete_rounded), onPressed: () { onDelete(); Navigator.pop(context); }, ); // 3 return AlertDialog( title: const Text('Warning'), content: const Text('Are you sure you want to delete this note?'), actions: [cancelIcon, confirmDeleteIcon], ); }
Here you:
- Create a cancel
IconButton
that pops the dialog when tapped. Since the dialog will be on top of the navigation stack, closing it is possible usingNavigator.pop
- Implement confirm deletion
IconButton
. When you tap this IconButton, you callonDelete()
function which deletes the selected note and then pop’s the dialog. - Return
AlertDialog
with bothIconButton
s,cancelIcon
andconfirmDeleteIcon
as list of actions.
Now, replace your build
method with the following code:
// 1 return IconButton( icon: const Icon(Icons.delete), // 2 onPressed: () => showDialog( context: context, builder: buildConfirmationDialog, ), );
Here’s what’s happening above:
- You return an
IconButton
that displays a delete icon. - When you tap the button, it triggers
showDialog
and builds the previously createdAlertDialog
inbuildConfirmationDialog
widget.
Build and run the app to check that when you tap on the delete icon, and you get a confirmation dialog.
Congratulations! You’ve successfully built an overlay in Flutter: you deserve a pat on the back.
Now that you understand how to use basic methods for displaying overlays in Flutter, let’s dive deeper to understand PopupRoute
.
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:
- You created your custom route that extends
PopupRoute
. - By passing
settings
, aRouteSettings
object tosuper
in the constructor, you pass it to the basePopupRoute
class. This ties the custom route to a page in case thesettings
is a subclass ofPage
. A page-based route, as opposed to a pageless route, is created fromPage.createRoute
duringNavigator.pages
updates. - 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. - The
transitionDuration
is the animation duration of opening the custom route. - Override the
buildPage
to return the passed builder with the newly createdcontext
. This builder contains the widget that you want to add as an overlay. - Specify
ScaleTransition
andFadeTransition
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:
- Switched the return to
CustomPopupRoute
instead ofMaterialPageRoute
. Now you’ll be using yourCustomPopupRoute
that you created before. - Returned
SaveNotePage
for thebuilder
property of the route and passedonNoteSaved
as the callback with typeNoteCallBack
from theRouteSettings
arguments. - CustomPopupRoute takes a
RouteSetting
parameter as settings. Provide thesettings
value as theRouteSettings
property to theCustomPopupRoute
.
Build and run; it functions as you’d expect when you click the add icon.
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 PopupRoute
s, 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 OverlayEntry
s into the Overlay
widget using the insert
and insertAll
functions.
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.
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:
- Creates a new
mixin
named OverlaysStateMixin that you’ll use with theState
class ofStatefulWidget
s. - 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. - A void function named
removeOverlay
that removes the overlayEntry by calling_overlayEntry?.remove()
function assigned in theOverlayEntry
before setting it to null. -
_dismissibleOverlay
is a private function that allows for callingremoveOverlay()
when clicking outside the child boundaries. - A private function you use to display a
Widget
as an overlay. - You assign
OverlayEntry
with the dismissablechild
. -
Overlay.of(context)
yieldsOverlayState
object which you use to insert your newly assignedOverlayEntry
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:
- Implement a getter method to check if the
OverlayEntry
is visible by checking if it was not null. - Expose a public function to toggle viewing the single
_overlayEntry
.
Since you’ll use OverlayStateMixin
with StatefulWidget
s, 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.
Using Overlay Mixin to Edit a Note
You want to show SaveNotePage
widget as an overlay when you tap on a note item. This page will let you edit and save the note.
To achieve this, first go to lib/ui/notes/widgets/note_item_widget.dart, and at the top of the page add an import of your newly created overlay mixin:
import '../../_shared/mixin/overlay_mixin.dart';
Now, you can use the mixin with the _NoteItemWidgetState
as the following:
class _NoteItemWidgetState extends State<NoteItemWidget> with OverlayStateMixin { ... }
Here, you are extending your _NoteItemWidgetState
class with state of NoteItemWidget
and adding the OverlayStateMixin
with a with
keyword.
Replace the code at /// TODO 4: Create and use OverlayMixin to show SaveNotePage as an overlay
with:
void onNoteTap() { // 1 toggleOverlay( SaveNotePage( // 2 noteToEdit: note, // 3 onNoteSaved: (editedNote) { widget.onEdit(editedNote); removeOverlay(); }, ), ); }
Here’s the breakdown of the code:
- In the
onNoteTap()
function you call thetoggleOverlay
to displaySaveNotePage
as an overlay. - You pass the displayed note as an argument to
noteToEdit
. - You implement callback function when you tap the save button which takes the
editedNote
as a parameter. Call thewidget.onEdit
function which will save theeditedNote
that you’ve passed as a parameter. This will save the notes and then remove the overlay by callingremoveOverlay
function
Build and run. When you click a note item in the list, you’ll see the edit note overlay.
The edit overlay works as expected, but instead of closing the overlay when you tap your device’s back button, the app closes. You’ll solve this next.
Removing the Overlay on Native Back Press
Since you’re not using the Navigator
to manage your overlay, the edit note overlay is not closed when you tap back on your device, even though you expect the modal to be closed.
While you’re in lib/ui/notes/widgets/note_item_widget.dart, replace the build
function at /// TODO 5: Use [WillPopScope] and [isOverlayShown] to prevent popping the page when used with the Overlay
with:
// 1 @override Widget build(BuildContext context) => WillPopScope( onWillPop: () async { // 2 if (isOverlayShown) { removeOverlay(); return false; } return true; }, child: buildListTile(), );
Here’s what you did:
- Wrap
buildListTile()
withWillPopScope
. The WillPopScope is triggered when a pop action is taking place. This has a methodonWillPop
which pop’s a widget if the return istrue
and prevents the pop if the return isfalse
. - Implement
onWillPop
callback that vetos attempts to close theRoute
. WhenisOverlayShown
is true, you remove the overlay byremoveOverlay
function and prevent closing the route by returning afalse
value, otherwise, you allow closing it.
Build and run. Now, when you click back, the edit note overlay should close.
Overlay
widget under the hood.Confirming Before Editing Notes
Now that you have already viewed an overlay to edit a note item, you want to show another overlay on top of it to confirm editing before saving it to the cache.
Head to lib/ui/save_note/save_note_page.dart, and add the following imports at the top of the file:
import '../_shared/mixin/overlay_mixin.dart'; import 'widgets/save_note_confirmation_widget.dart';
You can now add OverlayStateMixin
to _SaveNotePageState
as follows:
class _SaveNotePageState extends State<SaveNotePage> with OverlayStateMixin {
Add the following getters to the body of code>_SaveNotePageState:
// 1 bool get hasChanges { // 2 final isHeaderChanged = widget.noteToEdit?.header != note.header; final isTextChanged = widget.noteToEdit?.text != note.text; return isHeaderChanged || isTextChanged; } // 3 bool get isEditMode => widget.noteToEdit != null;
- Add a getter to get a
bool
if the note has been edited. - Detect if either the header or the text body has been modified.
- If
widget.noteToEdit
is passed to the widget, then it’s in edit mode.
Replace the code beneath /// TODO 6: Check page has edits and show confirmation overlay before saving the edits
with:
// 1 if (isEditMode && hasChanges) { toggleOverlay( SaveNoteConfirmationWidget( // 2 onConfirm: () => widget.onNoteSaved(note), onCancel: removeOverlay, ), ); } else { // 3 widget.onNoteSaved(note); }
Here’s what’s happening above:
- In the conditionals, if the note is in edit mode and has been modified, you show an overlay displaying
SaveNoteConfirmationWidget
. This has two buttons; one to confirm the save and other to cancel the save. - When you tap confirm button, you save the note by calling the
onNoteSaved
callback function and pass the edited note as a parameter. When you tap cancel, you remove the overlay by calling theremoveOverlay
function. - In case you’re not in edit mode or the note has changes, you trigger
onNoteSaved
callback, the else part of the conditionals.
Build and run. When you edit a note and modify the text, you get a confirmation overlay before you save the edits.
Congratulations again, you have successfully created overlays in Flutter that you can use accordingly.
Where to Go From Here?
Download the completed project files by clicking the Download Materials button at the top or bottom of the tutorial.
You now have a deeper understanding of overlays in Flutter and, more importantly, when and how to use them. When you find the need, you can create your own PopupRoute
or implement your own way of handling the Overlay
widget.
Check out the following links to learn more about some of the concepts in this tutorial:
We hope you enjoyed this tutorial. If you have any questions or comments, please join the forum discussion below!
raywenderlich.com Weekly
The raywenderlich.com newsletter is the easiest way to stay up-to-date on everything you need to know as a mobile developer.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK