1
votes

When a user clicks on delete for an employee, an AlertDialog shall pop up to warn the user.

enter image description here

If the user confirms the deletion, then the AlertDialog disappears and at the bottom of the Scaffold a SnackBar should appear with an Undo function.

Problem:

When I implement the SnackBar method showSnackBar(context, index, employee) within the AlertDialog class I get the following error:

he following assertion was thrown while handling a gesture:

Scaffold.of() called with a context that does not contain a Scaffold.

showDeleteDialog(BuildContext context, Employee employee, int index) {
    showDialog(
        context: context,
        builder: (context) => AlertDialog(
              title:
                  Text('Are you sure you want to delete: ${employee.name} ?'),
              actions: <Widget>[
                Row(
                  children: <Widget>[
                    FlatButton(
                        child: Text('Yes'),
                        onPressed: () {
                          DatabaseProvider.db.deleteEmployee(employee.id).then(
                              (_) => BlocProvider.of<EmployeeBloc>(context)
                                  .add(DeleteEmployee(index)));
                          Navigator.pop(context,employee);
                          showSnackBar(context, index, employee);
                        }),
                    FlatButton(
                        child: Text('No!'),
                        onPressed: () => Navigator.pop(context)),
                  ],
                )
              ],
            ));
  }

Instead, I thought I could return an employee from the showDeleteDialog when I confirm the deletion. When the result is not null, then I should show the SnackBar. I tried to implement this with Future/Async but with no success.

onPressed: () async {
                        Employee deletedEmployee = await showDeleteDialog(context, employee, index);
                        await showSnackBar(context, index, deletedEmployee);
                    },

Edit: I would like to avoid using GlobalKey if possible, since I read it is not good for the App's performance.

2
It looks like you are not passing BuiltContext from scaffold when you're calling your snackbar - LonelyWolf
@LonelyWolf exactly. I do not know how to though. - Georgios
try wrapping your widget that u r calling Snackbar with Builder() Widget - GJJ2019

2 Answers

3
votes

As the error says Scaffold.of() called with a context that does not contain a Scaffold., that means the current context that you are passing to showSnackBar() method doesn't contain a Scaffold in the immediate parent.

We can fix this by using GlobalKey and assign it to the Scaffold. Declare a global key in your stateful widget and pass this as a key in your Scaffold, as below:

final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();

....

return Scaffold(
  key: _scaffoldKey,
  appBar: AppBar(
    title: Text(widget.title),
  ),

I call a method _showSnackBar() after navigator.pop() on OK button click inside the alertDialog, as below:

return AlertDialog(
        title: Text('Not in stock'),
        content: const Text('This item is no longer available'),
        actions: <Widget>[
          FlatButton(
            child: Text('Ok'),
            onPressed: () {
              Navigator.of(context).pop();
              _showSnackBar();
            }, 
          ),
        ],
      );

Then in the _showSnackBar() method, use the key to show the snackbar, as below:

void _showSnackBar() {
    _scaffoldKey.currentState.showSnackBar(
  SnackBar(
    content: Text('Snackbar is displayed'),
  ));    

  }

With this approach, once you tap on OK button on alertDialog, the dialog will close and you'll see the snackbar. You may need to customize this per your code as you shared above.

Hope this answers your question and resolves your issue.

1
votes

Found the solution and it is super easy...

I only had to re-name one of the context to a dialogContext

showDeleteDialog(BuildContext context, Employee employee, int index) {
    showDialog(
        context: context,
        builder: (dialogContext) => AlertDialog(
              title:
                  Text('Are you sure you want to delete: ${employee.name} ?'),
              actions: <Widget>[
                Row(
                  children: <Widget>[
                    FlatButton(
                        child: Text('Yes'),
                        onPressed: () {
                          DatabaseProvider.db.deleteEmployee(employee.id).then(
                              (_) => BlocProvider.of<EmployeeBloc>(dialogContext)
                                  .add(DeleteEmployee(index)));
                          Navigator.pop(dialogContext);
                          showSnackBar(context, index, employee);
                        }),
                    FlatButton(
                        child: Text('No!'),
                        onPressed: () => Navigator.pop(context)),
                  ],
                )
              ],
            ));
  }