Supabase Flutter: A Comprehensive Guide for Flutter Developers

Published on by Flutter News Hub

Supabase Flutter: A Comprehensive Guide for Flutter Developers

Supabase is a powerful open-source Firebase alternative that offers a range of tools for building modern web and mobile applications. Its Flutter client library, supabase_flutter, provides an easy-to-use interface to integrate Supabase's features into your Flutter apps.

Authentication

supabase_flutter supports multiple authentication methods, including:

1. Email and Password:

await supabase.auth.signUp(
  email: "[email protected]",
  password: "my_password",
);

await supabase.auth.signInWithPassword(
  email: "[email protected]",
  password: "my_password",
);

2. Magic Link:

await supabase.auth.signInWithOtp(
  email: '[email protected]',
);

3. Native Apple Sign In:

final credential = await SignInWithApple.getAppleIDCredential(
  scopes: [
    AppleIDAuthorizationScopes.email,
    AppleIDAuthorizationScopes.fullName,
  ],
  nonce: hashedNonce,
);

final idToken = credential.identityToken;
if (idToken == null) {
  throw const AuthException('Could not find ID Token from generated credential.');
}

return signInWithIdToken(
  provider: OAuthProvider.apple,
  idToken: idToken,
  nonce: rawNonce,
);

4. Native Google Sign In:

final googleUser = await googleSignIn.signIn();
final googleAuth = await googleUser!.authentication;
final accessToken = googleAuth.accessToken;
final idToken = googleAuth.idToken;
if (accessToken == null) {
  throw 'No Access Token found.';
}
if (idToken == null) {
  throw 'No ID Token found.';
}

return supabase.auth.signInWithIdToken(
  provider: OAuthProvider.google,
  idToken: idToken,
  accessToken: accessToken,
);

Database

supabase_flutter provides methods for performing basic CRUD operations using the Supabase REST API:

// Select data with filters
final data = await supabase
  .from('cities')
  .select()
  .eq('country_id', 1) // equals filter
  .neq('name', 'The shire'); // does not equal filter

// Insert a new row
await supabase
  .from('cities')
  .insert({'name': 'The Shire', 'country_id': 554});

Realtime

1. Accessing Realtime Data as a Stream:

final stream = supabase.from('countries').stream(primaryKey: ['id']);

StreamBuilder(
  stream: stream,
  builder: (context, snapshot) {
    // return your widget with the data from snapshot
  },
);

2. Postgres Changes:

Listen to changes in your Supabase tables:

final myChannel = supabase.channel('my_channel');

myChannel
  .onPostgresChanges(
    event: PostgresChangeEvent.all,
    schema: 'public',
    table: 'countries',
    callback: (payload) {
      // Do something fun or interesting when there is an change on the database
    },
  )
  .subscribe();

3. Broadcast:

Bypassing the database, send and receive low latency messages between connected clients:

final myChannel = supabase.channel('my_channel');

// Subscribe to `cursor-pos` broadcast event
myChannel
  .onBroadcast(
    event: 'cursor-pos',
    callback: (payload) {} // Do something fun or interesting when there is an change on the database
  )
  .subscribe();

// Send a broadcast message to other connected clients
await myChannel.sendBroadcastMessage(
  event: 'cursor-pos',
  payload: {'x': 30, 'y': 50},
);

4. Presence:

Easily implement "I'm online" features:

final myChannel = supabase.channel('my_channel');

// Subscribe to presence events
myChannel
  .onPresence(
    event: PresenceEvent.sync,
    callback: (payload) {
      final onlineUsers = myChannel.presenceState(); // handle sync event
    },
  )
  .onPresence(
    event: PresenceEvent.join,
    callback: (payload) { // New users have joined
    },
  )
  .onPresence(
    event: PresenceEvent.leave,
    callback: (payload) { // Users have left
    },
  )
  .subscribe(((status, [_]) async {
    if (status == RealtimeSubscribeStatus.subscribed) {
      // Send the current user's state upon subscribing
      final status = await myChannel
        .track({'online_at': DateTime.now().toIso8601String()});
    }
  }));

Storage

supabase_flutter simplifies file upload and download:

final file = File('example.txt');
file.writeAsStringSync('File content');
await supabase.storage
  .from('my_bucket')
  .upload('my/path/to/files/example.txt', file);

Edge Functions

Invoke Supabase Edge Functions from your Flutter app:

final data = await supabase.functions.invoke('get_countries');

Deep Links

Set up deep links to handle authentication flows and more:

1. Dashboard Deep Link Config:

  • Enter your app redirect callback on the "Additional Redirect URLs" field in your Supabase Auth settings.
  • Use a format like [YOUR_SCHEME]://[YOUR_HOSTNAME] (e.g., io.supabase.flutterdemo://login-callback).

2. Flutter Deep Link Config:

  • Follow platform-specific instructions from the app_links package.

Custom LocalStorage

supabase_flutter supports custom implementations for session persistence. For example, using flutter_secure_storage:

class MySecureStorage extends LocalStorage {
  final storage = FlutterSecureStorage();

  @override
  Future persistSession(String persistSessionString) async {
    return storage.write(key: supabasePersistSessionKey, value: persistSessionString);
  }
}

// Use it when initializing Supabase
Supabase.initialize(
  // ...
  authOptions: FlutterAuthClientOptions(
    localStorage: MySecureStorage(),
  ),
);

Resources

Flutter News Hub