TSR
TSR

Reputation: 20406

Flutter | How to show AlertDialog on top of any overlay?

How to show AlertDialog always on top of anything on the screen?

enter image description here

Code:

import 'package:flutter/material.dart';

class CountriesField extends StatefulWidget {
  @override
  _CountriesFieldState createState() => _CountriesFieldState();
}

class _CountriesFieldState extends State<CountriesField> {
  final FocusNode _focusNode = FocusNode();

  OverlayEntry _overlayEntry;

  final LayerLink _layerLink = LayerLink();

  @override
  void initState() {
    _focusNode.addListener(() {
      if (_focusNode.hasFocus) {
        this._overlayEntry = this._createOverlayEntry();
        Overlay.of(context).insert(this._overlayEntry);
      } else {
//        this._overlayEntry.remove();
      }
    });
  }

  OverlayEntry _createOverlayEntry() {
    RenderBox renderBox = context.findRenderObject();
    var size = renderBox.size;

    return OverlayEntry(
        builder: (context) => Positioned(
              width: size.width,
              child: CompositedTransformFollower(
                link: this._layerLink,
                showWhenUnlinked: false,
                offset: Offset(0.0, size.height + 5.0),
                child: Material(
                  elevation: 4.0,
                  child: ListView(
                    padding: EdgeInsets.zero,
                    shrinkWrap: true,
                    children: <Widget>[
                      ListTile(
                        title: Text('Syria'),
                        onTap: () {
                          print('Syria Tapped');
                        },
                      ),
                      ListTile(
                        title: Text('Lebanon'),
                        onTap: () {
                          print('Lebanon Tapped');
                        },
                      )
                    ],
                  ),
                ),
              ),
            ));
  }

  @override
  Widget build(BuildContext context) {
    return CompositedTransformTarget(
      link: this._layerLink,
      child: Material(
        child: TextFormField(
          focusNode: this._focusNode,
          decoration: InputDecoration(labelText: 'Country'),
        ),
      ),
    );
  }
}


class FormPage extends StatefulWidget {
  @override
  _FormPageState createState() => _FormPageState();
}

class _FormPageState extends State<FormPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        children: <Widget>[
          Material(elevation: 4.0, child: CountriesField()),
          RaisedButton(
            child: Text('Help dialog'),
            onPressed: () {
              showDialog(
                  context: context,
                  builder: (BuildContext context) {
                    return AlertDialog(
                      title: Text("Help"),
                      content: Text("This should show on top of any overlay"),
                      actions: <Widget>[
                        FlatButton(
                          child: Text("Close"),
                          onPressed: () {
                            Navigator.of(context).pop();
                          },
                        ),
                      ],
                    );
                  });
            },
          )
        ],
      ),
    );
  }
}

Upvotes: 8

Views: 9304

Answers (2)

Jeff
Jeff

Reputation: 5956

This isn't really a "fix", but a potential workaround for this is to basically not use Overlays and rely on a top level Stack.

If you are using Positioned in your Overlay anyway, putting your widget in a top level Stack doesn't interfere with things like Dialogs (and you potentially have more control over which areas of your app show the overlay).

Example:

class StackOverlayState extends State<StackOverlay> {
  bool _showOverlay = true; // This could also easily be a list of widgets
  
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Positioned.fill(
          child: Scaffold(
            body: /// etc..
          ),
       ),
       if (_showOverlay)
         MyOverlayWidget(),
     ],
    );
  }
}

I was using Overlays, and switching to just using a Stack instead seems to have caused no issues and required no code changes in the Overlay widget itself, but YMMV.

Upvotes: 1

WSBT
WSBT

Reputation: 36333

There is no easy way to make dialogs appear on top of overlays. Depends on your use case, you can either convert both to Overlay, or convert both to Dialog.

This is an example of converting both to dialogs, using showDialog method:

demo

You don't have to return an AlertDialog widget when showing a dialog, for example, here I'm returning a Container with white filling, and contains a ListView for the "Menu A" dialog in the back.

When using showDialog, you get automatic features such as dimming the background and click anywhere outside to dismiss. If you don't want these or any other dialog things, and if you cannot find a way to disable them easily, you can always go the other way around and convert both to Overlay instead.

For overlays, whichever gets inserted latest, is displayed on top.

Upvotes: 2

Related Questions