diff --git a/packages/firebase_auth/firebase_auth_web/lib/firebase_auth_web.dart b/packages/firebase_auth/firebase_auth_web/lib/firebase_auth_web.dart index 182ff3490dd1..2200306a0044 100644 --- a/packages/firebase_auth/firebase_auth_web/lib/firebase_auth_web.dart +++ b/packages/firebase_auth/firebase_auth_web/lib/firebase_auth_web.dart @@ -89,7 +89,9 @@ class FirebaseAuthWeb extends FirebaseAuthPlatform { /// Called by PluginRegistry to register this plugin for Flutter Web static void registerWith(Registrar registrar) { - FirebaseCoreWeb.registerService('auth'); + FirebaseCoreWeb.registerService('auth', () async { + await FirebaseAuthWeb.instance.delegate.onWaitInitState(); + }); FirebaseAuthPlatform.instance = FirebaseAuthWeb.instance; PhoneMultiFactorGeneratorPlatform.instance = PhoneMultiFactorGeneratorWeb(); RecaptchaVerifierFactoryPlatform.instance = diff --git a/packages/firebase_auth/firebase_auth_web/lib/src/interop/auth.dart b/packages/firebase_auth/firebase_auth_web/lib/src/interop/auth.dart index 68f01652c9bc..13b3b657c803 100644 --- a/packages/firebase_auth/firebase_auth_web/lib/src/interop/auth.dart +++ b/packages/firebase_auth/firebase_auth_web/lib/src/interop/auth.dart @@ -362,6 +362,28 @@ class Auth extends JsObjectWrapper { auth_interop.AuthSettings get settings => jsObject.settings; + User? _initUser; + + /// On web we need to wait for the first onAuthStateChanged event to fire + /// in order to be sure that the currentUser is set. + /// To preserve behavior on web and mobile we store the initial user + /// in `_initUser` and add it manually to the `_changeController`. + Future onWaitInitState() async { + final completer = Completer(); + final nextWrapper = allowInterop((auth_interop.UserJsImpl? user) { + _initUser = User.getInstance(user); + completer.complete(); + }); + + final errorWrapper = allowInterop((e) => _changeController!.addError(e)); + + final unsubscribe = jsObject.onAuthStateChanged(nextWrapper, errorWrapper); + + await completer.future; + + unsubscribe(); + } + Func0? _onAuthUnsubscribe; // TODO(rrousselGit): fix memory leak – the controller isn't closed even in onCancel // ignore: close_sinks @@ -397,6 +419,8 @@ class Auth extends JsObjectWrapper { onCancel: stopListen, sync: true, ); + + _changeController!.add(_initUser); } return _changeController!.stream; } diff --git a/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart b/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart index e8ab0c0a2bea..86043dc396c3 100644 --- a/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart +++ b/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart @@ -14,23 +14,44 @@ class FirebaseWebService { /// property allows overriding of web naming to Flutterfire plugin naming. String? override; + /// Function to call to ensure the Firebase Service is initalized. + /// Usually used to ensure that the Web SDK match the behavior + /// of native SDKs. + EnsurePluginInitialized ensurePluginInitialized; + /// Creates a new [FirebaseWebService]. - FirebaseWebService._(this.name, [this.override]); + FirebaseWebService._( + this.name, { + this.override, + this.ensurePluginInitialized, + }); } +typedef EnsurePluginInitialized = Future Function()?; + /// The entry point for accessing Firebase. /// /// You can get an instance by calling [FirebaseCore.instance]. class FirebaseCoreWeb extends FirebasePlatform { static Map _services = { - 'core': FirebaseWebService._('app', 'core'), - 'app-check': FirebaseWebService._('app-check', 'app_check'), - 'remote-config': FirebaseWebService._('remote-config', 'remote_config'), + 'core': FirebaseWebService._('app', override: 'core'), + 'app-check': FirebaseWebService._('app-check', override: 'app_check'), + 'remote-config': + FirebaseWebService._('remote-config', override: 'remote_config'), }; /// Internally registers a Firebase Service to be initialized. - static void registerService(String service) { - _services.putIfAbsent(service, () => FirebaseWebService._(service)); + static void registerService( + String service, [ + EnsurePluginInitialized? ensurePluginInitialized, + ]) { + _services.putIfAbsent( + service, + () => FirebaseWebService._( + service, + ensurePluginInitialized: ensurePluginInitialized, + ), + ); } /// Registers that [FirebaseCoreWeb] is the platform implementation. @@ -251,6 +272,18 @@ class FirebaseCoreWeb extends FirebasePlatform { } } + await Future.wait( + _services.values.map((service) { + final ensureInitializedFunction = service.ensurePluginInitialized; + + if (ensureInitializedFunction == null) { + return Future.value(); + } + + return ensureInitializedFunction(); + }), + ); + return _createFromJsApp(app!); } diff --git a/packages/firebase_ui_auth/example/lib/main.dart b/packages/firebase_ui_auth/example/lib/main.dart index ecdec6c5b684..7187aaec4333 100644 --- a/packages/firebase_ui_auth/example/lib/main.dart +++ b/packages/firebase_ui_auth/example/lib/main.dart @@ -5,19 +5,18 @@ import 'package:firebase_auth/firebase_auth.dart' hide PhoneAuthProvider, EmailAuthProvider; import 'package:firebase_core/firebase_core.dart'; -import 'package:flutter/material.dart'; import 'package:firebase_ui_auth/firebase_ui_auth.dart'; import 'package:firebase_ui_localizations/firebase_ui_localizations.dart'; import 'package:firebase_ui_oauth_apple/firebase_ui_oauth_apple.dart'; import 'package:firebase_ui_oauth_facebook/firebase_ui_oauth_facebook.dart'; import 'package:firebase_ui_oauth_google/firebase_ui_oauth_google.dart'; import 'package:firebase_ui_oauth_twitter/firebase_ui_oauth_twitter.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; -import 'firebase_options.dart'; - import 'config.dart'; import 'decorations.dart'; +import 'firebase_options.dart'; final actionCodeSettings = ActionCodeSettings( url: 'https://flutterfire-e2e-tests.firebaseapp.com',