Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to overlay a widget on top of a flutter App?

People also ask

How do you overlay widgets on Flutter?

In Flutter, the overlay lets you print visual elements on top of other widgets by inserting them into the overlay's stack. You insert a widget into the overlay using an OverlayEntry and you use Positioned and AnimatedPositioned to choose where the entry is positioned within the overlay.

How do I overlay one widget over another Flutter?

The Overlay in Flutter makes it easy to create visual elements on top of other widgets by adding them to the Overlay's stack. OverlayEntry is used to insert a widget into the Overlay, then Positioned or AnimatedPositioned is used to determine where it will enter within the Overlay.

What is an overlay widget?

The Overlay widget uses a custom stack implementation, which is very similar to the Stack widget. The main use case of Overlay is related to navigation and being able to insert widgets on top of the pages in an app. To simply display a stack of widgets, consider using Stack instead.


Maybe a more optimal way exists, but as an option this is an example with two pages, local navigator and Overlay.

import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final _navigatorKey = GlobalKey<NavigatorState>();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: WillPopScope(
        onWillPop: () async => !await _navigatorKey.currentState.maybePop(),
        child: LayoutBuilder(
          builder: (context, constraints) {
            WidgetsBinding.instance.addPostFrameCallback((_) => _insertOverlay(context));
            return Navigator(
              key: _navigatorKey,
              onGenerateRoute: (RouteSettings settings) {
                switch (settings.name) {
                  case '/page2':
                    return MaterialPageRoute(builder: (_) => Page2());
                  default:
                    return MaterialPageRoute(builder: (_) => Page1(_navigatorKey));
                }
              },
            );
          },
        ),
      ),
    );
  }

  void _insertOverlay(BuildContext context) {
    return Overlay.of(context).insert(
      OverlayEntry(builder: (context) {
        final size = MediaQuery.of(context).size;
        print(size.width);
        return Positioned(
          width: 56,
          height: 56,
          top: size.height - 72,
          left: size.width - 72,
          child: Material(
            color: Colors.transparent,
            child: GestureDetector(
              onTap: () => print('ON TAP OVERLAY!'),
              child: Container(
                decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.redAccent),
              ),
            ),
          ),
        );
      }),
    );
  }
}

class Page1 extends StatelessWidget {
  final GlobalKey<NavigatorState> navigatorKey;

  Page1(this.navigatorKey);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.green[200],
      appBar: AppBar(title: Text('Page1')),
      body: Container(
        alignment: Alignment.center,
        child: RaisedButton(
          child: Text('go to Page2'),
          onPressed: () => navigatorKey.currentState.pushNamed('/page2'),
        ),
      ),
    );
  }
}

class Page2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.yellow[200],
      appBar: AppBar(title: Text('back to Page1')),
      body: Container(
        alignment: Alignment.center,
        child: Text('Page 2'),
      ),
    );
  }
}

enter image description here


After reading the comments, find github-repo-link

  1. created an overlay that will sit on top of everything
  2. that can be called from anywhere.
  3. just 4 easy steps to follow

flutterflutter-layout

enter image description here

STEP-1: in main.dart:

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Stack( <-- using stack
        children: [
          MaterialApp(
            title: 'Flutter Demo',
            theme: ThemeData(
              primarySwatch: Colors.blue,
              visualDensity: VisualDensity.adaptivePlatformDensity,
            ),
            home: MyHomePage(title: 'Flutter Demo Home Page'),
          ),
         OverlayView(),<-- my overlay widget
        ],
      ),
    );
  }
}

STEP-2: OverLayView.dart

class OverlayView extends StatelessWidget {
  const OverlayView({
    Key key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<bool>( <--- IMP , using ValueListenableBuilder for showing/removing overlay
      valueListenable: Loader.appLoader.loaderShowingNotifier,
      builder: (context, value, child) {
        if (value) {
          return yourOverLayWidget(); <-- your awesome overlay 
        } else {
          return Container();
        }
      },
    );
  }

STEP-3: loder_controller.dart (to show/hide)

class Loader {
  static final Loader appLoader = Loader(); <-- singleton
  ValueNotifier<bool> loaderShowingNotifier = ValueNotifier(false);
  ValueNotifier<String> loaderTextNotifier = ValueNotifier('error message');

  void showLoader() { <-- using to show from anywhere
    loaderShowingNotifier.value = true;
  }

  void hideLoader() { <-- using to hide from anywhere
    loaderShowingNotifier.value = false;
  }

  void setText({String errorMessage}) { <-- using to change error message from anywhere 
    loaderTextNotifier.value = errorMessage;
  }

  void setImage() { <-- DIY
    // same as that of setText //
  }
}

FINAL STEP-4: show/hide loder

I'm showing it, on boilerplate code of increment method to show the loader

 void _incrementCounter() async {
    Loader.appLoader.showLoader(); <-- show loder 
    Loader.appLoader.setText(errorMessage: 'this is custom error message');<-- set custom message 
    await Future.delayed(Duration(seconds: 5)); <-- im hiding it after 5 sec
    Loader.appLoader.hideLoader(); <-- do whatever you want 
  }

Screenshot (Null safe):

enter image description here


Full code:

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  Offset _offset = Offset.zero;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: LoginPage(),
      builder: (context, child) {
        return Stack(
          children: [
            child!,
            Positioned(
              left: _offset.dx,
              top: _offset.dy,
              child: GestureDetector(
                onPanUpdate: (d) => setState(() => _offset += Offset(d.delta.dx, d.delta.dy)),
                child: FloatingActionButton(
                  onPressed: () {},
                  backgroundColor: Colors.black,
                  child: Icon(Icons.add),
                ),
              ),
            ),
          ],
        );
      },
    );
  }
}

LoginPage:

class LoginPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('LoginPage')),
      body: Center(
        child: ElevatedButton(
          onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (_) => HomePage())),
          child: Text('Page2'),
        ),
      ),
    );
  }
}

HomePage:

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('HomePage')),
      body: FlutterLogo(size: 300),
    );
  }
}