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:

Code
flutter pub add supabase_flutter

This 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.

Dart / Flutter
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.

Code
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.

Code
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.

Code
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-define or a secure environment manager.
  • Skipping WidgetsFlutterBinding.ensureInitialized() before calling Supabase.initialize. This can cause null‑pointer errors on some platforms.
  • Ignoring auth state changes. Listen to supabase.auth.onAuthStateChange to 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.