Upgrade guide

supabase-flutter focuses on improving the developer experience and making it easier to use. This guide demonstrates how to upgrade from supabase-flutter v0 to v1.

Upgrade the client library#

Update the package in your pubspec.yaml file.

supabase_flutter: ^1.0.0

Error handling#

The way supabase-flutter throws error has changed in v1. In v0, errors were returned as a response. In v1, errors are thrown as exceptions. This makes it more intuitive as a Flutter developer to handle errors.

final res = await supabase.from('my_table').select().execute();
final error = res.error;
if (error != null) {
  // handle error
final data = res.data;

Auth classes and methods#

Usage of SupabaseAuthState and SupabaseAuthRequiredState classes#

In v0, SupabaseAuthState and SupabaseAuthRequiredState were required to handle automatic token refresh and to listen to auth state change. In v1, SupabaseAuthState and SupabaseAuthRequiredState are deprecated, and token refresh will happen automatically just by initializing Supabase. onAuthStateChange can be used to action on auth state change.

await Supabase.initialize(
  url: 'SUPABASE_URL',

class AuthState<T extends StatefulWidget> extends SupabaseAuthState<T> {


class AuthRequiredState<T extends StatefulWidget> extends SupabaseAuthState<T> {

Listening to auth state change#

onAuthStateChange now returns a Stream.

final authSubscription = supabase.auth.onAuthStateChange((event, session) {
  // handle auth state change

// Unsubscribe when no longer needed

Sign in with email and password#

signIn() has been deprecated in favor of more explicit method signatures to help with type hinting. Previously it was difficult for developers to know what they were missing (e.g., a lot of developers didn't realize they could use passwordless magic links).

await supabase.auth.signIn(email: email, password: password);
await supabase.auth.signIn(email: email);

Sign in with a third-party OAuth provider#

await supabase.auth.signInWithProvider(
  options: AuthOptions(
      redirectTo: kIsWeb
          ? null
          : 'io.supabase.flutter://reset-callback/'),

Sign in with phone#

await supabase.auth.signIn(
  phone: '+13334445555',
  password: 'example-password',

Sign in with phone using OTP#

final res = await supabase.auth.signIn(phone: phone);

Reset password for email#

await supabase.auth.api.resetPasswordForEmail(
      AuthOptions(redirectTo: 'io.supabase.flutter://reset-callback/'),

Get the user's current session#

final session = supabase.auth.session();

Get the logged-in user#

final user = supabase.auth.user();

Update user data for a logged-in user#

await supabase.auth.update(
  UserAttributes(data: {'hello': 'world'})

Data methods#

.insert() / .upsert() / .update() / .delete() no longer return rows by default. Previously, these methods return inserted/updated/deleted rows by default (which caused some confusion), and you can opt to not return it by specifying returning: 'minimal'. Now the default behavior is to not return rows. To return inserted/updated/deleted rows, add a .select() call at the end.

Also, calling .execute() at the end of the query was a requirement in v0, but deprecated in v1.

Insert without returning inserted data#

await supabase
  .insert(data, returning: ReturningOption.minimal)

Insert with returning inserted data#

final res = await supabase

Realtime methods#


.stream() no longer needs the .execute() at the end. Also, filtering by eq is a lot easier now. primaryKey is now a named parameter to make it more obvious what to pass.



final subscription = supabase
  .on(SupabaseEventTypes.all, (payload) {
    // Handle realtime payload