自定义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
相关产品推荐
相关产品推荐

