mirror of
https://github.com/supabase/supabase.git
synced 2026-07-02 01:54:22 +08:00
582 lines
9.8 KiB
Plaintext
582 lines
9.8 KiB
Plaintext
---
|
|
id: upgrade-guide
|
|
title: Upgrade to supabase-flutter v1
|
|
description: 'Learn how to upgrade to supabase-flutter v1.'
|
|
---
|
|
|
|
import Tabs from '@theme/Tabs'
|
|
import TabItem from '@theme/TabItem'
|
|
|
|
supabase-flutter focuses on improving the developer experience and making it easier to use. This guide will help you upgrade from supabase-flutter v0 to v1.
|
|
|
|
## Upgrade the client library
|
|
|
|
Update the package in your pubspec.yaml file.
|
|
|
|
```yaml
|
|
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.
|
|
|
|
<Tabs
|
|
groupId="version"
|
|
values={[
|
|
{label: 'Before', value: '0.x'},
|
|
{label: 'After', value: '1.x'},
|
|
]}>
|
|
|
|
<TabItem value="0.x">
|
|
|
|
```dart
|
|
final res = await supabase.from('my_table').select().execute();
|
|
final error = res.error;
|
|
if(error != null) {
|
|
// handle error
|
|
}
|
|
final data = res.data;
|
|
```
|
|
|
|
</TabItem>
|
|
|
|
<TabItem value="1.x">
|
|
|
|
```dart
|
|
try {
|
|
final data = supabase.from('my_table').select();
|
|
} catch (error) {
|
|
// handle error
|
|
}
|
|
```
|
|
|
|
</TabItem>
|
|
</Tabs>
|
|
|
|
## Auth classes / 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`](/docs/reference/dart/upgrade-guide#listening-to-auth-state-change) can be used to action on auth state change.
|
|
|
|
<Tabs
|
|
groupId="version"
|
|
values={[
|
|
{label: 'Before', value: '0.x'},
|
|
{label: 'After', value: '1.x'},
|
|
]}>
|
|
|
|
<TabItem value="0.x">
|
|
|
|
```dart
|
|
await Supabase.initialize(
|
|
url: 'SUPABASE_URL',
|
|
anonKey: 'SUPABASE_ANON_KEY',
|
|
);
|
|
...
|
|
|
|
class AuthState<T extends StatefulWidget> extends SupabaseAuthState<T> {
|
|
...
|
|
}
|
|
|
|
...
|
|
|
|
class AuthRequiredState<T extends StatefulWidget> extends SupabaseAuthState<T> {
|
|
...
|
|
}
|
|
```
|
|
|
|
</TabItem>
|
|
|
|
<TabItem value="1.x">
|
|
|
|
```dart
|
|
await Supabase.initialize(
|
|
url: 'SUPABASE_URL',
|
|
anonKey: 'SUPABASE_ANON_KEY',
|
|
);
|
|
```
|
|
|
|
</TabItem>
|
|
</Tabs>
|
|
|
|
### Listening to auth state change
|
|
|
|
`onAuthStateChange` now returns a `Stream`.
|
|
|
|
<Tabs
|
|
groupId="version"
|
|
values={[
|
|
{label: 'Before', value: '0.x'},
|
|
{label: 'After', value: '1.x'},
|
|
]}>
|
|
|
|
<TabItem value="0.x">
|
|
|
|
```dart
|
|
final authSubscription = supabase.auth.onAuthStateChange((event, session) {
|
|
// handle auth state change
|
|
});
|
|
|
|
// Unsubscribe when no longer needed
|
|
authSubscription.data?.unsubscribe();
|
|
```
|
|
|
|
</TabItem>
|
|
|
|
<TabItem value="1.x">
|
|
|
|
```dart
|
|
final authSubscription = supabase.auth.onAuthStateChange.listen((data) {
|
|
final AuthChangeEvent event = data.event;
|
|
final Session? session = data.session;
|
|
// handle auth state change
|
|
});
|
|
|
|
// Unsubscribe when no longer needed
|
|
authSubscription.cancel();
|
|
```
|
|
|
|
</TabItem>
|
|
</Tabs>
|
|
|
|
### Sign in with email and password
|
|
|
|
The signIn() method 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).
|
|
|
|
<Tabs
|
|
groupId="version"
|
|
values={[
|
|
{label: 'Before', value: '0.x'},
|
|
{label: 'After', value: '1.x'},
|
|
]}>
|
|
|
|
<TabItem value="0.x">
|
|
|
|
```dart
|
|
await supabase.auth.signIn(email: email, password: password);
|
|
```
|
|
|
|
</TabItem>
|
|
|
|
<TabItem value="1.x">
|
|
|
|
```dart
|
|
await supabase.auth.signInWithPassword(email: email, password: password);
|
|
```
|
|
|
|
</TabItem>
|
|
</Tabs>
|
|
|
|
### Sign in with magic link
|
|
|
|
<Tabs
|
|
groupId="version"
|
|
values={[
|
|
{label: 'Before', value: '0.x'},
|
|
{label: 'After', value: '1.x'},
|
|
]}>
|
|
|
|
<TabItem value="0.x">
|
|
|
|
```dart
|
|
await supabase.auth.signIn(email: email);
|
|
```
|
|
|
|
</TabItem>
|
|
|
|
<TabItem value="1.x">
|
|
|
|
```dart
|
|
await supabase.auth.signInWithOtp(email: email);
|
|
```
|
|
|
|
</TabItem>
|
|
</Tabs>
|
|
|
|
### Sign in with a third-party OAuth provider
|
|
|
|
<Tabs
|
|
groupId="version"
|
|
values={[
|
|
{label: 'Before', value: '0.x'},
|
|
{label: 'After', value: '1.x'},
|
|
]}>
|
|
|
|
<TabItem value="0.x">
|
|
|
|
```dart
|
|
await supabase.auth.signInWithProvider(
|
|
Provider.github,
|
|
options: AuthOptions(
|
|
redirectTo: kIsWeb
|
|
? null
|
|
: 'io.supabase.flutter://reset-callback/'),
|
|
);
|
|
```
|
|
|
|
</TabItem>
|
|
|
|
<TabItem value="1.x">
|
|
|
|
```dart
|
|
await supabase.auth.signInWithOAuth(
|
|
Provider.github,
|
|
redirectTo: kIsWeb ? null : 'io.supabase.flutter://reset-callback/',
|
|
);
|
|
```
|
|
|
|
</TabItem>
|
|
</Tabs>
|
|
|
|
### Sign in with phone
|
|
|
|
<Tabs
|
|
groupId="version"
|
|
values={[
|
|
{label: 'Before', value: '0.x'},
|
|
{label: 'After', value: '1.x'},
|
|
]}>
|
|
|
|
<TabItem value="0.x">
|
|
|
|
```dart
|
|
await supabase.auth.signIn(
|
|
phone: '+13334445555',
|
|
password: 'example-password',
|
|
);
|
|
```
|
|
|
|
</TabItem>
|
|
|
|
<TabItem value="1.x">
|
|
|
|
```dart
|
|
await supabase.auth.signInWithPassword(
|
|
phone: '+13334445555',
|
|
password: 'example-password',
|
|
);
|
|
```
|
|
|
|
</TabItem>
|
|
</Tabs>
|
|
|
|
### Sign in with phone using OTP
|
|
|
|
<Tabs
|
|
groupId="version"
|
|
values={[
|
|
{label: 'Before', value: '0.x'},
|
|
{label: 'After', value: '1.x'},
|
|
]}>
|
|
|
|
<TabItem value="0.x">
|
|
|
|
```dart
|
|
final res = await supabase.auth.signIn(phone: phone);
|
|
```
|
|
|
|
</TabItem>
|
|
|
|
<TabItem value="1.x">
|
|
|
|
```dart
|
|
await supabase.auth.signInWithOtp(
|
|
phone: phone,
|
|
);
|
|
|
|
// After receiving a SMS with a OTP.
|
|
await supabase.auth.verifyOTP(
|
|
type: OtpType.sms,
|
|
token: token,
|
|
phone: phone,
|
|
);
|
|
```
|
|
|
|
</TabItem>
|
|
</Tabs>
|
|
|
|
### Reset password for email
|
|
|
|
<Tabs
|
|
groupId="version"
|
|
values={[
|
|
{label: 'Before', value: '0.x'},
|
|
{label: 'After', value: '1.x'},
|
|
]}>
|
|
|
|
<TabItem value="0.x">
|
|
|
|
```dart
|
|
await supabase.auth.api.resetPasswordForEmail(
|
|
email,
|
|
options:
|
|
AuthOptions(redirectTo: 'io.supabase.flutter://reset-callback/'),
|
|
);
|
|
```
|
|
|
|
</TabItem>
|
|
|
|
<TabItem value="1.x">
|
|
|
|
```dart
|
|
await supabase.auth.resetPasswordForEmail(
|
|
email,
|
|
redirectTo: kIsWeb ? null : 'io.supabase.flutter://reset-callback/',
|
|
);
|
|
```
|
|
|
|
</TabItem>
|
|
</Tabs>
|
|
|
|
### Get the user's current session
|
|
|
|
<Tabs
|
|
groupId="version"
|
|
values={[
|
|
{label: 'Before', value: '0.x'},
|
|
{label: 'After', value: '1.x'},
|
|
]}>
|
|
|
|
<TabItem value="0.x">
|
|
|
|
```dart
|
|
final session = supabase.auth.session();
|
|
```
|
|
|
|
</TabItem>
|
|
|
|
<TabItem value="1.x">
|
|
|
|
```dart
|
|
final Session? session = supabase.auth.currentSession;
|
|
```
|
|
|
|
</TabItem>
|
|
</Tabs>
|
|
|
|
### Get the logged-in user
|
|
|
|
<Tabs
|
|
groupId="version"
|
|
values={[
|
|
{label: 'Before', value: '0.x'},
|
|
{label: 'After', value: '1.x'},
|
|
]}>
|
|
|
|
<TabItem value="0.x">
|
|
|
|
```dart
|
|
final user = supabase.auth.user();
|
|
```
|
|
|
|
</TabItem>
|
|
|
|
<TabItem value="1.x">
|
|
|
|
```dart
|
|
final User? user = supabase.auth.currentUser;
|
|
```
|
|
|
|
</TabItem>
|
|
</Tabs>
|
|
|
|
### Update user data for a logged-in user
|
|
|
|
<Tabs
|
|
groupId="version"
|
|
values={[
|
|
{label: 'Before', value: '0.x'},
|
|
{label: 'After', value: '1.x'},
|
|
]}>
|
|
|
|
<TabItem value="0.x">
|
|
|
|
```dart
|
|
await supabase.auth.update(
|
|
UserAttributes(data: {'hello': 'world'})
|
|
);
|
|
```
|
|
|
|
</TabItem>
|
|
|
|
<TabItem value="1.x">
|
|
|
|
```dart
|
|
await supabase.updateUser(
|
|
UserAttributes(
|
|
data: { 'hello': 'world' },
|
|
),
|
|
);
|
|
```
|
|
|
|
</TabItem>
|
|
</Tabs>
|
|
|
|
## Data methods
|
|
|
|
`.insert()` / `.upsert()` / `.update()` / `.delete()` don't return rows by default.
|
|
|
|
Previously, these methods return inserted/updated/deleted rows by default (which caused [some confusion](https://github.com/supabase/supabase/discussions/1548)), 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 in v1 `.execute` is deperecated.
|
|
|
|
### Insert without returning inserted data
|
|
|
|
<Tabs
|
|
groupId="version"
|
|
values={[
|
|
{label: 'Before', value: '0.x'},
|
|
{label: 'After', value: '1.x'},
|
|
]}>
|
|
|
|
<TabItem value="0.x">
|
|
|
|
```dart
|
|
await supabase
|
|
.from('my_table')
|
|
.insert(data, returning: ReturningOption.minimal)
|
|
.execute();
|
|
|
|
```
|
|
|
|
</TabItem>
|
|
|
|
<TabItem value="1.x">
|
|
|
|
```dart
|
|
await supabase.from('my_table').insert(data);
|
|
```
|
|
|
|
</TabItem>
|
|
</Tabs>
|
|
|
|
### Insert with returning inserted data
|
|
|
|
<Tabs
|
|
groupId="version"
|
|
values={[
|
|
{label: 'Before', value: '0.x'},
|
|
{label: 'After', value: '1.x'},
|
|
]}>
|
|
|
|
<TabItem value="0.x">
|
|
|
|
```dart
|
|
final res = await supabase
|
|
.from('my_table')
|
|
.insert(data)
|
|
.execute();
|
|
```
|
|
|
|
</TabItem>
|
|
|
|
<TabItem value="1.x">
|
|
|
|
```dart
|
|
final insertedData = await supabase.from('my_table').insert(data).select();
|
|
```
|
|
|
|
</TabItem>
|
|
</Tabs>
|
|
|
|
## Realtime methods
|
|
|
|
### Stream
|
|
|
|
`.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.
|
|
|
|
<Tabs
|
|
groupId="version"
|
|
values={[
|
|
{label: 'Before', value: '0.x'},
|
|
{label: 'After', value: '1.x'},
|
|
]}>
|
|
|
|
<TabItem value="0.x">
|
|
|
|
```dart
|
|
supabase.from('my_table:id=eq.120')
|
|
.stream(['id'])
|
|
.listen();
|
|
```
|
|
|
|
</TabItem>
|
|
|
|
<TabItem value="1.x">
|
|
|
|
```dart
|
|
supabase.from('my_table')
|
|
.stream(primaryKey: ['id'])
|
|
.eq('id', '120')
|
|
.listen();
|
|
```
|
|
|
|
</TabItem>
|
|
</Tabs>
|
|
|
|
### Subscribe
|
|
|
|
<Tabs
|
|
groupId="version"
|
|
values={[
|
|
{label: 'Before', value: '0.x'},
|
|
{label: 'After', value: '1.x'},
|
|
]}>
|
|
|
|
<TabItem value="0.x">
|
|
|
|
```dart
|
|
final subscription = supabase
|
|
.from('countries')
|
|
.on(SupabaseEventTypes.all, (payload) {
|
|
// Handle realtime payload
|
|
})
|
|
.subscribe();
|
|
```
|
|
|
|
</TabItem>
|
|
|
|
<TabItem value="1.x">
|
|
|
|
```dart
|
|
final channel = supabase.channel('*');
|
|
channel.on(
|
|
RealtimeListenTypes.postgresChanges,
|
|
ChannelFilter(event: '*', schema: '*'),
|
|
(payload, [ref]) {
|
|
// Handle realtime payload
|
|
},
|
|
).subscribe();
|
|
```
|
|
|
|
</TabItem>
|
|
</Tabs>
|
|
|
|
### Unsubscribe
|
|
|
|
<Tabs
|
|
groupId="version"
|
|
values={[
|
|
{label: 'Before', value: '0.x'},
|
|
{label: 'After', value: '1.x'},
|
|
]}>
|
|
|
|
<TabItem value="0.x">
|
|
|
|
```dart
|
|
supabase.removeSubscription(subscription);
|
|
```
|
|
|
|
</TabItem>
|
|
|
|
<TabItem value="1.x">
|
|
|
|
```dart
|
|
await supabase.removeChannel(channel);
|
|
```
|
|
|
|
</TabItem>
|
|
</Tabs>
|