【问题标题】:Call SnackBar from within an AlertDialog从 AlertDialog 中调用 SnackBar
【发布时间】:2020-07-20 22:28:28
【问题描述】:

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

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

问题:

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

在处理手势时抛出了以下断言:

使用不包含 Scaffold 的上下文调用 Scaffold.of()。

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

相反,当我确认删除时,我认为我可以从 showDeleteDialog 返回一个员工。当结果不为空时,我应该显示 SnackBar。我尝试使用 Future/Async 来实现这一点,但没有成功。

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

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

【问题讨论】:

  • 当您调用小吃店时,您似乎没有从脚手架传递 BuiltContext
  • @LonelyWolf 完全正确。我不知道该怎么做。
  • 尝试使用Builder() Widget 包装您调用Snackbar 的小部件

标签: flutter snackbar


【解决方案1】:

找到了解决办法,而且超级简单……

我只需要将 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)),
                  ],
                )
              ],
            ));
  }

【讨论】:

    【解决方案2】:

    正如错误所说的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),
      ),
    

    我在OK按钮点击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()方法中,使用key显示snackbar,如下:

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

    使用这种方法,一旦您点击alertDialog 上的OK 按钮,对话框将关闭,您将看到小吃栏。您可能需要按照上面共享的代码自定义此设置。

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

    【讨论】:

    • 虽然这似乎可行,但我找到了一个更简单的解决方案,同时避免了 GlobalKey 功能。我只需要重命名 AlertDialog 的上下文名称。
    • 虽然这是正确的,但我想补充一点,有一个 known issue 在持续时间后不会隐藏小吃栏,所以 workaround 是调用 Future.delayed(Duration(seconds: 1), () { _scaffoldKey.currentState.hideCurrentSnackBar(); });
    猜你喜欢
    • 1970-01-01
    • 2021-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-27
    • 2017-05-15
    • 1970-01-01
    • 2013-01-04
    相关资源
    最近更新 更多