Flutter中没有上下文的AlertDialog

59
当http get失败时,我希望显示一个AlertDialog。函数showDialog(https://api.flutter.dev/flutter/material/showDialog.html)有一个"@required BuildContext context"参数,但我想从我的异步函数getNews()调用AlertDialog,该函数没有上下文值。
类比Java,在没有所有者的情况下使用null来使用对话框,我尝试将上下文值设置为null,但它不被接受。
这是我的代码:
  Future<dynamic> getNews() async {
    dynamic retVal;
    try {
      var response = await http.get(url));
      if (response.statusCode == HttpStatus.ok) {
        retVal = jsonDecode(response.body);
      }
    } catch (e) {
      alertDlg(?????????, 'Error', e.toString());
  }
    return
    retVal;
  }

  static Future<void> alertDlg(context, String titolo, String messaggio) async {
    return showDialog<void>(
        context: context,
        barrierDismissible: false, // user must tap button!
        builder: (BuildContext context) {
        return AlertDialog(
              title: Text(titolo),
        ...
    );
  }

将您的“Build”上下文从“StatefulWidget”传递到显示HTTP响应的“getNews()”函数中。 - Mazin Ibrahim
你能举个例子吗?我试着将上下文传递给我的新小部件。它没有报错,但也没有显示任何警报。 - Aseem
绕过null不会得到任何编译时错误,但会收到相同的失败断言:行'context!= null':不是真实的。 - s.j
6个回答

120

不使用第三方库或存储BuildContext的解决方案:

final navigatorKey = GlobalKey<NavigatorState>();

void main() => runApp(
  MaterialApp(
    home: HomePage(),
    navigatorKey: navigatorKey, // Setting a global key for navigator
  ),
);

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: SafeArea(
        child: Center(
          child: Text('test')
        )
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: showMyDialog, // Calling the function without providing it BuildContext
      ),
    );
  }
}

void showMyDialog() {
  showDialog(
    context: navigatorKey.currentContext,
    builder: (context) => Center(
      child: Material(
        color: Colors.transparent,
        child: Text('Hello'),
      ),
    )
  );
}
在为导航器设置全局键(MaterialApp类的navigatorKey参数)后,它的当前状态可从代码的任何部分访问。我们可以将其上下文传递给showDialog函数。确保在第一帧构建之前不要使用它,否则状态将为null。 基本上,对话框只是另一种类型的路由,就像MaterialPageRouteCupertinoPageRoute一样-它们都源自ModalRoute类,它们的实例被推入NavigatorState中。仅需要上下文以获取当前导航器的状态。如果有全局导航器键,则可以推送新路由而无需上下文:
navigatorKey.currentState.push(route)

很遗憾,_DialogRoute类(位于...\flutter\lib\src\widgets\routes.dart)在showDialog函数中被设置为私有和不可访问的,但您可以创建自己的对话框路由类并将其推入导航器堆栈。

更新:Navigator.of方法已更新,无需传递子树上下文。


1
使用navigatorKey.currentContext怎么样?它似乎可以工作,但是与currentState.overlay.context相比是否存在潜在问题我不确定。 - Magnus
@IgorKharakhordin 如果Catcher.navigatorKey已经使用了navigatorKey,那该怎么办? - Jerry
2
如果您确定在显示对话框时,navigatorKey已经具备了上下文,请像这样强制解包您的currentContext变量:context:navigatorKey.currentContext! - Iván Yoed
1
有什么方法可以确保在应用程序初始化的早期,currentContext不会为null吗?每次我在代码中放置一个!时,我都感到紧张。 - Clifton Labrum
1
当我尝试使用当前上下文创建对话框时,出现错误“找不到Material小部件。ListTile小部件需要一个Material小部件祖先。” - queen3
显示剩余8条评论

11
对话框只需要上下文通过继承的navigateState来访问它。您可以制作自己修改过的对话框,或使用我的库来完成这个操作。
使用它,您可以在代码的任何地方打开对话框,而无需上下文,只需执行以下操作: https://pub.dev/packages/get
Get.dialog(SimpleDialog());

获取MaterialApp并弹出SimpleDialog对话框。 - s.j
我尝试使用 GetMaterialApp(title: UtilString.submit);,但是出现了编译时错误。 - s.j
17
如果不必要,不要强制使用第三方库。请查看以下带有final navigatorKey = GlobalKey<NavigatorState>();的答案。 - cs guy
被接受的答案是错误的?这至少是一个极端夸张的说法。为什么在众多实现此功能的方法中,只有 GetX/Get(你的)方法是唯一正确的呢?我尊重 GetX 所做的努力,但你不能硬生生地灌输给人们。我很惊讶这被标记为被接受的答案。 - om-ha
@jonatasBorges 没有上下文,我怎么关闭它? - L.Goyal
显示剩余6条评论

1
如果您使用await,则应在调用getNews时捕获异常,否则请使用Future的catchError属性。

1

所以你需要一个 BuildContext 来创建对话框,但你没有访问它的权限。这是一个常见的问题,你可以参考 这个StackOverflow问题 中的一种方法来解决它(创建一个静态对话框,并在需要时显示它)。

另一种方法是在创建异步方法或对象时将上下文作为参数传递。确保在完成后将其设置为 null。

或者你可以创建一个标志(布尔值),在特定条件下变为“true”,在其中一个 build() 方法中始终检查该标志,如果它为“true” - 做你想做的事情(例如显示对话框)。


-3
  1. 使用以下命令安装GetX: flutter pub add get
  2. 使用Get.dialog创建对话框
  3. 添加这些行
Get.dialog(
      Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 40),
            child: Container(
              decoration: const BoxDecoration(
                color: Colors.white,
                borderRadius: BorderRadius.all(
                  Radius.circular(20),
                ),
              ),
              child: Padding(
                padding: const EdgeInsets.all(20.0),
                child: Material(
                  child: Column(
                    children: [
                      const Text("Dialog with GetX without context")
                      Row(
                        children: [
                          const SizedBox(width: 10),
                          Expanded(
                            child: ElevatedButton(
                              style: ElevatedButton.styleFrom(
                                minimumSize: const Size(0, 45),
                                primary: Colors.green,
                                onPrimary: const Color(0xFFFFFFFF),
                                shape: RoundedRectangleBorder(
                                  borderRadius: BorderRadius.circular(8),
                                ),
                              ),
                              onPressed: () {
                                Navigator.pop(Get.overlayContext!, true);
                              },
                              child: const Text(
                                'Cerrar',
                              ),
                            ),
                          ),
                        ],
                      ),
                    ],
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );

完成!


-5
最简单的在任何地方显示警报的方法: 使用这个包link,并在您想要显示警报的任何位置输入以下行: < p > < code > QuickAlert.show(context:context,type:QuickAlertType.error,text:'Error Message');

1
但是您使用QuickAlert的解决方案需要一个BuildContext上下文。问题是关于“没有上下文的AlertDialog”。 - Stephan
1
我请求您仔细阅读问题,而不仅仅是标题。其次,我没有提供类的Build方法的上下文。我只提供了警报类型和文本。 - user3107831
void showAlert(type, text) { QuickAlert.show( context: context, type: type, text: text, ); } - user3107831

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接