从 AlertDialog 中调用 SnackBar

Call SnackBar from within an AlertDialog

当用户点击删除某个员工时,会弹出一个AlertDialog来警告用户。

如果用户确认删除,AlertDialog 就会消失,在 Scaffold 的底部应该会出现一个带有撤消功能的 SnackBar。

问题:

当我在 AlertDialog class 中实现 SnackBar 方法 showSnackBar(context, index, employee) 时,出现以下错误:

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)),
                  ],
                )
              ],
            ));
  }

相反,当我确认删除时,我认为我可以 return 来自 showDeleteDialog 的员工。当结果不为空时,我应该显示 SnackBar。我尝试用 Future/Async 来实现它,但没有成功。

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

编辑: 我想尽可能避免使用 GlobalKey,因为我读到它不利于应用程序的性能。

如错误所述 Scaffold.of() called with a context that does not contain a Scaffold.,这意味着您传递给 showSnackBar() 方法的当前 context 在直接父级中不包含 Scaffold

我们可以使用 GlobalKey 解决这个问题,并将其分配给 Scaffold。在您的有状态小部件中声明一个 global key 并将其作为键传递到您的 Scaffold 中,如下所示:

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

....

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

我在 alertDialog 内点击 navigator.pop() 按钮后调用方法 _showSnackBar(),如下所示:

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();
            }, 
          ),
        ],
      );

然后在_showSnackBar()方法中,使用按键显示snackbar,如下:

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

  }

使用这种方法,一旦您点击 alertDialog 上的 OK 按钮,对话框就会关闭,您会看到快餐栏。您可能需要根据您在上面分享的代码对其进行自定义。

希望这能回答您的问题并解决您的问题。

找到解决方案,而且非常简单...

我只需要将其中一个 context 重命名为 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)),
                  ],
                )
              ],
            ));
  }