Why Does the Snackbar Show Both Inside of and Behind the Dialog?

Viewed 215

I am displaying a Snackbar inside of a Dialog when the user presses the "Save" button. The Snackbar does show at the bottom of the Dialog, but it is also showing on the screen behind the Dialog. I only want the Snackbar in the Dialog.

I tried these possible fixes: using a Builder; using a Stateless widget; using a GlobalKey.

But these are fixes for when the scaffold can't be found in the tree, which doesn't seem to be the problem I'm having, unless the Snackbar is being displayed in both of the Scaffolds (the one in Home and the one in the dialog). I thought the GlobalKey would fix this possible problem, but it didn't. Those Scaffolds have different BuildContexts anyway. I left print statements below to see the BuildContext hashcodes.

This is what I got in the debug console (I know it's redundant, but I just wanted to see if there was some switch happening along the way):

MyApp context: 3
UserAccountToolbarItem build context: 194
UserAccountToolbarItem _buildDropDown context: 194
_UserPageState _showUser context: 327
_UserPageState _save context: 327

If you run the code below, you can see that the same thing happens when I open the Dialog from the body of the app or via a PopupMenuButton in the AppBar Actions. (In my actual app, I'm using the PopupMenuButton.)

If anyone has any ideas about what's going on here, I'd love to hear them. Am I missing something?

Thank you in advance!

Main.dart:

import 'package:flutter/material.dart';
import 'package:snackbars/user_account_toolbar_item.dart';
import 'package:snackbars/user_page.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('MyApp context: ${context.hashCode}');
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Snackbar Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: StatelessHome(),
    );
  }
}

class StatefulHome extends StatefulWidget {
  @override
  _StatefulHomeState createState() => _StatefulHomeState();
}

class _StatefulHomeState extends State<StatefulHome> {
  @override
  Widget build(BuildContext context) {
    print('_StatefulHomeState context: ${context.hashCode}');
    return Scaffold(
        appBar: AppBar(
          title: Text('Flutter Snackbar Demo Home Page with Stateful Home'),
          actions: [UserAccountToolbarItem()],
        ),
        body: Center(
          child: ElevatedButton(
              child: Text('Show dialog!'),
              onPressed: () {
                showDialog(
                    context: context,
                    builder: (BuildContext context) {
                      return Dialog(
                        child: UserPage(),
                      );
                    });
              }),
        ));
  }
}

User_Account_Toolbar_Item.dart:

import 'package:flutter/material.dart';
import 'package:snackbars/user_page.dart';

class UserAccountToolbarItem extends StatelessWidget {

  void _showUser(context) {
    print('UserAccountToolbarItem _showUser context: ${context.hashCode}');
    showDialog(
      context: context,
      builder: (context) {
        return Dialog(
          child: Container(width: 480, child: UserPage()),
        );
      },
    );
  }

  Widget _buildDropDown(BuildContext context) {
    print('UserAccountToolbarItem _buildDropDown context: ${context.hashCode}');
    return PopupMenuButton<Widget>(
      icon: Icon(Icons.account_circle_sharp),
      itemBuilder: (BuildContext context) => <PopupMenuEntry<Widget>>[
        PopupMenuItem<Widget>(
          value: Text('Sign Out'),
          child: ListTile(title: Text('Dummy ListTile')),
        ),
        PopupMenuItem<Widget>(
          child: ListTile(
            title: Text('CLICK ME'),
            onTap: () => {
              Navigator.pop(context), 
              _showUser(context)
            },
          ),
        ),
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    print('UserAccountToolbarItem build context: ${context.hashCode}');
    return _buildDropDown(context);
  }
}

User_Page.dart:

import 'package:flutter/material.dart';

class UserPage extends StatefulWidget {

  @override
  _UserPageState createState() => _UserPageState();
}

class _UserPageState extends State<UserPage> {
  @override
  Widget build(BuildContext context) {
    print('_UserPageState _showUser context: ${context.hashCode}');
    return Scaffold(
      appBar: AppBar(
        title: Text('User Account'),
        actions: <Widget>[
          MaterialButton(
            onPressed: () => _save(),
            child: Text(
              'Save',
              style: TextStyle(color: Colors.white),
            ),
          ),
        ],
      ),
      body: Text('Dialog Body'),
    );
  }


  void _save() async {
    print('_UserPageState _save context: ${context.hashCode}');
    try {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text('Something updated.'),
          backgroundColor: Colors.green,
        ),
      );
    } catch (e) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
            content: Text('Save failed: ${e.toString()}'),
            backgroundColor: Colors.red),
      );
    }
  }
}
1 Answers

hope this is what you are looking for

enter image description here

import 'package:flutter/material.dart';

class SnackOverDialog extends StatefulWidget {
  SnackOverDialog({Key? key}) : super(key: key);

  @override
  _SnackOverDialogState createState() => _SnackOverDialogState();
}

class _SnackOverDialogState extends State<SnackOverDialog> {
  final GlobalKey<ScaffoldState> _scaffoldkey = new GlobalKey<ScaffoldState>();
  @override
  Widget build(BuildContext context) {
    ///* show snack
    _snackbar(BuildContext context) {
      _scaffoldkey.currentState!.showSnackBar(SnackBar(
        content: const Text('snack'),
        duration: const Duration(seconds: 1),
        action: SnackBarAction(
          label: 'ACTION',
          onPressed: () {},
        ),
      ));
    }

    ///* dialog
    _dialog(BuildContext context) {
      WidgetsBinding.instance!.addPostFrameCallback((_) async {
        showDialog(
          context: context,
          builder: (BuildContext dialogContext) => AlertDialog(
            content: Scaffold(
              key: _scaffoldkey,
              body: GestureDetector(
                onTap: () {
                  _snackbar(dialogContext);
                },
                child: Center(
                  child: Text('Show SnackBar!'),
                ),
              ),
            ),
          ),
        );
      });
    }

    return Scaffold(
      appBar: AppBar(
        title: Text("SNackBarOVerDialog"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => _dialog(context),
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

Related