2

Is there a correct way to listen to a stream / scoped model from a 'root' page or location in a Flutter app and upon receiving some data display an appropriate notification (e.g. Snackbar) on any currently open page (may not be the home page)?

I'd like to be able to display certain notifications across the entire application and not just on a page by page basis.

Here I have a home page that's my apps initial page, it starts listening to a stream from a scoped_model retrieved from context and whenever data is received displays a dialog regardless of the page the user is visiting.

class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {

MyService service  = ScopedModel.of<MyService>(context);

service.events.stream.listen((data) {
  showDialog<String>(
      context: context,
      barrierDismissible: true, // dialog is dismissible with a tap on the barrier
      builder: (BuildContext context) {
        return AlertDialog(
            title: Text('An Event has Occurred'),
            content: Text('$data'));});
});

return Scaffold(
//Navigator Push Routes - Page 1 / Page 2 / Page 3 / etc
);}

This kind of works but really doesn't feel correct - has anyone done this before who could offer a better solution?

I could add a similar listener to every page but again that seems really unnecessary

user6635665
  • 337
  • 3
  • 15
  • I've had a similar question and someone found a solution for it. Maybe it helps you, too: https://stackoverflow.com/q/56664760/6586631 – chris Jun 19 '19 at 13:28

2 Answers2

0

I've done something mildly similar. Though you really should place this in a statefulwidget, add the listener in the initstate and not in the widget build method of a stateless widget.

Adrian Murray
  • 2,170
  • 1
  • 13
  • 15
  • That's a good point and would prevent resetting the listener each build- is this the best way to support application-wide notifications though or are there any other ways to do it? – user6635665 Jun 06 '19 at 15:24
  • What you're doing will work perfectly fine (with the modifications mentioned). Now is probably not the time to worry about "the best" way to do things. – Adrian Murray Jun 06 '19 at 15:29
0

++++ Update July 2019 ++++

I wrote a Package that supports application wide message display.

EZ Flutter is a collection of widgets, packages and many more usefull things, mixed up in little framework. The aim is to make standard features available from scratch. EZ Flutter supports displaying a message to the user from anywhere inside the app with just one line of code.

Github : https://github.com/Ephenodrom/EZ-Flutter

dependencies:
  ez_flutter: ^0.2.0

Check out the documentation how using different configurations works.

https://github.com/Ephenodrom/EZ-Flutter/blob/master/documentation/GLOBAL_MESSAGE.md

++++ Old Answer ++++

Maybe this example will help :

https://github.com/Ephenodrom/Flutter-Advanced-Examples/tree/master/lib/examples/globalMessage

The idea is to have a messagewrapper widget that displays messages with a snackbar that are pushed into a BLOC.

class GlobalMessageWrapper extends StatefulWidget {
  final Widget child;

  GlobalMessageWrapper(this.child);
  @override
  _GlobalMessageWrapperState createState() => _GlobalMessageWrapperState();
}

class _GlobalMessageWrapperState extends State<GlobalMessageWrapper> {
  @override
  Widget build(BuildContext context) {
    return StreamBuilder(
        initialData: null,
        stream: BlocProvider.of<GlobalBloc>(context).messageBloc.messageStream,
        builder: (BuildContext context, AsyncSnapshot<Message> snapshot) {
          Message msg = snapshot.data;
          if (msg != null) {
            WidgetsBinding.instance
                .addPostFrameCallback((_) => _showMessage(msg));
          }
          return Container(child: widget.child);
        });
  }

  void _showMessage(Message message) {
    Color color = Colors.grey;

    switch (message.type) {
      case "success":
        color = Colors.green;
        break;
      case "info":
        color = Colors.blue;
        break;
      case "warning":
        color = Colors.orange;
        break;
      case "error":
        color = Colors.red;
        break;
      default:
    }
    SnackBar bar = SnackBar(
        content: Padding(
          padding: const EdgeInsets.only(bottom: 50.0),
          child: Text(message.text),
        ),
        backgroundColor: color);

    Scaffold.of(context)
      ..hideCurrentSnackBar()
      ..showSnackBar(bar);
  }
}

To use the Wrapper globally in your app, put him on top of the widget tree. You could put it under your MaterialApp Widget in the main.dart file.

Ephenodrom
  • 1,797
  • 2
  • 16
  • 28