Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make OverlayEntry disappear after navigating with pushNamed in Flutter

I am trying to make an overlay such as that shown here: https://www.didierboelens.com/2018/06/how-to-create-a-toast-or-notifications-notion-of-overlay/ using OverlayEntry.

import 'package:flutter/material.dart';
import 'dart:async';

class ShowNotificationIcon {

    void show(BuildContext context) async {
        OverlayState overlayState = Overlay.of(context);
        OverlayEntry overlayEntry = new OverlayEntry(builder: _build);

        overlayState.insert(overlayEntry);
    }

    Widget _build(BuildContext context){
      return new Positioned(
        top: 50.0,
        left: 50.0,
        child: new Material(
            color: Colors.transparent,
            child: new Icon(Icons.warning, color: Colors.purple),
        ),
      );
    }
}

Invoked with:

ShowNotificationIcon _icon = new ShowNotificationIcon();

_icon.show(context);

However, when I try to navigate to other screens, the overlay remains in the screen.

How do I show the overlay only in the screen it is being called and not in the others?

Just in case, this is what I had tried inside my stateful widget:

  ShowNotificationIcon _icon = new ShowNotificationIcon();

  @override
  void initState() {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _icon.show(context);
    });

    super.initState();
  }

  @override
  void dispose() {
    _icon.remove();
    super.dispose();
  }
like image 376
Giraldi Avatar asked Jul 26 '19 10:07

Giraldi


1 Answers

This is typically performed using RouteAware+RouteObserver.

RouteObserver is an object that lets objects that implements RouteAware react to some changes related to routing, which includes:

  • a route has been pushed on the top of the current one
  • the route is back to being on the first plan again

You can then use these two events to hide/show your overlay


First, you'll need a RouteObserver.

This can be created as a global variable and needs to be passed to your Navigator. In a MaterialApp based app, it'll typically look like this:

final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();

void main() {
  runApp(MaterialApp(
    home: Container(),
    navigatorObservers: [routeObserver],
  ));
}

Then, your widget that owns the OverlayEntry can now implement RouteAware like so:

class RouteAwareWidget extends StatefulWidget {
  State<RouteAwareWidget> createState() => RouteAwareWidgetState();
}

// Implement RouteAware in a widget's state and subscribe it to the RouteObserver.
class RouteAwareWidgetState extends State<RouteAwareWidget> with RouteAware {

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    // routeObserver is the global variable we created before
    routeObserver.subscribe(this, ModalRoute.of(context) as PageRoute);
  }

  @override
  void dispose() {
    routeObserver.unsubscribe(this);
    super.dispose();
  }

  @override
  void didPush() {
    // Route was pushed onto navigator and is now topmost route.
  }

  @override
  void didPopNext() {
    // Covering route was popped off the navigator.
  }

  @override
  Widget build(BuildContext context) => Container();

}

At this point, you can use didPush and didPopNext to show/hide your OverlayEntry:

OverlayEntry myOverlay;

@override
void didPush() {
  myOverlay.remove();
}

@override
void didPopNext() {
  Overlay.of(context).insert(myOverlay);
}
like image 162
Rémi Rousselet Avatar answered Oct 05 '22 08:10

Rémi Rousselet