5

I use flushbar to display in-app notifications to the user, but I think this issue applies to any widget that one wants to overlay over the current screen, such as a snackbar.

I want to be able to display a notification that is independent of the navigation between screens, i.e., if the user navigates between the screens the notification should stay across all screens. Because the flushbar notification is drawn over the current BuildContext only, as soon as the user closes the current screen, the notification disappears as well (because the notification widget is part of that screen's widget subtree).

Is there a way to display a widget (such as a notification) on top of the entire app, no matter the navigation?

EDIT1: Added example code.

import 'package:flushbar/flushbar_route.dart' as route;
import 'package:flushbar/flushbar.dart';
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: FirstScreen(),
    );
  }
}

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First Screen'),
      ),
      body: Center(
        child: RaisedButton(
          child: Text('go to second screen'),
          onPressed: () {
            Navigator.of(context).push(
              MaterialPageRoute(builder: (BuildContext context) => SecondScreen())
            );
          },
        ),
      ),
    );
  }
}

class SecondScreen extends StatelessWidget {

  final Flushbar _flushbar = Flushbar(message: 'Flushbar Notification');

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Second Screen'),
      ),
      body: Center(
        child: RaisedButton(
          child: Text('show flushbar'),
          onPressed: () {
            showFlushbar(_flushbar, context);
          },
        ),
      ),
    );
  }
}

Future showFlushbar(Flushbar instance, BuildContext context) {
  final _route = route.showFlushbar(
    context: context,
    flushbar: instance,
  );

  return Navigator.of(context, rootNavigator: true).push(_route);
}

The result will look like this (when going back to the first screen I would want the notification to stay on the screen):

example app demo

EDIT2: George's solution works. Additionally, using overlays might be a suitable solution for others as well (see this blog post), i.e., the overlay stays on-screen even during route navigation. However, the overlay solution is less elegant in my case because it doesn't allow the flushbar to be dismissible or animated (or at least it's not as straightforward).

Related question: Flutter - Application Wide Notifications

chris
  • 353
  • 3
  • 8

2 Answers2

2

Try displaying your Flushbar from the root Navigator.

Looking at the sources of flushbar lib, we can create our own Flushbar route by importing showFlushbar method from package:flushbar/flushbar_route.dart.

e.g.

import 'package:flushbar/flushbar_route.dart' as route;

// ...

Future showFlushbar(Flushbar instance) {
  final _route = route.showFlushbar(
    context: context,
    flushbar: instance,
  );

  return Navigator.of(context, rootNavigator: true).push(_route);
}

Update

Thank you for the added code in your question.

The ultimate solution, in my opinion, is creating another Navigator right inside of MaterialApp - some kind of a second 'sub-root' navigator.

Now you will be able to choose between 2 layers of Navigator depending on your needs.

Navigator.of(context, rootNavigator: true) will return the top-level Navigator - use it for your Flushbar or any other modal popups that you want to keep persistent above all screens.

Navigator.of(context), where rootNavigator is false by default. Use it to get the 'sub-root' Navigator to display new screens / pages.

e.g. placing another Navigator right in the MaterialApp.

MaterialApp(
  home: Navigator( // 'sub-root' navigator. Second in the hierarchy.
    onGenerateRoute: (_) => MaterialPageRoute(
      builder: (_) => FirstScreen(),
      settings: RouteSettings(isInitialRoute: true),
    ),
  ),
);

Let me know if this helped.

George Zvonov
  • 9,401
  • 5
  • 33
  • 37
  • Thank you for your answer, George! Unfortunately, this did not work for me. The flushbar is pushed on top of the current screen, but as soon as the current screen (route) gets closed (`Navigator.of(context).pop()`), it also closes the flushbar (presumably because all child-routes of the current screen get popped from the navigator as well). – chris Jun 19 '19 at 10:48
  • @chris please expand your question with some code - how exactly do you initialize the Flushbar and where/when do you call `.pop()`. – George Zvonov Jun 19 '19 at 10:55
  • I've added some code in my question which already incorporates your answer. In this example, I'm not calling `.pop()` explicitly but the issue is the same when tapping the back button on the appBar. – chris Jun 19 '19 at 11:25
-2

This worked for me.

When I pop back the previous screen the Flush message will show on the previous screen.

onPressed: () {
   Navigator.pop(context);
   Flush.showGoodFlushbar(context, 'login successful!');
}
Sailendra
  • 1,318
  • 14
  • 29
Moses
  • 7
  • 1
  • 5