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

自定义NavigationDrawer组件报错,Flutter项目启动失败求助

问题排查与修复方案

错误原因

报错Null check operator used on a null value(空值检查运算符被用于空值),核心问题出在未处理StreamBuilder的非活跃状态/空数据场景,以及部分空值未做安全校验:

  • 直接用usuario!.email强制解包Firebase用户实例,若用户未登录会触发空值错误
  • StreamBuilder仅处理connectionState == ConnectionState.active的情况,加载中、请求失败时snapshot.data为null,但代码仍强制解包并使用空字符串初始化NetworkImage
  • 在build方法内定义GlobalKey<ScaffoldState>,每次重建都会生成新key,失去全局key作用

修复代码

1. 修正NavigationDrawer组件

class NavigationDrawer extends StatefulWidget {
  const NavigationDrawer({Key? key}) : super(key: key);

  @override
  State<NavigationDrawer> createState() => _NavigationDrawerState();
}

class _NavigationDrawerState extends State<NavigationDrawer> {
  // 将GlobalKey移至build方法外,避免重复创建
  final GlobalKey<ScaffoldState> _key = GlobalKey();

  @override
  Widget build(BuildContext context) {
    User? usuario = FirebaseAuth.instance.currentUser;
    // 安全获取用户邮箱,为空则返回提示Drawer
    final correo = usuario?.email;
    if (correo == null) {
      return const Drawer(
        child: Center(child: Text("请先登录")),
      );
    }

    bool esAmbassador = false;
    String avatar = "";
    String alias = "";

    Stream<DocumentSnapshot<Map<String, dynamic>>> documentStream = FirebaseFirestore.instance
        .collection('perfiles')
        .doc(correo)
        .snapshots();

    return StreamBuilder<DocumentSnapshot<Map<String, dynamic>>>(
      stream: documentStream,
      builder: (context, snapshot) {
        // 处理加载状态
        if (snapshot.connectionState == ConnectionState.waiting) {
          return const Drawer(
            child: Center(child: CircularProgressIndicator()),
          );
        }

        // 处理错误状态
        if (snapshot.hasError) {
          return Drawer(
            child: Center(child: Text("加载失败: ${snapshot.error}")),
          );
        }

        // 处理空数据状态
        if (!snapshot.hasData || snapshot.data!.data() == null) {
          return const Drawer(
            child: Center(child: Text("暂无用户数据")),
          );
        }

        // 安全解析数据,使用??提供默认值
        final perfilData = snapshot.data!.data()!;
        avatar = perfilData['avatar'] ?? "";
        alias = perfilData['alias'] ?? "";
        esAmbassador = perfilData['esAmbassador'] ?? false;

        // 安全打印日志
        print("avatar es ${perfilData['alias'] ?? '未知'}");
        print("esBmx es ${perfilData['esBmx'] ?? '未知'}");
        print("esAmb es ${perfilData['esAmbassador'] ?? false}");

        return Drawer(
          child: ListView(
            children: [
              DrawerHeader(
                decoration: const BoxDecoration(
                  color: AppColors.rojoMovMap,
                ),
                child: Center(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Padding(
                        padding: const EdgeInsets.all(8.0),
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.start,
                          children: [
                            Container(
                              width: 100,
                              height: 100,
                              decoration: BoxDecoration(
                                borderRadius: BorderRadius.circular(180),
                                border: Border.all(color: Colors.black, width: 4),
                                // 图片为空时使用本地占位图
                                image: avatar.isNotEmpty
                                    ? DecorationImage(
                                        fit: BoxFit.fill,
                                        image: NetworkImage(avatar),
                                      )
                                    : const DecorationImage(
                                        fit: BoxFit.fill,
                                        image: AssetImage("assets/images/default_avatar.png"),
                                      ),
                              ),
                            ),
                          ],
                        ),
                      ),
                      Row(
                        children: [
                          Text(
                            alias.isNotEmpty ? alias : "未设置昵称",
                            style: const TextStyle(
                              fontSize: 18,
                              fontWeight: FontWeight.bold,
                              color: Colors.white,
                            ),
                          )
                        ],
                      )
                    ],
                  ),
                ),
              ),
              // 以下ListTile保留原逻辑
              ListTile(
                title: Text("drawer1".tr(), style: const TextStyle(fontSize: 16)),
                leading: Container(
                  width: 30,
                  height: 30,
                  decoration: const BoxDecoration(
                    image: DecorationImage(
                      image: AssetImage("assets/images/edit_profile.png"),
                    ),
                  ),
                ),
                onTap: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(builder: (context) => const EditarPerfil1()),
                  );
                },
              ),
              ListTile(
                title: Text("drawer2".tr(), style: const TextStyle(fontSize: 16)),
                leading: Container(
                  width: 30,
                  height: 30,
                  decoration: const BoxDecoration(
                    image: DecorationImage(
                      image: AssetImage("assets/images/mi_network.png"),
                    ),
                  ),
                ),
                onTap: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(builder: (context) => const MiNetwork()),
                  );
                },
              ),
              ListTile(
                title: Text("drawer3".tr(), style: const TextStyle(fontSize: 16)),
                leading: Container(
                  width: 30,
                  height: 30,
                  decoration: const BoxDecoration(
                    image: DecorationImage(
                      image: AssetImage("assets/images/privacy.png"),
                    ),
                  ),
                ),
                onTap: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(builder: (context) => const TermsUso()),
                  );
                },
              ),
              ListTile(
                title: Text("drawer4".tr(), style: const TextStyle(fontSize: 16)),
                leading: Container(
                  width: 30,
                  height: 30,
                  decoration: const BoxDecoration(
                    image: DecorationImage(
                      image: AssetImage("assets/images/contact.png"),
                    ),
                  ),
                ),
                onTap: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(builder: (context) => const Contacto()),
                  );
                },
              ),
              ListTile(
                title: Text("drawer5".tr(), style: const TextStyle(fontSize: 16)),
                leading: Container(
                  width: 30,
                  height: 30,
                  decoration: const BoxDecoration(
                    image: DecorationImage(
                      image: AssetImage("assets/images/about.png"),
                    ),
                  ),
                ),
                onTap: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(builder: (context) => const AcercaDe()),
                  );
                },
              ),
              ListTile(
                title: Text("drawer6".tr(), style: const TextStyle(fontSize: 16)),
                leading: Container(
                  width: 30,
                  height: 30,
                  decoration: const BoxDecoration(
                    image: DecorationImage(
                      image: AssetImage("assets/images/ic_ambassador.png"),
                    ),
                  ),
                ),
                onTap: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(builder: (context) => const ConvertirAmb()),
                  );
                },
              ),
              ListTile(
                title: Text("drawer7".tr(), style: const TextStyle(fontSize: 16)),
                leading: Container(
                  width: 30,
                  height: 30,
                  decoration: const BoxDecoration(
                    image: DecorationImage(
                      image: AssetImage("assets/images/salir.png"),
                    ),
                  ),
                ),
                onTap: () {
                  ServiciosFirebaseUsuario().cerrarSesion(context);
                },
              )
            ],
          ),
        );
      },
    );
  }
}

2. 优化UsuarioPage调用逻辑

return SafeArea(
  child: Scaffold(
    // 无需全局控制Drawer时可省略key参数
    drawer: const NavigationDrawer(),
    appBar: AppBar(
      title: Text(miEmail ?? "用户页面"),
    ),
    body: const Column(
      children: [
        // 页面内容
      ],
    ),
  ),
);

关键修复点说明

  • 空值安全:使用??运算符提供默认值,避免强制解包!引发的空值错误
  • 状态全覆盖:处理StreamBuilder的加载、错误、空数据状态,保证UI稳定
  • 资源兜底:NetworkImage为空时使用本地占位图,防止图片加载失败
  • 用户校验:提前判断用户登录状态,避免未登录时无效请求Firestore数据

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 03:00:02