Open In App

Flutter – Implementing Overlay

Last Updated : 18 Apr, 2025
Comments
Improve
Suggest changes
Like Article
Like
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 : 

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 :

Property

Description

builder

Takes a widget builder

opaque

Takes a Boolean 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 :

Method

Description

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 :

Method

Description

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';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(debugShowCheckedModeBanner: false, home: Example1());
  }
}

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);
    late 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(
                  'assets/commentCloud.png',
                  colorBlendMode: BlendMode.multiply,
                ),
                Positioned(
                  top: MediaQuery.of(context).size.height * 0.15,
                  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.028,
                            color: Colors.green,
                          ),
                        ),
                      ),
                      SizedBox(width: MediaQuery.of(context).size.width * 0.05),
                      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 1',
          style: TextStyle(fontWeight: FontWeight.bold),
        ),
        backgroundColor: Colors.green,
        foregroundColor: Colors.white,
      ),
      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 the 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 the 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';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(debugShowCheckedModeBanner: false, home: Example2());
  }
}

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);
    late 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(
                  'assets/commentCloud.png',
                  colorBlendMode: BlendMode.multiply,
                ),
                Positioned(
                  top: MediaQuery.of(context).size.height * 0.15,
                  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.028,
                            color: Colors.green,
                          ),
                        ),
                      ),
                      SizedBox(width: MediaQuery.of(context).size.width * 0.05),
                      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 1',
          style: TextStyle(fontWeight: FontWeight.bold),
        ),
        backgroundColor: Colors.green,
        foregroundColor: Colors.white,
      ),
      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);
            },
          ),
        ),
      ),
    );
  }
}

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(
                  'assets/commentCloud.png',
                  colorBlendMode: BlendMode.multiply,
                ),
                Positioned(
                  top: MediaQuery.of(context).size.height * 0.14,
                  left: MediaQuery.of(context).size.width * 0.15,
                  child: Material(
                    color: Colors.transparent,
                    child: Text(
                      'I will disappear in\n 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),
        ),
        backgroundColor: Colors.green,
        foregroundColor: Colors.white,
      ),
      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 have 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';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(debugShowCheckedModeBanner: false, home: Example3());
  }
}

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),
        ),
        backgroundColor: Colors.green,
        foregroundColor: Colors.white,
      ),
      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.



Next Article

Similar Reads