fix: apply redirect for links to the old Dart docs (#9724)

* fix: update some old flutter code to the current version

* feat: add flutter upgrade guides until stream

* fix: make primaryKey named parameter in flutter docs

* complete flutter upgrade guide

* add redirects for incoming traffic for old dart docs
This commit is contained in:
Tyler authored and GitHub committed 2022-10-21 14:12:06 +02:00
1 parent e2ab4fe590
commit 66df16cdff
4 files changed
+591 -56

No files matched your search

@@ -0,0 +1,499 @@
---
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 methods
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).
### 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
<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>
+2 -2
View File
@@ -6,7 +6,7 @@ const sidebars = {
{
type: 'category',
label: 'Getting Started',
items: ['intro', 'installing', 'initializing'],
items: ['intro', 'installing', 'initializing', 'upgrade-guide'],
collapsed: true,
},
{
@@ -23,7 +23,7 @@ const sidebars = {
'generated/auth-currentuser',
'generated/auth-updateuser',
'generated/auth-onauthstatechange',
'generated/reset-password-email',
'generated/auth-resetpasswordforemail',
],
collapsed: true,
},
+55
View File
@@ -1475,4 +1475,59 @@ module.exports = [
source: '/docs/reference/javascript/getsubscriptions',
destination: '/docs/reference/javascript/v1/getsubscriptions',
},
// supabase-flutter v0 to v1 redirects
// v0: /auth-update
// v1: /auth-updateuser
{
permanent: true,
source: '/docs/reference/dart/auth-update',
destination: '/docs/reference/dart/auth-updateuser',
},
// v0: /auth-api-resetpasswordforemail
// v1: /auth-resetpasswordforemail
{
permanent: true,
source: '/docs/reference/dart/reset-password-email',
destination: '/docs/reference/dart/auth-resetpasswordforemail',
},
// signIn method is now split into signInWithPassword ,signInWithPasswordless ,signInWithOAuth
// send traffic to v0 docs instead
{
permanent: true,
source: '/docs/reference/dart/auth-signin',
destination: '/docs/reference/dart/v0/auth-signin',
},
// v0: /auth-session
// v1: /auth-currentsession
{
permanent: true,
source: '/docs/reference/dart/auth-session',
destination: '/docs/reference/dart/auth-currentsession',
},
// v0: /auth-user
// v1: /auth-currentuser
{
permanent: true,
source: '/docs/reference/dart/auth-user',
destination: '/docs/reference/dart/auth-currentuser',
},
// v0: /auth-signinwithprovider
// v1: /auth-signinwithoauth
{
permanent: true,
source: '/docs/reference/dart/v0/auth-signinwithprovider',
destination: '/docs/reference/dart/auth-signinwithoauth',
},
// realtime methods been replaced with new names
{
permanent: true,
source: '/docs/reference/dart/removesubscription',
destination: '/docs/reference/dart/v0/removesubscription',
},
{
permanent: true,
source: '/docs/reference/dart/getsubscriptions',
destination: '/docs/reference/dart/v0/getsubscriptions',
},
]
+35 -54
View File
@@ -100,7 +100,7 @@ pages:
dart: |
```dart
await supabase.auth.signInWithOtp(
phone: 'example@email.com',
phone: '+13334445555',
);
```
auth.signInWithOAuth():
@@ -115,7 +115,7 @@ pages:
isSpotlight: true
dart: |
```dart
await supabase.auth.signInWithProvider(Provider.github);
await supabase.auth.signInWithOAuth(Provider.github);
```
- name: With `redirectTo`
description: |
@@ -123,11 +123,9 @@ pages:
Note that `redirectTo` should be null for Flutter Web.
dart: |
```dart
await supabase.auth.signInWithProvider(
await supabase.auth.signInWithOAuth(
Provider.github,
options: const AuthOptions(
redirectTo: kIsWeb ? null : 'io.supabase.flutter://reset-callback/',
),
redirectTo: kIsWeb ? null : 'io.supabase.flutter://reset-callback/',
);
```
- name: With scopes
@@ -136,8 +134,10 @@ pages:
You may also need to specify the scopes in the provider's OAuth app settings, depending on the provider.
dart: |
```dart
await supabase.auth.signInWithProvider(Provider.github,
options: const AuthOptions(scopes: 'repo gist notifications'));
await supabase.auth.signInWithOAuth(
Provider.github,
scopes: 'repo gist notifications'
);
...
// after user comes back from signin flow
@@ -263,71 +263,52 @@ pages:
isSpotlight: true
dart: |
```dart
final subscription = supabase.auth.onAuthStateChange(
(event, session) {
print(session?.user.id);
// handle auth state change
},
);
final authSubscription = supabase.auth.onAuthStateChange.listen((data) {
final AuthChangeEvent event = data.event;
final Session? session = data.session;
});
```
- name: Listen to a specific event
dart: |
```dart
final subscription = supabase.auth.onAuthStateChange(
(event, session) {
if (event == AuthChangeEvent.signedIn) {
print(session?.user.id);
// handle signIn
}
},
);
final authSubscription = supabase.auth.onAuthStateChange.listen((data) {
final AuthChangeEvent event = data.event;
if (event == AuthChangeEvent.signedIn) {
// handle signIn
}
});
```
- name: Unsubscribe from auth subscription
dart: |
```dart
final subscription = supabase.auth.onAuthStateChange((event, session) {});
final authSubscription = supabase.auth.onAuthStateChange((event, session) {});
subscription.data?.unsubscribe();
authSubscription.cancel();
```
Reset Password (Email):
auth.resetPasswordForEmail:
title: 'resetPasswordForEmail()'
description: |
Sends a reset request to an email address.
notes: |
Sends a reset request to an email address.
When the user clicks the reset link in the email they will be forwarded to:
`<SITE_URL>#access_token=x&refresh_token=y&expires_in=z&token_type=bearer&type=recovery`
Your app must detect `type=recovery` in the fragment and display a password reset form to the user.
You should then use the access_token in the url and new password to update the user as follows:
Sends a password reset request to an email address. When the user clicks the reset link in the email they are redirected back to your application. Prompt the user for a new password and call auth.updateUser():
```dart
final res = await supabase.auth.api.updateUser(
accessToken,
UserAttributes(password: 'NEW_PASSWORD'),
await supabase.auth.resetPasswordForEmail(
'sample@email.com',
redirectTo: kIsWeb ? null : 'io.supabase.flutter://reset-callback/',
);
```
examples:
- name: Reset password for Flutter
isSpotlight: true
dart: |
You can pass `redirectTo` to open the app via deeplink when user opens the password reset email.
`redirectTo` is used to open the app via deeplink when user opens the password reset email.
```dart
final res = await supabase.auth.api.resetPasswordForEmail(
'user@example.com',
options: AuthOptions(redirectTo: kIsWeb
? null
: 'io.supabase.flutter://reset-callback/'),
await supabase.auth.resetPasswordForEmail(
'sample@email.com',
redirectTo: kIsWeb ? null : 'io.supabase.flutter://reset-callback/',
);
```
- name: Reset password
isSpotlight: true
dart: |
```dart
final res = await supabase.auth.api.resetPasswordForEmail('user@example.com');
```
invoke():
title: 'invoke()'
description: |
@@ -782,7 +763,7 @@ pages:
dart: |
```dart
supabase.from('countries')
.stream(['id'])
.stream(primaryKey: ['id'])
.listen((List<Map<String, dynamic>> data) {
// Do something awesome with the data
});
@@ -794,7 +775,7 @@ pages:
dart: |
```dart
supabase.from('countries')
.stream(['id'])
.stream(primaryKey: ['id'])
.eq('id', '120')
.listen((List<Map<String, dynamic>> data) {
// Do something awesome with the data
@@ -804,7 +785,7 @@ pages:
dart: |
```dart
supabase.from('countries')
.stream(['id'])
.stream(primaryKey: ['id'])
.order('name', ascending: true)
.listen((List<Map<String, dynamic>> data) {
// Do something awesome with the data
@@ -814,7 +795,7 @@ pages:
dart: |
```dart
supabase.from('countries')
.stream(['id'])
.stream(primaryKey: ['id'])
.order('name', ascending: true)
.limit(10)
.listen((List<Map<String, dynamic>> data) {
@@ -837,7 +818,7 @@ pages:
class _MyWidgetState extends State<MyWidget> {
// Persist the stream in a local variable to prevent refetching upon rebuilds
final _stream = supabase.from('countries').stream(['id']);
final _stream = supabase.from('countries').stream(primaryKey: ['id']);
@override
Widget build(BuildContext context) {