← Files FirebaseARCHIVED FILE
skills/firebase-auth-basics/references/flutter_setup.md
5.46 KB · Sep 30, 2026 · 23:01 UTC
# 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 with `authenticate()`.
- **Token Separation**: The `GoogleSignInAuthentication` object no longer
packages both identity and authorization tokens together. Initial
authentication now only provides the `idToken`. If an `accessToken` is
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 a `clientId` argument OR specifying the
`<meta name="google-signin-client_id" ... />` tag in `web/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 local `GoogleSignIn` package initialization entirely:
```dart
import 'package:flutter/foundation.dart' show kIsWeb;
if (!kIsWeb) {
await GoogleSignIn.instance.initialize();
}
```
## 3. Web Logout Crashes
- If you bypassed `GoogleSignIn` initialization on the web (as demonstrated
above), you cannot call its `signOut()` method later. Attempting to execute
`await 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`:
```dart
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 resulting `List` in Dart.
## 5. Robust `AuthService` Boilerplate
Here is a comprehensive `AuthService` implementation that properly handles the
initialization and platform differences between Flutter Web and Mobile:
```dart
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();
if (googleUser == null) return null; // Cancelled
final GoogleSignInAuthentication googleAuth = await 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 `localhost` during local testing) is not listed
in the Authorized Domains in the Firebase Console.
- **Solution**: Add `localhost` to the Authorized Domains list in the Firebase
Console (Authentication > Settings > Authorized domains) or in your
`firebase.json` auth config.
- **CRITICAL**: Do NOT include the protocol or port number when adding the
domain (e.g., use `localhost`, NOT `http://localhost:9090`). Flutter Web often
runs on random ports or specific ports, but Firebase Auth only cares about the
domain.
SHA-256: 181fdba6d78f2d2da85bfccb30ac560b85c61c55454381763725cfe4f2467110