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

Flutter中Firebase authStateChanges未等API完成即跳转路由求助

解决Firebase authStateChanges提前跳转,未等待REST API完成的问题

问题核心是Firebase的authStateChanges会在认证成功(比如OTP验证通过)时立即触发,而后续的用户数据保存API是异步操作,导致路由跳转先于API执行完成。以下是几种可行的解决方案:

方案1:组合认证状态与数据同步状态流

创建一个组合流,仅当用户认证成功且数据同步完成时,才触发路由跳转。

步骤1:在AuthService中添加同步状态流

使用BehaviorSubject(需依赖rxdart)或普通StreamController跟踪数据同步状态:

import 'package:rxdart/rxdart.dart';
import 'package:firebase_auth/firebase_auth.dart';

class AuthService {
  final FirebaseAuth _account = FirebaseAuth.instance;
  final BehaviorSubject<bool> _userDataSynced = BehaviorSubject.seeded(false);

  Stream<User?> get authStateChange => _account.authStateChanges();
  Stream<bool> get userDataSynced => _userDataSynced.stream;

  Future<void> verifyOTp(String otp, String verificationId) async {
    try {
      PhoneAuthCredential credential = PhoneAuthProvider.credential(
        verificationId: verificationId,
        smsCode: otp,
      );
      UserCredential userCredential = await _account.signInWithCredential(credential);
      
      // 先执行所有用户数据保存API
      await createNewVendor(userCredential.user!.uid);
      // 其他必要的API操作...
      
      // 标记数据同步完成
      _userDataSynced.add(true);
    } catch (e) {
      _userDataSynced.add(false);
      // 错误处理:弹窗提示、登出Firebase
      await _account.signOut();
    }
  }

  // 其他方法...
}

步骤2:在main.dart中监听组合流

将认证状态流与同步状态流组合,只有两个条件都满足时才跳转首页:

StreamBuilder(
  stream: Stream.combineLatest2(
    authService.authStateChange,
    authService.userDataSynced,
    (User? user, bool isSynced) => user != null && isSynced,
  ),
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return const SplashScreen();
    }
    return snapshot.data == true 
        ? const HomeScreen() 
        : const LoginScreen();
  },
)

方案2:手动控制路由跳转,跳过authStateChanges自动触发

在OTP验证完成后,先执行API操作,完成后手动跳转路由,同时在authStateChanges监听中添加同步状态判断。

步骤1:修改verifyOTp方法

执行API操作完成后手动跳转,并通过本地存储标记同步状态:

import 'package:shared_preferences/shared_preferences.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';

Future<void> verifyOTp(String otp, String verificationId, BuildContext context) async {
  try {
    PhoneAuthCredential credential = PhoneAuthProvider.credential(
      verificationId: verificationId,
      smsCode: otp,
    );
    await FirebaseAuth.instance.signInWithCredential(credential);
    
    // 显示加载弹窗,防止用户操作
    showDialog(
      context: context,
      barrierDismissible: false,
      builder: (_) => const Center(child: CircularProgressIndicator()),
    );
    
    // 执行数据保存API
    await createNewVendor(FirebaseAuth.instance.currentUser!.uid);
    // 其他API操作...
    
    // 标记同步完成到本地存储
    final prefs = await SharedPreferences.getInstance();
    await prefs.setBool('user_data_synced', true);
    
    // 关闭弹窗并手动跳转首页
    Navigator.pop(context);
    Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => const HomeScreen()));
  } catch (e) {
    await FirebaseAuth.instance.signOut();
    // 错误提示
    ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(e.toString())));
  }
}

步骤2:在main.dart中添加同步状态判断

import 'package:shared_preferences/shared_preferences.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';

StreamBuilder<User?>(
  stream: FirebaseAuth.instance.authStateChanges(),
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return const SplashScreen();
    }
    final user = snapshot.data;
    if (user != null) {
      return FutureBuilder<bool>(
        future: SharedPreferences.getInstance().then((prefs) => prefs.getBool('user_data_synced') ?? false),
        builder: (context, prefsSnapshot) {
          if (prefsSnapshot.connectionState == ConnectionState.waiting) {
            return const LoadingScreen();
          }
          return prefsSnapshot.data == true 
              ? const HomeScreen() 
              : const OtpVerificationScreen();
        },
      );
    } else {
      // 清除同步标记,避免下次登录残留
      SharedPreferences.getInstance().then((prefs) => prefs.setBool('user_data_synced', false));
      return const LoginScreen();
    }
  },
)

方案3:使用Firebase自定义Claims标记同步状态

通过Firebase用户自定义Claims记录数据同步状态,确保只有同步完成后才跳转。

步骤1:在verifyOTp中设置自定义Claim

import 'package:firebase_auth/firebase_auth.dart';

Future<void> verifyOTp(String otp, String verificationId) async {
  try {
    PhoneAuthCredential credential = PhoneAuthProvider.credential(
      verificationId: verificationId,
      smsCode: otp,
    );
    UserCredential userCredential = await FirebaseAuth.instance.signInWithCredential(credential);
    final user = userCredential.user!;
    
    // 执行数据保存API
    await createNewVendor(user.uid);
    
    // 设置自定义Claim标记同步完成
    await FirebaseAuth.instance.setCustomUserClaims(user.uid, {'dataSynced': true});
    
    // 刷新用户令牌,使新Claim生效
    await user.reload();
  } catch (e) {
    await FirebaseAuth.instance.signOut();
    // 错误处理
  }
}

步骤2:在main.dart中检查自定义Claim

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';

StreamBuilder<User?>(
  stream: FirebaseAuth.instance.authStateChanges(),
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return const SplashScreen();
    }
    final user = snapshot.data;
    if (user != null) {
      return FutureBuilder<IdTokenResult>(
        future: user.getIdTokenResult(),
        builder: (context, tokenSnapshot) {
          if (tokenSnapshot.connectionState == ConnectionState.waiting) {
            return const LoadingScreen();
          }
          bool isSynced = tokenSnapshot.data?.claims?['dataSynced'] ?? false;
          return isSynced 
              ? const HomeScreen() 
              : const OtpVerificationScreen();
        },
      );
    } else {
      return const LoginScreen();
    }
  },
)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 11:34:54