使用自定义AlertSnackbar时触发Null check operator空值错误排查
我有一个main.dart文件,用于根据用户状态返回登录界面或已登录界面,核心代码如下:
@override Widget build(BuildContext context) { return FutureBuilder( future: AuthService.firebase().initialize(), builder: (context, snapshot) { try { final user = AuthService.firebase().currentUser; final userEmail = FirebaseAuth.instance.currentUser?.email; if (user != null) { return FutureBuilder<bool>( future: checkEmail(user, userEmail), builder: (context, snapshot) { if (snapshot.hasData && snapshot.data == true || user.isEmailVerified == true) { updateEmailVerification(userEmail); return LoggedMainView(); } else { AuthService.firebase().sendEmailVerification(); return AlertSnackbar( statusColor: Colors.red, messageStatus: 'Snap!', message: 'Email is not Verified', secondaryMessage: 'Verifiction link has been sent to mail.'); } }, ); } else { return LoginView(); } } catch (e) { return LoginView(); } }, ); }
这段代码会检查用户是否已登录,若已登录则进一步检查邮箱是否已验证。我采用了两种邮箱验证方式:首次注册时用户会被引导至OTP窗口验证邮件中的验证码,验证通过后Firestore中的isVerified字段会更新为true;若用户未验证就再次登录,系统会检查邮箱是否通过Firebase邮箱验证或上述OTP方式验证。如果未通过OTP验证,系统会发送Firebase邮箱验证链接。
现在,当我尝试在登录流程中返回自定义AlertSnackbar组件时,会抛出**"Null check operator used on a null value"**错误,但替换为返回LoginView()则能正常运行。我尝试过将组件放在同一文件中、返回普通Snackbar,都无法解决问题,始终出现相同错误。
错误日志如下:
I/flutter ( 8066): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════ I/flutter ( 8066): The following _TypeError was thrown building FutureBuilder<bool>(state: I/flutter ( 8066): _FutureBuilderState<bool>#ff56f): I/flutter ( 8066): Null check operator used on a null value I/flutter ( 8066): I/flutter ( 8066): The relevant error-causing widget was: I/flutter ( 8066): FutureBuilder<bool> FutureBuilder:file:///D:/my-flutter-app/lib/main.dart:47:20 I/flutter ( 8066): I/flutter ( 8066): When the exception was thrown, this was the stack: I/flutter ( 8066): #0 _SnackBarState.initState (package:flutter/src/material/snack_bar.dart:508:21) I/flutter ( 8066): #1 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:5430:55) I/flutter ( 8066): #2 ComponentElement.mount (package:flutter/src/widgets/framework.dart:5273:5) I/flutter ( 8066): #3 Element.inflateWidget (package:flutter/src/widgets/framework.dart:4182:16) I/flutter ( 8066): #4 Element.updateChild (package:flutter/src/widgets/framework.dart:3701:20) I/flutter ( 8066): #5 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5322:16) I/flutter ( 8066): #6 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5462:11) I/flutter ( 8066): #7 Element.rebuild (package:flutter/src/widgets/framework.dart:5016:7) I/flutter ( 8066): #8 StatefulElement.update (package:flutter/src/widgets/framework.dart:5485:5) I/flutter ( 8066): #9 Element.updateChild (package:flutter/src/widgets/framework.dart:3685:15) I/flutter ( 8066): #10 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5322:16)
我的自定义AlertSnackbar代码如下:
import 'package:flutter/material.dart'; class AlertSnackbar extends SnackBar { AlertSnackbar({ super.key, required Color? statusColor, required String? messageStatus, required String? message, required String? secondaryMessage, }) : super( content: SizedBox( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "$messageStatus", style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w600), ), Row( children: [ Text( "$message", style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w600), ), Text( "$secondaryMessage", style: const TextStyle(fontSize: 12), ), ], ), ], )), behavior: SnackBarBehavior.floating, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(10)), ), backgroundColor: statusColor, ); }
请问该错误的原因是什么?
核心原因
SnackBar不是普通页面级Widget,它的设计定位是临时提示组件,必须通过ScaffoldMessenger来触发显示,不能直接在build方法中作为页面根Widget返回。
从错误栈可以看出,错误发生在_SnackBarState.initState方法中——SnackBar内部依赖ScaffoldMessenger的上下文,直接返回它会导致内部找不到所需的上下文引用,触发空值检查错误。
解决方案
不要直接返回AlertSnackbar,而是返回一个包含Scaffold的完整页面,再通过ScaffoldMessenger在页面初始化时显示Snackbar:
步骤1:创建验证提示页面
class EmailVerificationPage extends StatefulWidget { const EmailVerificationPage({super.key}); @override State<EmailVerificationPage> createState() => _EmailVerificationPageState(); } class _EmailVerificationPageState extends State<EmailVerificationPage> { @override void initState() { super.initState(); // 页面渲染完成后显示Snackbar WidgetsBinding.instance.addPostFrameCallback((_) { ScaffoldMessenger.of(context).showSnackBar( AlertSnackbar( statusColor: Colors.red, messageStatus: 'Snap!', message: 'Email is not Verified', secondaryMessage: 'Verification link has been sent to mail.', ), ); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('邮箱验证')), body: const Center( child: Text('请检查您的邮箱,点击验证链接完成验证'), ), ); } }
步骤2:修改原代码的else分支
else { // 避免重复发送验证邮件,建议将该逻辑移到页面的initState中 AuthService.firebase().sendEmailVerification(); return const EmailVerificationPage(); }
额外注意事项
- 不要在
FutureBuilder的builder方法中直接调用sendEmailVerification(),因为builder会被多次触发,导致重复发送验证邮件,建议将该逻辑移到验证页面的initState中。 - 检查
checkEmail方法的逻辑,确保它不会返回null或触发不必要的重复执行。
内容的提问来源于stack exchange,提问作者Profesor

