Supabase Auth With Flutter: A Simple Guide
Supabase Auth with Flutter: A Simple Guide
Hey there, fellow developers! Ever found yourself diving into a new app project and thinking, “Man, I need a solid authentication system, but I don’t want to reinvent the wheel?” Well, you’re in luck, because today we’re going to explore how to supercharge your Flutter apps with Supabase Auth .
Table of Contents
Supabase is an absolute game-changer, guys. It’s an open-source Firebase alternative that gives you a PostgreSQL database, real-time subscriptions, and, most importantly for this chat, a seriously robust authentication service. And when you pair it with Flutter, a UI toolkit that lets you build natively compiled applications for mobile, web, and desktop from a single codebase, you’ve got a match made in developer heaven. So, let’s get down to brass tacks and see how we can get a Supabase Auth Flutter example up and running.
Getting Started with Supabase and Flutter
First things first, you’ll need a Supabase project. Head over to
supabase.com
and create a free account if you don’t have one already. Once you’re in, create a new project. You’ll get a project URL and a
anon
key – these are super important, so keep them handy. Think of the
anon
key as your app’s secret handshake to talk to your Supabase backend.
Now, let’s get your Flutter project set up. If you haven’t already, make sure you have Flutter installed. Create a new Flutter project using
flutter create my_supabase_app
. Inside your
pubspec.yaml
file, you’ll need to add the Supabase Flutter client library. So, under
dependencies
, add:
supabase_flutter: ^latest_version
(Replace
latest_version
with the actual latest version number, guys. Always good to check the official documentation for the most up-to-date package info!). After saving, run
flutter pub get
to fetch the package.
Next, in your
lib/main.dart
file, you’ll need to initialize the Supabase client. This is where those project URL and
anon
key come in. You’ll want to do this
before
your app starts rendering anything.
import 'package:flutter/material.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Supabase.initialize(
url: 'YOUR_SUPABASE_URL',
anonKey: 'YOUR_SUPABASE_ANON_KEY',
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
// Your app's main widget will go here
return MaterialApp(
title: 'Supabase Auth Demo',
home: Scaffold(
appBar: AppBar(title: const Text('Supabase Auth Demo')),
body: const Center(
child: Text('Welcome to the Supabase Auth Demo!'),
),
),
);
}
}
See? Pretty straightforward, right? We’re just telling our Flutter app how to connect to our Supabase project. This initial setup is the bedrock of your
Supabase Auth Flutter example
, so nailing it is key. Don’t forget to replace
'YOUR_SUPABASE_URL'
and
'YOUR_SUPABASE_ANON_KEY'
with your actual credentials. It’s like giving your app the address and key to its own personal cloud backend. This step ensures that every subsequent interaction with Supabase, especially for authentication, is securely and correctly routed.
Implementing Sign Up with Email and Password
Alright, now for the fun part – actually letting users sign up! Supabase Auth makes this a breeze. We’ll create a simple UI for signing up, typically involving two
TextField
widgets for email and password, and a button to trigger the sign-up action.
Let’s imagine you have a
SignUpScreen
widget. Here’s a peek at how you might structure it:
import 'package:flutter/material.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
class SignUpScreen extends StatefulWidget {
const SignUpScreen({super.key});
@override
State<SignUpScreen> createState() => _SignUpScreenState();
}
class _SignUpScreenState extends State<SignUpScreen> {
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
bool _isLoading = false;
Future<void> _signUp() async {
setState(() {
_isLoading = true;
});
try {
final email = _emailController.text;
final password = _passwordController.text;
final AuthResponse res = await Supabase.instance.client.auth.signUp(
email: email,
password: password,
);
// You might want to send a confirmation email here
// final user = res.user;
// Handle success, maybe navigate to a welcome screen or login
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Sign up successful! Please check your email.')),
);
// Navigate to login or another screen after successful signup
// Navigator.of(context).pushReplacementNamed('/login');
} catch (error) {
// Handle errors, show a message to the user
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Sign up failed: ${error.toString()}')),
);
} finally {
setState(() {
_isLoading = false;
});
}
}
@override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Sign Up'))
,
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: _emailController,
decoration: const InputDecoration(labelText: 'Email')
,
keyboardType: TextInputType.emailAddress,
),
const SizedBox(height: 16.0),
TextField(
controller: _passwordController,
decoration: const InputDecoration(labelText: 'Password')
,
obscureText: true,
),
const SizedBox(height: 24.0),
_isLoading
? const CircularProgressIndicator()
: ElevatedButton(
onPressed: _signUp,
child: const Text('Sign Up')),
TextButton(
onPressed: () {
// Navigate to login screen
// Navigator.of(context).pushReplacementNamed('/login');
},
child: const Text('Already have an account? Log in')),
],
),
),
);
}
}
In this snippet, we’re using
TextEditingController
s to grab the user’s input. The magic happens in the
_signUp
method, where we call
Supabase.instance.client.auth.signUp()
. This is the core function that sends the credentials to Supabase. If successful, Supabase usually sends a confirmation email to the user (you can configure this in your Supabase project settings). We then show a success message and, in a real app, you’d navigate the user to a login screen or a dashboard. Error handling is also crucial, so we’re catching potential errors and displaying them. This block is central to your
Supabase Auth Flutter example
because it demonstrates the fundamental user registration flow. Remember to enable email confirmation in your Supabase dashboard under Authentication -> Settings -> Email Templates if you want that extra layer of security!
Handling User Log In
Once a user has signed up (and perhaps confirmed their email), they’ll want to log in. The process is very similar to sign-up, but we use a different Supabase client method. Let’s say we have a
LoginScreen
widget.
import 'package:flutter/material.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
@override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
bool _isLoading = false;
Future<void> _login() async {
setState(() {
_isLoading = true;
});
try {
final email = _emailController.text;
final password = _passwordController.text;
final AuthResponse res = await Supabase.instance.client.auth.signInWithPassword(
email: email,
password: password,
);
// Handle successful login
// final user = res.user;
// Navigate to the main app screen
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Login successful!'))
);
// Navigator.of(context).pushReplacementNamed('/home');
} catch (error) {
// Handle login errors
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Login failed: ${error.toString()}')),
);
} finally {
setState(() {
_isLoading = false;
});
}
}
@override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Log In'))
,
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: _emailController,
decoration: const InputDecoration(labelText: 'Email')
,
keyboardType: TextInputType.emailAddress,
),
const SizedBox(height: 16.0),
TextField(
controller: _passwordController,
decoration: const InputDecoration(labelText: 'Password')
,
obscureText: true,
),
const SizedBox(height: 24.0),
_isLoading
? const CircularProgressIndicator()
: ElevatedButton(
onPressed: _login,
child: const Text('Log In')),
TextButton(
onPressed: () {
// Navigate to sign up screen
// Navigator.of(context).pushReplacementNamed('/signup');
},
child: const Text('Don\'t have an account? Sign up')),
],
),
),
);
}
}
The
_login
method now calls
Supabase.instance.client.auth.signInWithPassword()
. Similar to sign-up, we handle loading states and errors. Upon successful login, the user is authenticated, and you’d typically navigate them to your app’s main content area. This part of the
Supabase Auth Flutter example
is critical for user retention, providing a seamless way for returning users to access their accounts. Remember that Supabase’s authentication is stateless by default, meaning it relies on tokens. The client library handles token management, including refreshing them, so you don’t have to worry too much about that.
Managing the Current User Session
Keeping track of who’s logged in is super important. Supabase Flutter makes this easy with session management. You can check if a user is currently logged in and get their details.
Supabase provides a
supabase.auth.currentUser
property which will be null if no one is logged in, or it will contain the
User
object of the logged-in user. A common pattern is to check this on app startup to determine whether to show the login screen or the main app content.
// In your main app logic or a dedicated auth service
Future<void> checkLoginStatus() async {
final currentUser = Supabase.instance.client.auth.currentUser;
if (currentUser != null) {
// User is logged in, navigate to home screen
// Navigator.of(context).pushReplacementNamed('/home');
print('User is logged in: ${currentUser.id}');
} else {
// User is not logged in, navigate to login screen
// Navigator.of(context).pushReplacementNamed('/login');
print('No user logged in.');
}
}
// You would call this function, perhaps in your MyApp widget's initState
// or when your app starts.
This is a fundamental aspect of any authentication flow. You want your app to remember if a user has already logged in so they don’t have to enter their credentials every single time they open the app. The
currentUser
property is your go-to for this. When it’s not null, it means a valid session is active. You can access user details like
currentUser.id
,
currentUser.email
, and custom metadata if you’ve stored any. This session management piece is what transforms a simple
Supabase Auth Flutter example
into a real-world application experience. It ensures continuity and a personalized feel for your users, making your app feel polished and professional.
Implementing Log Out
Finally, no authentication system is complete without a way to log out. Supabase makes this incredibly simple.
Future<void> _logout() async {
try {
await Supabase.instance.client.auth.signOut();
// After logging out, navigate back to the login screen
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Logged out successfully.'))
);
// Navigator.of(context).pushReplacementNamed('/login');
} catch (error) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Logout failed: ${error.toString()}')),
);
}
}
Just one line:
Supabase.instance.client.auth.signOut()
. This invalidates the current session on the server and clears the local session data. After calling
signOut
, you’ll want to redirect the user back to the login screen, just like in the example. This completes the user lifecycle for your
Supabase Auth Flutter example
, providing a clean and secure way for users to exit their accounts. It’s the final piece of the puzzle that ensures a comprehensive and user-friendly authentication experience.
Conclusion
And there you have it, guys! A whirlwind tour of implementing Supabase Auth in Flutter . We’ve covered setting up Supabase, handling sign-up, logging in, managing sessions, and logging out. Supabase makes it incredibly accessible to add powerful authentication features to your Flutter apps without the headache.
Remember, this is just the tip of the iceberg. Supabase Auth offers much more, including social logins (Google, GitHub, etc.), magic links, multi-factor authentication, and row-level security for your database. But by mastering these fundamentals, you’ve got a fantastic foundation to build upon.
Happy coding, and may your authentication flows always be smooth!