Firebase Auth & Google Sign-In for Flutter
When integrating Firebase Authentication and Google Sign-In into Flutter apps
targeting cross-platform environments (like Mobile + Web), you must navigate
several breaking changes introduced in google_sign_in 7.x+ and some
platform-specific quirks.
1. google_sign_in 7.2.0 API Changes
- Method Renamed: The
signIn()method is deprecated/removed and has been replaced withauthenticate(). - Token Separation: The
GoogleSignInAuthenticationobject no longer packages both identity and authorization tokens together. Initial authentication now only provides theidToken. If anaccessTokenis required for Google APIs, you must explicitly request server authorization separately.
2. Initialization & Web Hang/Crash Pitfalls
Initialization Requirement: In 7.x, you must call
await GoogleSignIn.instance.initialize();globally before using the plugin.Web Client ID Constraint: On Flutter Web, if you call
initialize()without passing aclientIdargument OR specifying the<meta name="google-signin-client_id" ... />tag inweb/index.html, the Dart Web Debug Service (DWDS) and the app will throw an assertion error and hang infinitely, resulting in a blank screen.Common Workaround: If you intend to use Firebase Auth's
signInWithPopup(GoogleAuthProvider())for the web, you can conditionally skip the localGoogleSignInpackage initialization entirely:import 'package:flutter/foundation.dart' show kIsWeb; if (!kIsWeb) { await GoogleSignIn.instance.initialize(); }
3. Web Logout Crashes
If you bypassed
GoogleSignIninitialization on the web (as demonstrated above), you cannot call itssignOut()method later. Attempting to executeawait GoogleSignIn.instance.signOut();during the user's logout flow on the Web platform evaluates against an uninitialized context or unsupported environment, crashing the app.Solution: Conditionally separate the logout logic for Web to rely entirely on
FirebaseAuth:if (!kIsWeb) { await GoogleSignIn.instance.signOut(); } await FirebaseAuth.instance.signOut();
4. Prototyping Workaround: Bypassing Firestore Composite Indices
Note: This is a Firestore consideration frequently encountered while fetching user-specific auth data.
When querying data via FirebaseFirestore.instance, using
.where('userId', isEqualTo: uid) combined with a sort on a different field
like .orderBy('createdAt', descending: true) mandates a custom composite
index.
- Quick Alternative: During local development, you can avoid defining
indexes by pulling the data using only
.where()and applying the.sort()operation client-side on the resultingListin Dart.
5. Robust AuthService Boilerplate
Here is a comprehensive AuthService implementation that properly handles the
initialization and platform differences between Flutter Web and Mobile:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/foundation.dart';
import 'package:google_sign_in/google_sign_in.dart';
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
AuthService() {
if (!kIsWeb) {
GoogleSignIn.instance.initialize();
}
}
// Stream to listen to auth state changes
Stream<User?> get authStateChanges => _auth.authStateChanges();
// Get current user
User? get currentUser => _auth.currentUser;
// Google Sign-In
Future<UserCredential?> signInWithGoogle() async {
try {
if (kIsWeb) {
// Web uses popup to avoid DWDS hangs and manual client ID config
GoogleAuthProvider authProvider = GoogleAuthProvider();
return await _auth.signInWithPopup(authProvider);
} else {
// Mobile uses standard flow
final GoogleSignInAccount googleUser = await GoogleSignIn.instance.authenticate();
final GoogleSignInAuthentication googleAuth = googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.credential(
idToken: googleAuth.idToken,
);
return await _auth.signInWithCredential(credential);
}
} catch (e) {
print("Error during Google Sign-In: $e");
return null;
}
}
// Sign out
Future<void> signOut() async {
try {
if (!kIsWeb) {
await GoogleSignIn.instance.signOut();
}
await _auth.signOut();
} catch (e) {
print("Error signing out: $e");
}
}
}6. Troubleshooting auth/unauthorized-domain on Flutter Web
When running Flutter Web locally and using signInWithPopup, you might
encounter a situation where the Google Sign-In popup opens and immediately
closes.
- Symptom: The console shows
Sign-in failed: [firebase_auth/unauthorized-domain] This domain is not authorized for OAuth operation for your Firebase project. - Cause: The domain (usually
localhostduring local testing) is not listed in the Authorized Domains in the Firebase Console. - Solution: Add
localhostto the Authorized Domains list in the Firebase Console (Authentication > Settings > Authorized domains). - CRITICAL: Do NOT include the protocol or port number when adding the
domain (e.g., use
localhost, NOThttp://localhost:9090). Flutter Web often runs on random ports or specific ports, but Firebase Auth only cares about the domain.