Open In App

Flutter – Implementing Overlay

Last Updated : 28 Jul, 2021
Improve
Improve
Like Article
Like
Save
Share
Report

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.



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads