When to Choose the supabase_flutter Flutter Package
The supabase_flutter package gives you a single client for Supabase's auth, Postgres, storage, and realtime features. It’s a great fit when you want a Postgres‑first backend without juggling multiple SDKs, and when you need real‑time data sync across devices.
Installation
Open a terminal at the root of your Flutter project and run:
flutter pub add supabase_flutterThis adds the latest version of supabase_flutter to your pubspec.yaml and fetches the dependency.
Basic Setup
Before you can call any Supabase APIs you must initialize the client. The recommended place is the main() function.
import 'package:flutter/widgets.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
await Supabase.initialize(
// Use --dart-define to keep keys out of source control
url: const String.fromEnvironment('SUPABASE_URL'),
anonKey: const String.fromEnvironment('SUPABASE_ANON_KEY'),
// Optional: enable realtime debugging in development
// realtimeClientOptions: const RealtimeClientOptions(debug: true),
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) => const MaterialApp(home: HomePage());
}Tip: Pass the Supabase URL and anon key via
--dart-define=SUPABASE_URL=…and--dart-define=SUPABASE_ANON_KEY=…when building. This prevents accidental key exposure in version control.
Authentication Example
Signing in with email and password is straightforward. The client returns a Session object on success.
final supabase = Supabase.instance.client;
Future signIn(String email, String password) async {
final response = await supabase.auth.signInWithPassword(
email: email,
password: password,
);
if (response.session != null) {
print('Logged in as ${response.user?.email}');
} else {
print('Login error: ${response.error?.message}');
}
}Fetching Data from Postgres
Supabase uses PostgREST under the hood, so you can write SQL‑like queries with a fluent Dart API.
Future> fetchTodos() async {
final response = await supabase
.from('todos')
.select()
.order('created_at', ascending: false)
.execute();
if (response.error != null) {
throw response.error!;
}
return response.data as List;
}Realtime Subscription
Realtime updates are powered by PostgreSQL replication. Below is a minimal subscription that prints new rows inserted into the todos table.
void subscribeToTodos() {
final subscription = supabase
.from('todos')
.on(SupabaseEventTypes.insert, (payload) {
print('New todo added: ${payload.newRecord}');
})
.subscribe();
// When the widget is disposed, clean up the subscription:
// subscription.unsubscribe();
}Mistakes to Avoid
- Hard‑coding the Supabase URL or anon key in source files. Use
--dart-defineor a secure environment manager. - Skipping
WidgetsFlutterBinding.ensureInitialized()before callingSupabase.initialize. This can cause null‑pointer errors on some platforms. - Ignoring auth state changes. Listen to
supabase.auth.onAuthStateChangeto keep UI in sync. - Leaving realtime subscriptions open after a widget is disposed. Always call
unsubscribe()to avoid memory leaks.
Remember: Supabase keys are public by design (the anon key). Still, treat them like any other credential—rotate them regularly and restrict usage with Row Level Security (RLS) policies in your database.
Next Steps
- Explore Supabase Auth docs for social logins and magic links.
- Use
supabase.storage.from('avatars').upload(...)to handle file uploads. - Implement Row Level Security to protect data per user.
Frequently Asked Questions
Do I need to call Supabase.initialize in every Dart file?
No. Call <code>Supabase.initialize</code> once, typically in <code>main()</code>. After that you can access the client anywhere via <code>Supabase.instance.client</code>.
Can I use supabase_flutter with Flutter Web?
Yes. The package supports iOS, Android, macOS, Linux, Windows, and Web. Ensure you configure CORS in your Supabase project settings for Web usage.
How do I securely store the Supabase service_role key?
The <code>service_role</code> key should never be shipped to the client. Keep it on a trusted server or Cloud Function and call it via a secure API when server‑side operations are required.
What is the difference between <code>signInWithPassword</code> and <code>signInWithOAuth</code>?
<code>signInWithPassword</code> authenticates users with email/password credentials. <code>signInWithOAuth</code> starts an OAuth flow for providers like Google, GitHub, or Apple.
How can I debug realtime events locally?
Enable the <code>debug</code> flag in <code>RealtimeClientOptions</code> when calling <code>Supabase.initialize</code>. The client will print connection logs to the console.