Flutter Dialog Context 注意事项

先来看一段代码:

Future<dynamic> _showDeleteDialog(
      BuildContext context, UserProvider userProvider, PlayerData player) {
    return showDialog(
        context: context,
        builder: (BuildContext context) {
          return AlertDialog(
            actionsPadding:
                const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
            titleTextStyle: const TextStyle(
                color: MyColors.styleBlack,
                fontWeight: FontWeight.normal,
                fontSize: 20),
            title: Text(S.of(context).delete_player_or_not),
            actions: <Widget>[
              TextButton(
                onPressed: () => Navigator.pop(context, 'Cancel'),
                child: Text(S.of(context).dialog_cancel,
                    style: const TextStyle(
                      color: MyColors.styleColor,
                    )),
              ),
              TextButton(
                onPressed: () async {
                  Navigator.pop(context, 'OK');
                  // AlertDialog 的 context 已经出栈,再使用会出错
                  LoadingDialog.show(context: context);
                  var success =
                      await userProvider.deletePlayer(playerId: player.id);
                  LoadingDialog.hide();
                  if (success) {
                    if (!mounted) return;
                    Navigator.pop(context);
                  }
                },
                child: Text(S.of(context).dialog_ok,
                    style: const TextStyle(
                      color: Colors.red,
                    )),
              ),
            ],
          );
        });
  }

在这个例子中,显示调用 AlertDialog,假设按了确定,则弹出一个 LoadingDialog 访问 api。在这个过程中,LoadingDialog 还可以正常展示,但是无法隐藏,此时 context 为空。因为 Navigator.pop(context, 'OK') 已经把 AlertDialog 出栈,其对应的 context 也会 unmounted。

正确的做法是 Navigator.pop(context, 'OK') 之后的 context 均使用 AlertDialog 上一层的 context(即当前还 mounted 的 context)

Future<dynamic> _showDeleteDialog(
      BuildContext context, UserProvider userProvider, PlayerData player) {
    // 外层 context
    var outsideContext = context;
    return showDialog(
        context: context,
        builder: (BuildContext context) {
          return AlertDialog(
            actionsPadding:
                const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
            titleTextStyle: const TextStyle(
                color: MyColors.styleBlack,
                fontWeight: FontWeight.normal,
                fontSize: 20),
            title: Text(S.of(context).delete_player_or_not),
            actions: <Widget>[
              TextButton(
                onPressed: () => Navigator.pop(context, 'Cancel'),
                child: Text(S.of(context).dialog_cancel,
                    style: const TextStyle(
                      color: MyColors.styleColor,
                    )),
              ),
              TextButton(
                onPressed: () async {
                  Navigator.pop(context, 'OK');
                  // AlertDialog 的 context 已经出栈,需要使用上一层的 context
                  LoadingDialog.show(context: outsideContext);
                  var success =
                      await userProvider.deletePlayer(playerId: player.id);
                  LoadingDialog.hide();
                  if (success) {
                    if (!mounted) return;
                    Navigator.pop(outsideContext);
                  }
                },
                child: Text(S.of(context).dialog_ok,
                    style: const TextStyle(
                      color: Colors.red,
                    )),
              ),
            ],
          );
        });
  }

发表评论


*