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

Flutter Late变量赋值后仍触发LateInitializationError问题排查

问题

在Flutter中使用Firebase手机号认证时,定义了late String类型的verificationId变量,本期望发送OTP时为其赋值供后续使用,但调用submitOTP函数时该变量始终未初始化,触发LateInitializationError。相关Cubit代码如下:

import 'package:bloc/bloc.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:meta/meta.dart';

part 'phone_auth_state.dart';

class PhoneAuthCubit extends Cubit<PhoneAuthState> {
  late String verificationId;

  PhoneAuthCubit() : super(PhoneAuthInitial());

  Future<void> submitPhoneNumber(String phoneNumber) async {
    emit(PhoneAuthLoading());

    await FirebaseAuth.instance.verifyPhoneNumber(
      phoneNumber: '$phoneNumber',
      timeout: const Duration(seconds: 14),
      verificationCompleted: verificationCompleted,
      verificationFailed: verificationFailed,
      codeSent: codeSent,
      codeAutoRetrievalTimeout: codeAutoRetrievalTimeout,
    );
  }

  void verificationCompleted(PhoneAuthCredential credential) async {
    print('verificationCompleted');
    await signIn(credential);
  }

  void verificationFailed(FirebaseAuthException error) {
    print('verificationFailed : ${error.toString()}');
    emit(PhoneAuthFailure(errorMessage: error.toString()));
  }

  void codeSent(String verificationId, int? resendToken) {
    print('codeSent');
    this.verificationId = verificationId;
    emit(PhoneNumberSubmitted());
    print("khlst code sent");
  }

  void codeAutoRetrievalTimeout(String verificationId) {
    print('codeAutoRetrievalTimeout');
  }

  Future<void> submitOTP(String otpCode) async { 
    PhoneAuthCredential credential = PhoneAuthProvider.credential(
        verificationId: this.verificationId, smsCode: otpCode);

    await signIn(credential);
  }

  Future<void> signIn(PhoneAuthCredential credential) async {
    try {
      await FirebaseAuth.instance.signInWithCredential(credential);
      emit(PhoneOTPVerified());
    } catch (error) {
      emit(PhoneAuthFailure(errorMessage: error.toString()));
    }
  }

  Future<void> signOut() async {
    await FirebaseAuth.instance.signOut();
  }

  User getLoggedInUser() {
    User firebaseUser = FirebaseAuth.instance.currentUser!;
    return firebaseUser;
  }
}
解决方案

核心问题分析

LateInitializationError的触发原因有两个:要么codeSent回调根本没执行(verificationId从未被赋值),要么submitOTP在codeSent执行前就被调用了。

针对性修复方案

  1. 替换late变量为可空类型
    把late String verificationId改为String? verificationId,明确变量的未初始化状态,避免强制非空带来的崩溃:

    class PhoneAuthCubit extends Cubit<PhoneAuthState> {
      String? verificationId;
      // ... 其他代码不变
    }
    
  2. 在submitOTP中增加安全校验
    调用认证方法前先检查verificationId是否有效,无效则直接返回错误状态:

    Future<void> submitOTP(String otpCode) async { 
      if (verificationId == null) {
        emit(PhoneAuthFailure(errorMessage: "请先获取验证码"));
        return;
      }
      PhoneAuthCredential credential = PhoneAuthProvider.credential(
          verificationId: verificationId!, smsCode: otpCode);
      await signIn(credential);
    }
    
  3. 确保codeSent回调正常触发
    排查以下几点确认verificationId能被赋值:

    • 手机号必须包含正确的国家码(例如+8613xxxxxxxxx)
    • Firebase控制台已启用手机号认证方式
    • 设备网络正常,能访问Firebase服务
    • 查看控制台日志,确认codeSent和khlst code sent是否打印
  4. 优化状态传递逻辑
    把verificationId通过状态传递,避免依赖Cubit内部变量:

    • 修改PhoneNumberSubmitted状态类,添加verificationId字段:
      class PhoneNumberSubmitted extends PhoneAuthState {
        final String verificationId;
        PhoneNumberSubmitted(this.verificationId);
      }
      
    • 更新codeSent方法:
      void codeSent(String verificationId, int? resendToken) {
        print('codeSent');
        emit(PhoneNumberSubmitted(verificationId));
        print("khlst code sent");
      }
      
    • UI层在收到PhoneNumberSubmitted状态后,把verificationId暂存,调用submitOTP时直接传入,彻底规避Cubit内部变量的生命周期问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 11:13:18