集成Firebase Auth后Flutter报错:'_debugCurrentBuildTarget == context'断言失败
_debugCurrentBuildTarget == context断言失败问题 问题背景
集成Firebase Authentication前项目运行正常,现在出现断言失败错误。
错误信息
Unhandled Exception: 'package:flutter/src/widgets/framework.dart': Failed assertion: line 2609 pos 20: '_debugCurrentBuildTarget == context': is not true.
堆栈跟踪
E/flutter (22184): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: 'package:flutter/src/widgets/framework.dart': Failed assertion: line 2609 pos 20: '_debugCurrentBuildTarget == context': is not true. E/flutter (22184): #0 _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:51:61) E/flutter (22184): #1 _AssertionError._throwNew (dart:core-patch/errors_patch.dart:40:5) E/flutter (22184): #2 BuildOwner.buildScope.<anonymous closure> E/flutter (22184): #3 BuildOwner.buildScope E/flutter (22184): #4 RenderObjectToWidgetAdapter.attachToRenderTree E/flutter (22184): #5 WidgetsBinding.attachRootWidget E/flutter (22184): #6 WidgetsBinding.scheduleAttachRootWidget.<anonymous closure> E/flutter (22184): #7 Timer._createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:18:15) E/flutter (22184): #8 _Timer._runTimers (dart:isolate-patch/timer_impl.dart:398:19) E/flutter (22184): #9 _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:429:5) E/flutter (22184): #10 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:192:12) E/flutter (22184):
相关代码
auth_repo.dart
import 'package:auth/auth.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:auth/firebase_options.dart'; class SignInWithEmailAndPasswordFailure implements Exception { final String code; const SignInWithEmailAndPasswordFailure(this.code); } class SignOutFailure implements Exception {} class AuthRepo { final FirebaseAuth _auth = FirebaseAuth.instance; Future<FirebaseApp> firebaseInit() async { return Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); } Stream<AuthUser> get user { firebaseInit(); return _auth.authStateChanges().map((firebaseUser) { return firebaseUser == null ? AuthUser.empty : AuthUser(id: firebaseUser.uid, email: firebaseUser.email); }); } Future<void> signInWithEmailAndPassword( {required String email, required String password}) async { try { await _auth.signInWithEmailAndPassword(email: email, password: password); } on FirebaseAuthException catch (e) { throw SignInWithEmailAndPasswordFailure(e.code); } } Future<void> signOut() async { try { await _auth.signOut(); } catch (e) { throw SignOutFailure(); } } }
auth_user.dart
import 'package:equatable/equatable.dart'; class AuthUser extends Equatable { final String id; final String? email; const AuthUser({required this.id, this.email,}); static const AuthUser empty = AuthUser(id: ""); @override List<Object?> get props => [id, email]; }
auth.dart
export 'src/auth_repo.dart'; export 'src/auth_user.dart';
auth_state.dart
part of 'auth_controller.dart'; enum AuthenticationStatus { authenticated, unauthenticated } class AuthState extends Equatable { final AuthenticationStatus status; final AuthUser user; const AuthState._({required this.status, this.user = AuthUser.empty}); const AuthState.authenticated(AuthUser user) : this._(status: AuthenticationStatus.authenticated, user: user); const AuthState.unauthenticated() : this._(status: AuthenticationStatus.unauthenticated); @override List<Object?> get props => [status,user]; }
auth_controller.dart
import 'dart:async'; import 'package:auth/auth.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_task/auth_state_management/auth_provider.dart'; part 'auth_state.dart'; final authProvider = StateNotifierProvider<AuthController,AuthState>((ref) => AuthController(ref.watch(authRepoProvider)),); class AuthController extends StateNotifier<AuthState> { final AuthRepo _repo; late final StreamSubscription subscription; AuthController(this._repo) : super(const AuthState.unauthenticated()) { _repo.user.listen((user) => _onUserChanged(user)); } void _onUserChanged(AuthUser user) { if (user == null) { state = const AuthState.unauthenticated(); } else { state = AuthState.authenticated(user); } } void onSignOut() { _repo.signOut(); } @override void dispose() { subscription.cancel(); super.dispose(); } }
main.dart
import 'package:flutter/material.dart'; import 'package:flutter_task/auth_state_management/auth_controller.dart'; import 'package:flutter_task/widgets/email_field.dart'; import 'package:flutter_task/widgets/password_field.dart'; import 'package:flutter_task/widgets/sign_in_button.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; void main() { runApp(const ProviderScope(child: MyApp())); } class MyApp extends ConsumerWidget { const MyApp({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final AuthState authState = ref.watch(authProvider); return MaterialApp( title: 'Attendance Management Sytem', theme: ThemeData( primarySwatch: Colors.teal, ), home: const MyHomePage(), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { TextEditingController email = TextEditingController(); TextEditingController password = TextEditingController(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("Attendance Management System"), ), body: SafeArea( child: SingleChildScrollView( child: Container( padding: const EdgeInsets.all(10), child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: <Widget>[ Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ SizedBox( height: 156, width: 156, child: Image.asset( "images/logo.png", fit: BoxFit.contain, ), ), const SizedBox( height: 10, ), const Text( "Attendance Management System", textAlign: TextAlign.center, style: TextStyle( fontSize: 26, fontWeight: FontWeight.bold, ), ) ], ), ], ), const SizedBox( height: 50, ), Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.stretch, children: const [ EmailField(), SizedBox( height: 15, ), PasswordField(), SizedBox( height: 15, ), SignInButton() ], ) ], ), ), ), ), ); } }
解决方案
这个错误的核心原因是Firebase初始化时机不正确,并且在Stream getter中调用未等待的异步初始化方法,导致UI渲染和Firebase初始化的时序冲突,触发了Flutter框架的上下文断言。
修复步骤:
确保Firebase在应用启动时完成初始化
修改main.dart,在runApp前等待Firebase初始化完成:void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); runApp(const ProviderScope(child: MyApp())); }移除
AuthRepo中usergetter里的firebaseInit()调用
因为Firebase已经在启动时初始化完成,不需要在Stream中重复调用,修改auth_repo.dart的usergetter:Stream<AuthUser> get user { return _auth.authStateChanges().map((firebaseUser) { return firebaseUser == null ? AuthUser.empty : AuthUser(id: firebaseUser.uid, email: firebaseUser.email); }); }同时可以移除
AuthRepo中的firebaseInit方法,或者保留但只在需要的地方调用(现在已经不需要了)。修复
AuthController中的Stream订阅问题
当前代码中subscription变量声明了但没有赋值,会导致dispose时出错,修改AuthController的构造函数:AuthController(this._repo) : super(const AuthState.unauthenticated()) { subscription = _repo.user.listen((user) => _onUserChanged(user)); }修正
_onUserChanged方法的判断逻辑AuthUser不会为null,因为map中返回的是AuthUser.empty或实例,所以判断应该改为:void _onUserChanged(AuthUser user) { if (user == AuthUser.empty) { state = const AuthState.unauthenticated(); } else { state = AuthState.authenticated(user); } }
为什么这些修改有效?
- 提前初始化Firebase并等待完成,避免了异步操作干扰UI渲染流程,解决了上下文断言冲突。
- 修复Stream订阅的赋值问题,防止dispose时出现空指针。
- 修正用户状态判断逻辑,符合
AuthUser的实际定义,避免无效状态切换。
内容的提问来源于stack exchange,提问作者Muhammad Junaid Khalid

