Flutter – Implementing Overlay
Overlays let independent child widgets float visual elements on top of other widgets by inserting them into the overlay’s Stack.
This article discusses the implementation of Overlays in Flutter. To implement Overlay in Flutter we need to know about two Flutter built-in classes OverlayEntry class and the OverlayState class.
OverlayEntry :
Vaguely speaking OverlayEntry is a place in an Overlay that can contain a widget.
Constructor for OverlayEntry class :
OverlayEntry( { required WidgetBuilder builder, bool opaque = false, bool maintainState = false } )
Properties of OverlayEntry class :
- builder: Takes a widget builder.
- opaque: Takes a bool value that decides whether this entry occludes the entire overlay. If an entry claims to be opaque, then, for efficiency, the overlay will skip building entries below that entry unless they have maintainState set.
- maintainState: Takes a bool value and if set true it forcefully builds the occluded entries below an opaque entry.
Methods of OverlayEntry class :
- remove: Removes this entry from the overlay.
OverlayState :
The current state of an Overlay is used to insert OverlayEntries into the overlay.
Methods of OverlayState class :
- debugIsVisible: Checks whether the given OverlayEntry is visible or not and returns a bool.
- insert: Inserts the given OverlayEntry into the Overlay.
- insertAll: Takes a List of OverlayEntries and inserts all the entries into the Overlay. You can also specify the above and below properties to state in which order entries are to be inserted.
- rearrange: Remove all the entries listed in the given List of OverlayEntries, then reinsert them into the overlay in the given order.
I know you are not that much interested in reading theory, so let’s head on to some Examples.
Example 1:
Dart
import 'package:flutter/material.dart' ; class Example1 extends StatefulWidget { const Example1({Key key}) : super(key: key); @override _Example1State createState() => _Example1State(); } class _Example1State extends State<Example1> { void _showOverlay(BuildContext context) async { // Declaring and Initializing OverlayState // and OverlayEntry objects OverlayState overlayState = Overlay.of(context); OverlayEntry overlayEntry; overlayEntry = OverlayEntry(builder: (context) { // You can return any widget you like here // to be displayed on the Overlay return Positioned( left: MediaQuery.of(context).size.width * 0.2, top: MediaQuery.of(context).size.height * 0.3, child: Container( width: MediaQuery.of(context).size.width * 0.8, child: Stack( children: [ Image.asset( 'images/commentCloud.png' , colorBlendMode: BlendMode.multiply, ), Positioned( top: MediaQuery.of(context).size.height * 0.13, left: MediaQuery.of(context).size.width * 0.13, child: Row( children: [ Material( color: Colors.transparent, child: Text( 'This is a button!' , style: TextStyle( fontSize: MediaQuery.of(context).size.height * 0.03, color: Colors.green), ), ), SizedBox( width: MediaQuery.of(context).size.width * 0.18, ), GestureDetector( onTap: () { // When the icon is pressed the OverlayEntry // is removed from Overlay overlayEntry. remove (); }, child: Icon(Icons.close, color: Colors.green, size: MediaQuery.of(context).size.height * 0.025), ) ], ), ), ], ), ), ); }); // Inserting the OverlayEntry into the Overlay overlayState.insert(overlayEntry); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text( 'GeeksForGeeks Example 2' , style: TextStyle(fontWeight: FontWeight.bold), ), ), body: SafeArea( child: Center( child: MaterialButton( color: Colors.green, minWidth: MediaQuery.of(context).size.width * 0.4, height: MediaQuery.of(context).size.height * 0.06, child: Text( 'show Overlay' , style: TextStyle(color: Colors.white), ), onPressed: () { // calling the _showOverlay method // when Button is pressed _showOverlay(context); }, ))), ); } } |
Output:
Explanation:
In this flutter app, I have called a function _showOverlay in the onPressed callback of the MaterialButton. In the _showOverlay function, I have declared and initialized OverlayState and OverlayEntry objects. In the OverlayEntry, I have passed the widgets for a Comment Cloud and it has a Text and an Icon, I have wrapped the Icon with a GestureDetector and on its onTap callback, I have called remove function for the OverlayEntry, which removes this entry from the overlay. You can also make an OverlayEntry remove itself automatically after a certain duration, the next Example addresses that. After the initialization of OverlayEntry I have called insert method for OverlayState and passed in the current OverlayEntry, this adds the Entry to the Overlay.
Example 2:
Dart
import 'package:flutter/material.dart' ; class Example2 extends StatefulWidget { const Example2({Key key}) : super(key: key); @override _Example2State createState() => _Example2State(); } class _Example2State extends State<Example2> { void _showOverlay(BuildContext context) async { // Declaring and Initializing OverlayState // and OverlayEntry objects OverlayState overlayState = Overlay.of(context); OverlayEntry overlayEntry; overlayEntry = OverlayEntry(builder: (context) { // You can return any widget you like // here to be displayed on the Overlay return Positioned( left: MediaQuery.of(context).size.width * 0.2, top: MediaQuery.of(context).size.height * 0.3, child: Container( width: MediaQuery.of(context).size.width * 0.8, child: Stack( children: [ Image.asset( 'images/commentCloud.png' , colorBlendMode: BlendMode.multiply, ), Positioned( top: MediaQuery.of(context).size.height * 0.13, left: MediaQuery.of(context).size.width * 0.13, child: Material( color: Colors.transparent, child: Text( 'I will disappear in 3 seconds.' , style: TextStyle( fontSize: MediaQuery.of(context).size.height * 0.025, color: Colors.green), ), ), ), ], ), ), ); }); // Inserting the OverlayEntry into the Overlay overlayState.insert(overlayEntry); // Awaiting for 3 seconds await Future.delayed(Duration(seconds: 3)); // Removing the OverlayEntry from the Overlay overlayEntry. remove (); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text( 'GeeksForGeeks Example 2' , style: TextStyle(fontWeight: FontWeight.bold), ), ), body: SafeArea( child: Center( child: MaterialButton( color: Colors.green, minWidth: MediaQuery.of(context).size.width * 0.4, height: MediaQuery.of(context).size.height * 0.06, child: Text( 'show Overlay' , style: TextStyle(color: Colors.white), ), onPressed: () { // calling the _showOverlay method // when Button is pressed _showOverlay(context); }, ))), ); } } |
Output:
Explanation:
In this example, I have called a function _showOverlay in the onPressed callback of the MaterialButton. In the _showOverlay function, I have declared and initialized OverlayState and OverlayEntry objects. In the OverlayEntry, I have passed the widgets for a Comment Cloud and it displays a Text. After the initialization of OverlayEntry I have called insert method for OverlayState and passed in the current OverlayEntry, this adds the Entry to the Overlay. After that I havehttps://github.com/curiousyuvi/overlay_implementation awaited on Future.delayed to make a delay of 3 seconds and then called remove method to remove the current OverlayEntry from the Overlay. This makes the entry appear for 3 seconds and then it disappears.
Example 3:
Dart
import 'package:flutter/material.dart' ; class Example3 extends StatefulWidget { const Example3({Key key}) : super(key: key); @override _Example3State createState() => _Example3State(); } class _Example3State extends State<Example3> { void _showOverlay(BuildContext context) async { // Declaring and Initializing OverlayState and // OverlayEntry objects OverlayState overlayState = Overlay.of(context); OverlayEntry overlayEntry1; OverlayEntry overlayEntry2; OverlayEntry overlayEntry3; overlayEntry1 = OverlayEntry(builder: (context) { // You can return any widget you like here // to be displayed on the Overlay return Positioned( left: MediaQuery.of(context).size.width * 0.1, top: MediaQuery.of(context).size.height * 0.3, child: ClipRRect( borderRadius: BorderRadius.circular(20), child: Container( padding: EdgeInsets.all(MediaQuery.of(context).size.height * 0.02), width: MediaQuery.of(context).size.width * 0.8, height: MediaQuery.of(context).size.height * 0.1, color: Colors.pink.withOpacity(0.3), child: Material( color: Colors.transparent, child: Text( 'I will disappear in 3 seconds' , style: TextStyle( fontSize: MediaQuery.of(context).size.height * 0.03, fontWeight: FontWeight.bold, color: Colors.white)), ), ), ), ); }); overlayEntry2 = OverlayEntry(builder: (context) { // You can return any widget you like here // to be displayed on the Overlay return Positioned( left: MediaQuery.of(context).size.width * 0.1, top: MediaQuery.of(context).size.height * 0.5, child: ClipRRect( borderRadius: BorderRadius.circular(20), child: Container( padding: EdgeInsets.all(MediaQuery.of(context).size.height * 0.02), width: MediaQuery.of(context).size.width * 0.8, height: MediaQuery.of(context).size.height * 0.1, color: Colors.blue.withOpacity(0.3), child: Material( color: Colors.transparent, child: Text( 'I will disappear in 5 seconds' , style: TextStyle( fontSize: MediaQuery.of(context).size.height * 0.03, fontWeight: FontWeight.bold, color: Colors.white)), ), ), ), ); }); overlayEntry3 = OverlayEntry(builder: (context) { // You can return any widget you like // here to be displayed on the Overlay return Positioned( left: MediaQuery.of(context).size.width * 0.1, top: MediaQuery.of(context).size.height * 0.7, child: ClipRRect( borderRadius: BorderRadius.circular(20), child: Container( padding: EdgeInsets.all(MediaQuery.of(context).size.height * 0.02), width: MediaQuery.of(context).size.width * 0.8, height: MediaQuery.of(context).size.height * 0.1, color: Colors.green.withOpacity(0.3), child: Material( color: Colors.transparent, child: Text( 'I will disappear in 7 seconds' , style: TextStyle( fontSize: MediaQuery.of(context).size.height * 0.03, fontWeight: FontWeight.bold, color: Colors.white)), ), ), ), ); }); // Inserting the OverlayEntry into the Overlay overlayState.insertAll([overlayEntry1, overlayEntry2, overlayEntry3]); // Awaiting for 3 seconds await Future.delayed(Duration(seconds: 3)); // Removing the first OverlayEntry from the Overlay overlayEntry1. remove (); // Awaiting for 2 seconds more await Future.delayed(Duration(seconds: 2)); // Removing the second OverlayEntry from the Overlay overlayEntry2. remove (); // Awaiting for 2 seconds more await Future.delayed(Duration(seconds: 2)); // Removing the third OverlayEntry from the Overlay overlayEntry3. remove (); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text( 'GeeksForGeeks Example 3' , style: TextStyle(fontWeight: FontWeight.bold), ), ), body: SafeArea( child: Center( child: MaterialButton( color: Colors.green, minWidth: MediaQuery.of(context).size.width * 0.4, height: MediaQuery.of(context).size.height * 0.06, child: Text( 'show Overlay' , style: TextStyle(color: Colors.white), ), onPressed: () { //calling the _showOverlay method // when Button is pressed _showOverlay(context); }, ))), ); } } |
Output:
Explanation:
In this example, I have called a function _showOverlay in the onPressed callback of the MaterialButton. In the _showOverlay function, I have declared and initialized OverlayState and three OverlayEntry objects. In these OverlayEntries, I have different colored Containers and they all display a Text. After the initialization of OverlayEntries I have called insertAll method for OverlayState and passed in the List of OverlayEntries, this adds all the Entries to the Overlay. After that I have awaited on Future.delayed to make a delay of 3 seconds and then called remove method to remove the first OverlayEntry from the Overlay and then similarly I have delayed for two seconds then called remove for the second OverlayEntry and then again delayed for 2 seconds and called remove for the third and last OverlayEntry, this makes the OverlayEntries disappear one after another.
Please Login to comment...