You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中如何让SnackBar显示在showModalBottomSheet上方

解决ShowModalBottomSheet内SnackBar显示位置问题

你遇到的问题是因为调用showSnackBar时使用了BottomSheet内部的context,导致SnackBar被挂载在弹窗的上下文层级中,从而显示在弹窗下方。以下两种方案可以解决这个问题:

方案一:使用外部上下文显示全局SnackBar

通过提前获取页面根上下文的ScaffoldMessenger,让SnackBar挂载在页面顶层,自然显示在BottomSheet上方:

// 先获取外部页面的ScaffoldMessenger实例
final outerScaffoldMessenger = ScaffoldMessenger.of(context);

showModalBottomSheet(
        isScrollControlled: true,
        isDismissible: false,
        context: context,
        builder: (context) {
          return SafeArea(
            child: Stack(
              alignment: Alignment.topCenter,
              clipBehavior: Clip.none,
              children: [
                Column(
                  children: [
                    Container(
                      padding: const EdgeInsets.symmetric(
                          horizontal: 30, vertical: 0),
                      child: Column(children: [
                        Padding(
                          padding:
                              const EdgeInsets.symmetric(horizontal: 50),
                          child: TextField(
                            controller: codeController,
                            keyboardType: TextInputType.number,
                          ),
                        ),
                        const SizedBox(
                          height: 5,
                        ),
                        Padding(
                          padding:
                              const EdgeInsets.symmetric(horizontal: 50),
                          child: CustomButton(
                              onTap: () async {
                                if (codeController.text.length == 6) {
                                  try {
                                    PhoneAuthCredential credential =
                                        PhoneAuthProvider.credential(
                                            verificationId: verificationId,
                                            smsCode:
                                                codeController.text.trim());
                                    await _auth
                                        .signInWithCredential(credential);
                                    Navigator.of(context)
                                        .pushNamedAndRemoveUntil(
                                            '/home',
                                            (Route<dynamic> route) =>
                                                false);
                                  } catch (e) {
                                    // 使用外部的ScaffoldMessenger显示SnackBar
                                    outerScaffoldMessenger.showSnackBar(
                                      SnackBar(
                                        content: Column(
                                          mainAxisSize: MainAxisSize.min,
                                          children: const [
                                            Text("Invalid verification code"),
                                            Text("Please enter correct verification code"),
                                          ],
                                        ),
                                        duration: const Duration(seconds: 2),
                                      ),
                                    );
                                  }
                                }
                              },
                              text: "Continue",
                              height: 50),
                        )
                      ]),
                    )
                  ],
                )
              ],
            ),
          );
        });

方案二:在BottomSheet内部自定义本地SnackBar

如果希望SnackBar完全属于弹窗的一部分,可以在弹窗的Stack中添加自定义的提示组件,通过状态控制显示隐藏:

showModalBottomSheet(
        isScrollControlled: true,
        isDismissible: false,
        context: context,
        builder: (context) {
          return StatefulBuilder(
            builder: (context, setState) {
              bool showErrorSnackBar = false;
              
              return SafeArea(
                child: Stack(
                  alignment: Alignment.topCenter,
                  clipBehavior: Clip.none,
                  children: [
                    Column(
                      children: [
                        // 预留SnackBar高度,避免主体内容被遮挡
                        if (showErrorSnackBar) const SizedBox(height: 60),
                        Container(
                          padding: const EdgeInsets.symmetric(
                              horizontal: 30, vertical: 0),
                          child: Column(children: [
                            Padding(
                              padding:
                                  const EdgeInsets.symmetric(horizontal: 50),
                              child: TextField(
                                controller: codeController,
                                keyboardType: TextInputType.number,
                              ),
                            ),
                            const SizedBox(
                              height: 5,
                            ),
                            Padding(
                              padding:
                                  const EdgeInsets.symmetric(horizontal: 50),
                              child: CustomButton(
                                  onTap: () async {
                                    if (codeController.text.length == 6) {
                                      try {
                                        PhoneAuthCredential credential =
                                            PhoneAuthProvider.credential(
                                                verificationId: verificationId,
                                                smsCode:
                                                    codeController.text.trim());
                                        await _auth
                                            .signInWithCredential(credential);
                                        Navigator.of(context)
                                            .pushNamedAndRemoveUntil(
                                                '/home',
                                                (Route<dynamic> route) =>
                                                    false);
                                      } catch (e) {
                                        // 显示自定义SnackBar
                                        setState(() {
                                          showErrorSnackBar = true;
                                        });
                                        // 2秒后自动隐藏
                                        Future.delayed(const Duration(seconds: 2), () {
                                          setState(() {
                                            showErrorSnackBar = false;
                                          });
                                        });
                                      }
                                    }
                                  },
                                  text: "Continue",
                                  height: 50),
                            )
                          ]),
                        )
                      ],
                    ),
                    // 自定义错误提示组件,位于弹窗顶部
                    if (showErrorSnackBar)
                      Positioned(
                        top: -50,
                        left: 20,
                        right: 20,
                        child: Container(
                          padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
                          decoration: BoxDecoration(
                            color: Colors.redAccent,
                            borderRadius: BorderRadius.circular(8),
                          ),
                          child: Column(
                            mainAxisSize: MainAxisSize.min,
                            children: const [
                              Text("Invalid verification code", style: TextStyle(color: Colors.white)),
                              Text("Please enter correct verification code", style: TextStyle(color: Colors.white, fontSize: 12)),
                            ],
                          ),
                        ),
                      )
                  ],
                ),
              );
            },
          );
        });

关键说明

  • 方案一的SnackBar属于全局提示,会显示在页面顶部,覆盖BottomSheet的上边缘,不会隐藏弹窗。
  • 方案二的提示组件完全属于BottomSheet,位置可以自由调整,样式也能自定义,更贴合弹窗的整体设计。

内容的提问来源于stack exchange,提问作者Rokey Z

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 06:35:31