Supabase Auth handles user registration, login, and session management out of the box. This guide covers setting it up in Flutter with email/password, native Google sign-in, and native Apple sign-in.
For the full reference, see the Supabase Flutter Auth docs.
Setup
Add the Supabase Flutter package to your pubspec.yaml:
dependencies:
supabase_flutter: ^2.0.0Initialize Supabase before your app runs. You can find your project URL and anon key in the Supabase dashboard under Settings → API.
import 'package:supabase_flutter/supabase_flutter.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Supabase.initialize(
url: 'https://your-project.supabase.co',
anonKey: 'your-anon-key',
);
runApp(MyApp());
}
// Access the client anywhere in your app
final supabase = Supabase.instance.client;Email & Password
Sign Up
Creates a new account. Supabase sends a confirmation email by default. You can disable this in the dashboard under Auth → Settings.
final response = await supabase.auth.signUp(
email: 'user@example.com',
password: 'password123',
);
final user = response.user;Sign In
Logs in an existing user and returns a session with access and refresh tokens. The SDK stores these locally and refreshes them automatically.
final response = await supabase.auth.signInWithPassword(
email: 'user@example.com',
password: 'password123',
);
final session = response.session;
final user = response.user;Sign Out
Clears the local session and invalidates the refresh token.
await supabase.auth.signOut();Google Sign In
Native Google sign-in gives the best user experience (the system account picker instead of a web view). The flow: your app gets an ID token from Google, then sends it to Supabase to create or log in the user.
You need to set up OAuth credentials in the Google Cloud Console and enable Google as an auth provider in the Supabase dashboard under Auth → Providers. Then add the google_sign_in package.
import 'package:google_sign_in/google_sign_in.dart';
Future<void> signInWithGoogle() async {
// Show the native Google account picker
final googleSignIn = GoogleSignIn(
serverClientId: 'your-web-client-id.apps.googleusercontent.com',
);
final googleUser = await googleSignIn.signIn();
if (googleUser == null) return; // user cancelled
// Get the ID token from Google
final googleAuth = await googleUser.authentication;
final idToken = googleAuth.idToken;
if (idToken == null) throw 'No ID token';
// Send the token to Supabase to create/login the user
await supabase.auth.signInWithIdToken(
provider: OAuthProvider.google,
idToken: idToken,
);
}Apple Sign In
Works the same way as Google: get an ID token from Apple, send it to Supabase. You need to enable Sign in with Apple in your Xcode project under Signing & Capabilities, and enable Apple as a provider in the Supabase dashboard. Add the sign_in_with_apple package.
import 'package:sign_in_with_apple/sign_in_with_apple.dart';
Future<void> signInWithApple() async {
// Show the native Apple sign-in sheet
final credential = await SignInWithApple.getAppleIDCredential(
scopes: [
AppleIDAuthorizationScopes.email,
AppleIDAuthorizationScopes.fullName,
],
);
// Send the token to Supabase
await supabase.auth.signInWithIdToken(
provider: OAuthProvider.apple,
idToken: credential.identityToken!,
);
}Sessions
Supabase handles sessions automatically. The SDK stores the session on the device, refreshes tokens in the background when they expire, and restores the session when the app restarts. You rarely need to manage this yourself.
// Get the current session (null if not logged in)
final session = supabase.auth.currentSession;
// Get the current user (null if not logged in)
final user = supabase.auth.currentUser;
// Quick check
final isLoggedIn = supabase.auth.currentSession != null;Auth State Changes
Listen to auth events to react when the user signs in, signs out, or the session refreshes. This is where you handle navigation after login or redirect to a login screen after sign out.
supabase.auth.onAuthStateChange.listen((data) {
final event = data.event;
switch (event) {
case AuthChangeEvent.signedIn:
// User just logged in — navigate to home
break;
case AuthChangeEvent.signedOut:
// User logged out — navigate to login
break;
case AuthChangeEvent.tokenRefreshed:
// Token was refreshed in the background, no action needed
break;
default:
break;
}
});A common pattern: listen in your root widget's initStateand redirect based on the event. Or combine with go_router's redirect to guard routes automatically.