mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
fix: Update Google login example app, guide, and blog article to use new Google login flow for Flutter (#18222)
* update Flutter Google login example to use google_sign_in package * update build.gradle to support multidex * compatible with native google login * update comments * update readme of sample app * fix the blog to use the new flow * update google login guide for Flutter to use the new flow * Add an intro line for Flutter native google auth guide
This commit is contained in:
1 parent
3e95f766ed
commit
cb51a8bb9c
7 files changed
+524
-310
No files matched your search
@@ -234,11 +234,11 @@ Before you can use Sign in with Google, you need to obtain a [Google Cloud Platf
|
||||
|
||||
## Using Native Sign in with Google in Flutter
|
||||
|
||||
Unlike the OAuth flow which requires the use of a web browser, the native Sign in with Google flow on Android uses the [operating system's built-in functionalities](https://developers.google.com/android/reference/com/google/android/gms/auth/api/identity/package-summary) to prompt the user for consent. Note that native sign-in has been rebranded as _One Tap sign-in on Android_ by Google, which you should not confuse with _One Tap sign in for web_, as mentioned below.
|
||||
Native Google sign in with Supabase is done through [flutter_appauth](https://pub.dev/packages/flutter_appauth) package for iOS and [google_sign_in](https://pub.dev/packages/google_sign_in) package for Android.
|
||||
|
||||
When the user provides consent, Google issues an identity token (commonly abbreviated as ID token) that is then sent to your project's Supabase Auth server. When valid, a new user session is started by issuing an access and refresh token from Supabase Auth.
|
||||
|
||||
If you are building a Flutter app, you can use the [flutter_appauth](https://pub.dev/packages/flutter_appauth) package to sign a user into your Supabase project:
|
||||
If you are building a Flutter app, you can use the [google_sign_in](https://pub.dev/packages/google_sign_in) package for Android and [flutter_appauth](https://pub.dev/packages/flutter_appauth) package for iOS to sign a user into your Supabase project:
|
||||
|
||||
```dart
|
||||
import 'dart:convert';
|
||||
@@ -246,6 +246,7 @@ Before you can use Sign in with Google, you need to obtain a [Google Cloud Platf
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:flutter_appauth/flutter_appauth.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
import 'package:google_sign_in/google_sign_in.dart';
|
||||
|
||||
/// Function to generate a random 16 character string.
|
||||
String _generateRandomString() {
|
||||
@@ -254,70 +255,95 @@ Before you can use Sign in with Google, you need to obtain a [Google Cloud Platf
|
||||
}
|
||||
|
||||
Future<AuthResponse> signInWithGoogle() {
|
||||
// Just a random string
|
||||
final rawNonce = _generateRandomString();
|
||||
final hashedNonce =
|
||||
sha256.convert(utf8.encode(rawNonce)).toString();
|
||||
|
||||
/// TODO: update the client ID with your own
|
||||
/// TODO: update the iOS and Web client ID with your own.
|
||||
///
|
||||
/// Client ID that you registered with Google Cloud.
|
||||
/// You will have two different values for iOS and Android.
|
||||
const clientId = 'YOUR_CLIENT_ID_HERE';
|
||||
/// Note that in order to perform Google sign in on Android, you need to
|
||||
/// provide the web client ID, not the Android client ID.
|
||||
final clientId = Platform.isIOS ? 'IOS_CLIENT_ID' : 'WEB_CLIENT_ID';
|
||||
|
||||
/// reverse DNS form of the client ID + `:/` is set as the redirect URL
|
||||
final redirectUrl = '${clientId.split('.').reversed.join('.')}:/';
|
||||
late final String? idToken;
|
||||
late final String? accessToken;
|
||||
String? rawNonce;
|
||||
|
||||
/// Fixed value for google login
|
||||
const discoveryUrl =
|
||||
'https://accounts.google.com/.well-known/openid-configuration';
|
||||
// Use AppAuth to perform Google sign in on iOS
|
||||
// and use GoogleSignIn package for Google sign in on Android
|
||||
if (Platform.isIOS) {
|
||||
const appAuth = FlutterAppAuth();
|
||||
|
||||
final appAuth = FlutterAppAuth();
|
||||
// Just a random string
|
||||
rawNonce = _generateRandomString();
|
||||
final hashedNonce = sha256.convert(utf8.encode(rawNonce)).toString();
|
||||
|
||||
// authorize the user by opening the consent page
|
||||
final result = await appAuth.authorize(
|
||||
AuthorizationRequest(
|
||||
clientId,
|
||||
redirectUrl,
|
||||
discoveryUrl: discoveryUrl,
|
||||
nonce: hashedNonce,
|
||||
/// Set as reversed DNS form of Google Client ID + `:/` for Google login
|
||||
final redirectUrl = '${clientId.split('.').reversed.join('.')}:/';
|
||||
|
||||
/// Fixed value for google login
|
||||
const discoveryUrl =
|
||||
'https://accounts.google.com/.well-known/openid-configuration';
|
||||
|
||||
// authorize the user by opening the concent page
|
||||
final result = await appAuth.authorize(
|
||||
AuthorizationRequest(
|
||||
clientId,
|
||||
redirectUrl,
|
||||
discoveryUrl: discoveryUrl,
|
||||
nonce: hashedNonce,
|
||||
scopes: [
|
||||
'openid',
|
||||
'email',
|
||||
'profile',
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (result == null) {
|
||||
throw 'No result';
|
||||
}
|
||||
|
||||
// Request the access and id token to google
|
||||
final tokenResult = await appAuth.token(
|
||||
TokenRequest(
|
||||
clientId,
|
||||
redirectUrl,
|
||||
authorizationCode: result.authorizationCode,
|
||||
discoveryUrl: discoveryUrl,
|
||||
codeVerifier: result.codeVerifier,
|
||||
nonce: result.nonce,
|
||||
scopes: [
|
||||
'openid',
|
||||
'email',
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
accessToken = tokenResult?.accessToken;
|
||||
idToken = tokenResult?.idToken;
|
||||
} else {
|
||||
final GoogleSignIn googleSignIn = GoogleSignIn(
|
||||
serverClientId: clientId,
|
||||
scopes: [
|
||||
'openid',
|
||||
'email',
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (result == null) {
|
||||
throw 'Could not find AuthorizationResponse after authorizing';
|
||||
);
|
||||
final googleUser = await googleSignIn.signIn();
|
||||
final googleAuth = await googleUser!.authentication;
|
||||
accessToken = googleAuth.accessToken;
|
||||
idToken = googleAuth.idToken;
|
||||
}
|
||||
|
||||
// Request the access and id token to google
|
||||
final tokenResult = await appAuth.token(
|
||||
TokenRequest(
|
||||
clientId,
|
||||
redirectUrl,
|
||||
authorizationCode: result.authorizationCode,
|
||||
discoveryUrl: discoveryUrl,
|
||||
codeVerifier: result.codeVerifier,
|
||||
nonce: result.nonce,
|
||||
scopes: [
|
||||
'openid',
|
||||
'email',
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
final idToken = tokenResult?.idToken;
|
||||
|
||||
if (idToken == null) {
|
||||
throw 'Could not find idToken from the token response';
|
||||
throw 'No ID Token';
|
||||
}
|
||||
if (accessToken == null) {
|
||||
throw 'No Access Token';
|
||||
}
|
||||
|
||||
return supabase.auth.signInWithIdToken(
|
||||
provider: Provider.google,
|
||||
idToken: idToken,
|
||||
accessToken: tokenResponse?.accessToken,
|
||||
accessToken: accessToken,
|
||||
nonce: rawNonce,
|
||||
);
|
||||
}
|
||||
@@ -327,7 +353,7 @@ Before you can use Sign in with Google, you need to obtain a [Google Cloud Platf
|
||||
|
||||
1. Configure OAuth credentials for your Google Cloud project in the [Credentials](https://console.cloud.google.com/apis/credentials) page of the console. When creating a new OAuth client ID, choose _Android_ or _iOS_ depending on the mobile operating system your app is built for.
|
||||
|
||||
- For Android, use the instructions on screen to provide the SHA-1 certificate fingerprint used to sign your Android app.
|
||||
- For Android, use the instructions on screen to provide the SHA-1 certificate fingerprint used to sign your Android app. You also need to create a web client ID to perform Google sign in on Android.
|
||||
- For iOS, use the instructions on screen to provide the app Bundle ID, and App Store ID and Team ID if the app is already published on the Apple AppStore.
|
||||
|
||||
2. Configure the [OAuth Consent Screen](https://console.cloud.google.com/apis/credentials/consent). This information is shown to the user when giving consent to your app. In particular, make sure you have set up links to your app's privacy policy and terms of service.
|
||||
|
||||
@@ -52,18 +52,20 @@ flutter create myauthapp
|
||||
then we can install the dependencies. Change the working directory to the newly created app directory and run the following command to install our dependencies.
|
||||
|
||||
```dart
|
||||
flutter pub add supabase_flutter flutter_appauth crypto
|
||||
flutter pub add supabase_flutter flutter_appauth crypto google_sign_in
|
||||
```
|
||||
|
||||
We will use [supabase_flutter](https://pub.dev/packages/supabase_flutter) to interact with our Supabase instance. [flutter_appauth](https://pub.dev/packages/flutter_appauth) will be used to implement Google login, and [crypto](https://pub.dev/packages/crypto) is a library that has utility functions for encryption that we will use when performing OIDC logins.
|
||||
We will use [supabase_flutter](https://pub.dev/packages/supabase_flutter) to interact with our Supabase instance. [google_sign_in](https://pub.dev/packages/google_sign_in) will be used to authenticate on Android, and [flutter_appauth](https://pub.dev/packages/flutter_appauth) will be used for iOS. [crypto](https://pub.dev/packages/crypto) is a library that has utility functions for encryption that we will use when performing OIDC logins.
|
||||
|
||||
We are done installing our dependencies. Let’s set up [authentication](https://supabase.com/docs/guides/auth) now.
|
||||
|
||||
## Configure Google sign-in on Supabase Auth
|
||||
|
||||
We will obtain client IDs for iOS and Android from the Google Cloud console, and register them to our Supabase project.
|
||||
We will obtain client IDs for iOS, Android, and web from the Google Cloud console, and register them to our Supabase project.
|
||||
We need the web client ID in order to use the Google sign-in flow on Android.
|
||||
|
||||
First, create your Google Cloud project [here](https://cloud.google.com/) if you do not have one yet. Within your Google Cloud project, follow the [Configure a Google API Console project for Android](https://developers.google.com/identity/sign-in/android/start-integrating#configure_a_project) guide and [Get an OAuth client ID for the iOS](https://developers.google.com/identity/sign-in/ios/start-integrating#get_an_oauth_client_id) guide to obtain client IDs for Android and iOS respectively.
|
||||
First, create your Google Cloud project [here](https://cloud.google.com/) if you do not have one yet.
|
||||
Within your Google Cloud project, follow the [Get an OAuth client ID for the iOS](https://developers.google.com/identity/sign-in/ios/start-integrating#get_an_oauth_client_id) guide, [Configure a Google API Console project for Android](https://developers.google.com/identity/sign-in/android/start-integrating#configure_a_project) guide, and [Get your backend server's OAuth 2.0 client ID](https://developers.google.com/identity/sign-in/android/start-integrating#configure_a_project) to obtain client IDs for iOS, Android, and web respectively.
|
||||
|
||||
Once you have the client IDs, let’s add them to our Supabase dashboard. If you don’t have a Supabase project created yet, you can create one at [database.new](https://database.new) for free. The name is just an internal name, so we can call it “Auth” for now. Database Password will not be used in this example and can be reconfigured later, so press the `Generate a password` button and let Supabase generate a secure random password. No need to copy it anywhere. The region should be anywhere close to where you live, or where your users live in an actual production app.
|
||||
|
||||
@@ -71,11 +73,11 @@ Lastly, for the pricing plan choose the free plan that allows you to connect wit
|
||||
|
||||

|
||||
|
||||
Your project should be ready in a minute or two. Once your project is ready, you can open `authentication -> Providers -> Google` to set up Google auth. Toggle the `Enable Sign in with Google` switch first. Then add the two client IDs you obtained in your Google Cloud console to `Authorized Client IDs` field with a comma in between the two client IDs like this: `ANDROID_CLIENT_ID,IOS_CLIENT_ID`.
|
||||
Your project should be ready in a minute or two. Once your project is ready, you can open `authentication -> Providers -> Google` to set up Google auth. Toggle the `Enable Sign in with Google` switch first. Then add the two client IDs you obtained in your Google Cloud console to `Authorized Client IDs` field with a comma in between the two client IDs like this: `IOS_CLIENT_ID,ANDROID_CLIENT_ID,WEB_CLIENT_ID`.
|
||||
|
||||

|
||||
|
||||
We also need some Android specific settings to make [flutter_appauth](https://pub.dev/packages/flutter_appauth#android-setup) work. Open `android/app/build.gradle` and find the `defaultConfig`. We need to set the reversed DNS form of the Android Client ID as the`appAuthRedirectScheme` manifest placeholder value.
|
||||
We also need some Android specific settings to make [flutter_appauth](https://pub.dev/packages/flutter_appauth#android-setup) not throw during app building. Open `android/app/build.gradle` and find the `defaultConfig`. We need to to provide an empty string here.
|
||||
|
||||
```groovy
|
||||
...
|
||||
@@ -83,15 +85,15 @@ android {
|
||||
...
|
||||
defaultConfig {
|
||||
...
|
||||
// We need an empty manifestPlaceholders for appauth to not throw an error
|
||||
manifestPlaceholders += [
|
||||
// *account_id* will be unique for every single app
|
||||
'appAuthRedirectScheme': 'com.googleusercontent.apps.*account_id*'
|
||||
'appAuthRedirectScheme': ''
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
That is it for setting up our [Supabase auth to prepare for Google sign-in](https://supabase.com/docs/guides/auth/social-login/auth-google#using-native-sign-in).
|
||||
That is it for setting up our [Supabase auth to prepare for Google sign-in](https://supabase.com/docs/guides/auth/social-login/auth-google?platform=flutter).
|
||||
|
||||
Finally, we can initialize Supabase in our Flutter application with the credentials of our Supabase instance. Update your `main.dart` file and add `Supabase.initialize()` in the `main` function like the following. Note that you will see some errors since the home screen is set to the `LoginScreen`, which we will create later.
|
||||
|
||||
@@ -145,6 +147,7 @@ import 'dart:math';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_appauth/flutter_appauth.dart';
|
||||
import 'package:google_sign_in/google_sign_in.dart';
|
||||
import 'package:myauthapp/main.dart';
|
||||
import 'package:myauthapp/screens/profile_screen.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
@@ -182,6 +185,100 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||
return base64Url.encode(List<int>.generate(16, (_) => random.nextInt(256)));
|
||||
}
|
||||
|
||||
Future<AuthResponse> _googleSignIn() async {
|
||||
/// TODO: update the iOS and Web client ID with your own.
|
||||
///
|
||||
/// Client ID that you registered with Google Cloud.
|
||||
/// Note that in order to perform Google sign in on Android, you need to
|
||||
/// provide the web client ID, not the Android client ID.
|
||||
final clientId = Platform.isIOS ? 'IOS_CLIENT_ID' : 'WEB_CLIENT_ID';
|
||||
|
||||
late final String? idToken;
|
||||
late final String? accessToken;
|
||||
String? rawNonce;
|
||||
|
||||
// Use AppAuth to perform Google sign in on iOS
|
||||
// and use GoogleSignIn package for Google sign in on Android
|
||||
if (Platform.isIOS) {
|
||||
const appAuth = FlutterAppAuth();
|
||||
|
||||
// Just a random string
|
||||
rawNonce = _generateRandomString();
|
||||
final hashedNonce = sha256.convert(utf8.encode(rawNonce)).toString();
|
||||
|
||||
/// Set as reversed DNS form of Google Client ID + `:/` for Google login
|
||||
final redirectUrl = '${clientId.split('.').reversed.join('.')}:/';
|
||||
|
||||
/// Fixed value for google login
|
||||
const discoveryUrl =
|
||||
'https://accounts.google.com/.well-known/openid-configuration';
|
||||
|
||||
// authorize the user by opening the concent page
|
||||
final result = await appAuth.authorize(
|
||||
AuthorizationRequest(
|
||||
clientId,
|
||||
redirectUrl,
|
||||
discoveryUrl: discoveryUrl,
|
||||
nonce: hashedNonce,
|
||||
scopes: [
|
||||
'openid',
|
||||
'email',
|
||||
'profile',
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (result == null) {
|
||||
throw 'No result';
|
||||
}
|
||||
|
||||
// Request the access and id token to google
|
||||
final tokenResult = await appAuth.token(
|
||||
TokenRequest(
|
||||
clientId,
|
||||
redirectUrl,
|
||||
authorizationCode: result.authorizationCode,
|
||||
discoveryUrl: discoveryUrl,
|
||||
codeVerifier: result.codeVerifier,
|
||||
nonce: result.nonce,
|
||||
scopes: [
|
||||
'openid',
|
||||
'email',
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
accessToken = tokenResult?.accessToken;
|
||||
idToken = tokenResult?.idToken;
|
||||
} else {
|
||||
final GoogleSignIn googleSignIn = GoogleSignIn(
|
||||
serverClientId: clientId,
|
||||
scopes: [
|
||||
'openid',
|
||||
'email',
|
||||
],
|
||||
);
|
||||
final googleUser = await googleSignIn.signIn();
|
||||
final googleAuth = await googleUser!.authentication;
|
||||
accessToken = googleAuth.accessToken;
|
||||
idToken = googleAuth.idToken;
|
||||
}
|
||||
|
||||
if (idToken == null) {
|
||||
throw 'No ID Token';
|
||||
}
|
||||
if (accessToken == null) {
|
||||
throw 'No Access Token';
|
||||
}
|
||||
|
||||
return supabase.auth.signInWithIdToken(
|
||||
provider: Provider.google,
|
||||
idToken: idToken,
|
||||
accessToken: accessToken,
|
||||
nonce: rawNonce,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@@ -190,75 +287,7 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
onPressed: () async {
|
||||
const appAuth = FlutterAppAuth();
|
||||
|
||||
// Just a random string
|
||||
final rawNonce = _generateRandomString();
|
||||
final hashedNonce =
|
||||
sha256.convert(utf8.encode(rawNonce)).toString();
|
||||
|
||||
/// TODO: update the iOS and Android client ID with your own.
|
||||
///
|
||||
/// Client ID that you registered with Google Cloud.
|
||||
/// You will have two different values for iOS and Android.
|
||||
final clientId =
|
||||
Platform.isIOS ? 'IOS_CLIENT_ID' : 'ANDROID_CLIENT_ID';
|
||||
|
||||
/// Set as reversed DNS form of Google Client ID + `:/` for Google login
|
||||
final redirectUrl = '${clientId.split('.').reversed.join('.')}:/';
|
||||
|
||||
/// Fixed value for google login
|
||||
const discoveryUrl =
|
||||
'https://accounts.google.com/.well-known/openid-configuration';
|
||||
|
||||
// authorize the user by opening the concent page
|
||||
final result = await appAuth.authorize(
|
||||
AuthorizationRequest(
|
||||
clientId,
|
||||
redirectUrl,
|
||||
discoveryUrl: discoveryUrl,
|
||||
nonce: hashedNonce,
|
||||
scopes: [
|
||||
'openid',
|
||||
'email',
|
||||
'profile',
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (result == null) {
|
||||
throw 'No result';
|
||||
}
|
||||
|
||||
// Request the access and id token to google
|
||||
final tokenResult = await appAuth.token(
|
||||
TokenRequest(
|
||||
clientId,
|
||||
redirectUrl,
|
||||
authorizationCode: result.authorizationCode,
|
||||
discoveryUrl: discoveryUrl,
|
||||
codeVerifier: result.codeVerifier,
|
||||
nonce: result.nonce,
|
||||
scopes: [
|
||||
'openid',
|
||||
'email',
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
final idToken = tokenResult?.idToken;
|
||||
|
||||
if (idToken == null) {
|
||||
throw 'No idToken';
|
||||
}
|
||||
|
||||
await supabase.auth.signInWithIdToken(
|
||||
provider: Provider.google,
|
||||
idToken: idToken,
|
||||
nonce: rawNonce,
|
||||
);
|
||||
},
|
||||
onPressed: _googleSignIn,
|
||||
child: const Text('Google login'),
|
||||
),
|
||||
),
|
||||
@@ -272,6 +301,8 @@ In terms of UI, this page is very simple, it just has a basic `Scaffold` with an
|
||||

|
||||
|
||||
Let’s break down what is going on within the `onPressed` callback of the sign in button.
|
||||
At the top, we check on which platform the user is running the application.
|
||||
We will break down the iOS flow, because it helps us understand how the Open ID Connect flow works.
|
||||
|
||||
First, we are generating a [nonce](https://openid.net/specs/openid-connect-core-1_0.html#SelfIssuedDiscovery:~:text=auth_time%20response%20parameter.)-,nonce,-String%20value%20used), which is essentially just a random string. This string is later passed to Google after being hashed to verify that the ID token has not been tampered to prevent a man-in-the-middle attack.
|
||||
|
||||
@@ -349,6 +380,8 @@ await supabase.auth.signInWithIdToken(
|
||||
);
|
||||
```
|
||||
|
||||
The android flow goes through similar steps, but all of the heavy lifting of obtaining the access token and ID token is done by the `google_sign_in` package.
|
||||
|
||||
## Create the Profile Screen
|
||||
|
||||
The `ProfileScreen` will be just a simple UI presenting some of the information we obtained in the `LoginPage`. We can access the user data with `supabase.auth.currentUser`, where Supabase has saved the personal information in a property called `userMetadata`. In this example, we are displaying the `avatar_url` and `full_name` to display a basic profile page. Create a `lib/screens/profile_screen.dart` file and add the following.
|
||||
|
||||
@@ -11,10 +11,9 @@ A simple Flutter application with native Google login capabilities on iOS and An
|
||||
|
||||
- Create a new Supabase project [here](https://database.new)
|
||||
- Add your Supabase credentials to `lib/main.dart`
|
||||
- Obtain Google API client ID for [Android](https://developers.google.com/identity/sign-in/android/start-integrating#configure_a_project) and [iOS](https://developers.google.com/identity/sign-in/ios/start-integrating#get_an_oauth_client_id)
|
||||
- Add the client IDs in Supabase dashboard under `Auth -> Providers -> Google -> Authorized Client IDs` and turn on `Enable Sign in with Google`
|
||||
- Find the `clientId` variable in `lib/screens/login_screen.dart` and paste the two client IDs
|
||||
- For android open `android/app/build.gradle` file, locate `appAuthRedirectScheme` variable and replace the value with your reversed DNS form of the Android client ID. For example, if your client ID is `1234567890-abc123def456.apps.googleusercontent.com`, then the value should be `com.googleusercontent.apps.1234567890-abc123def456`
|
||||
- Obtain Google API client ID for [iOS](https://developers.google.com/identity/sign-in/ios/start-integrating#get_an_oauth_client_id), [Android](https://developers.google.com/identity/sign-in/android/start-integrating#configure_a_project), and [Web](https://developers.google.com/identity/sign-in/android/start-integrating#get_your_backend_servers_oauth_20_client_id)
|
||||
- Add all of the client IDs in Supabase dashboard under `Auth -> Providers -> Google -> Authorized Client IDs` as comma separated values and turn on `Enable Sign in with Google`
|
||||
- Find the `clientId` variable in `lib/screens/login_screen.dart` and paste the iOS and web client IDs.
|
||||
- Run the app on iOS or Android and test the login flow 🚀
|
||||
|
||||
## Resources
|
||||
|
||||
@@ -48,15 +48,15 @@ android {
|
||||
applicationId "com.example.myauthapp"
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
||||
minSdkVersion 20
|
||||
minSdkVersion 21
|
||||
targetSdkVersion flutter.targetSdkVersion
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
// You need an empty manifestPlaceholders for appauth to not throw an error
|
||||
manifestPlaceholders += [
|
||||
// *account_id* will be unique for every single app
|
||||
'appAuthRedirectScheme': 'com.googleusercontent.apps.*account_id*'
|
||||
'appAuthRedirectScheme': ''
|
||||
]
|
||||
|
||||
multiDexEnabled true
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
@@ -74,4 +74,5 @@ flutter {
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'androidx.multidex:multidex:2.0.1'
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import 'dart:math';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_appauth/flutter_appauth.dart';
|
||||
import 'package:google_sign_in/google_sign_in.dart';
|
||||
import 'package:myauthapp/main.dart';
|
||||
import 'package:myauthapp/screens/profile_screen.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
@@ -42,6 +43,100 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||
return base64Url.encode(List<int>.generate(16, (_) => random.nextInt(256)));
|
||||
}
|
||||
|
||||
Future<AuthResponse> _googleSignIn() async {
|
||||
/// TODO: update the iOS and Web client ID with your own.
|
||||
///
|
||||
/// Client ID that you registered with Google Cloud.
|
||||
/// Note that in order to perform Google sign in on Android, you need to
|
||||
/// provide the web client ID, not the Android client ID.
|
||||
final clientId = Platform.isIOS ? 'IOS_CLIENT_ID' : 'WEB_CLIENT_ID';
|
||||
|
||||
late final String? idToken;
|
||||
late final String? accessToken;
|
||||
String? rawNonce;
|
||||
|
||||
// Use AppAuth to perform Google sign in on iOS
|
||||
// and use GoogleSignIn package for Google sign in on Android
|
||||
if (Platform.isIOS) {
|
||||
const appAuth = FlutterAppAuth();
|
||||
|
||||
// Just a random string
|
||||
rawNonce = _generateRandomString();
|
||||
final hashedNonce = sha256.convert(utf8.encode(rawNonce)).toString();
|
||||
|
||||
/// Set as reversed DNS form of Google Client ID + `:/` for Google login
|
||||
final redirectUrl = '${clientId.split('.').reversed.join('.')}:/';
|
||||
|
||||
/// Fixed value for google login
|
||||
const discoveryUrl =
|
||||
'https://accounts.google.com/.well-known/openid-configuration';
|
||||
|
||||
// authorize the user by opening the concent page
|
||||
final result = await appAuth.authorize(
|
||||
AuthorizationRequest(
|
||||
clientId,
|
||||
redirectUrl,
|
||||
discoveryUrl: discoveryUrl,
|
||||
nonce: hashedNonce,
|
||||
scopes: [
|
||||
'openid',
|
||||
'email',
|
||||
'profile',
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (result == null) {
|
||||
throw 'No result';
|
||||
}
|
||||
|
||||
// Request the access and id token to google
|
||||
final tokenResult = await appAuth.token(
|
||||
TokenRequest(
|
||||
clientId,
|
||||
redirectUrl,
|
||||
authorizationCode: result.authorizationCode,
|
||||
discoveryUrl: discoveryUrl,
|
||||
codeVerifier: result.codeVerifier,
|
||||
nonce: result.nonce,
|
||||
scopes: [
|
||||
'openid',
|
||||
'email',
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
accessToken = tokenResult?.accessToken;
|
||||
idToken = tokenResult?.idToken;
|
||||
} else {
|
||||
final GoogleSignIn googleSignIn = GoogleSignIn(
|
||||
serverClientId: clientId,
|
||||
scopes: [
|
||||
'openid',
|
||||
'email',
|
||||
],
|
||||
);
|
||||
final googleUser = await googleSignIn.signIn();
|
||||
final googleAuth = await googleUser!.authentication;
|
||||
accessToken = googleAuth.accessToken;
|
||||
idToken = googleAuth.idToken;
|
||||
}
|
||||
|
||||
if (idToken == null) {
|
||||
throw 'No ID Token';
|
||||
}
|
||||
if (accessToken == null) {
|
||||
throw 'No Access Token';
|
||||
}
|
||||
|
||||
return supabase.auth.signInWithIdToken(
|
||||
provider: Provider.google,
|
||||
idToken: idToken,
|
||||
accessToken: accessToken,
|
||||
nonce: rawNonce,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@@ -50,75 +145,7 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
onPressed: () async {
|
||||
const appAuth = FlutterAppAuth();
|
||||
|
||||
// Just a random string
|
||||
final rawNonce = _generateRandomString();
|
||||
final hashedNonce =
|
||||
sha256.convert(utf8.encode(rawNonce)).toString();
|
||||
|
||||
/// TODO: update the iOS and Android client ID with your own.
|
||||
///
|
||||
/// Client ID that you registered with Google Cloud.
|
||||
/// You will have two different values for iOS and Android.
|
||||
final clientId =
|
||||
Platform.isIOS ? 'IOS_CLIENT_ID' : 'ANDROID_CLIENT_ID';
|
||||
|
||||
/// Set as reversed DNS form of Google Client ID + `:/` for Google login
|
||||
final redirectUrl = '${clientId.split('.').reversed.join('.')}:/';
|
||||
|
||||
/// Fixed value for google login
|
||||
const discoveryUrl =
|
||||
'https://accounts.google.com/.well-known/openid-configuration';
|
||||
|
||||
// authorize the user by opening the concent page
|
||||
final result = await appAuth.authorize(
|
||||
AuthorizationRequest(
|
||||
clientId,
|
||||
redirectUrl,
|
||||
discoveryUrl: discoveryUrl,
|
||||
nonce: hashedNonce,
|
||||
scopes: [
|
||||
'openid',
|
||||
'email',
|
||||
'profile',
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (result == null) {
|
||||
throw 'No result';
|
||||
}
|
||||
|
||||
// Request the access and id token to google
|
||||
final tokenResult = await appAuth.token(
|
||||
TokenRequest(
|
||||
clientId,
|
||||
redirectUrl,
|
||||
authorizationCode: result.authorizationCode,
|
||||
discoveryUrl: discoveryUrl,
|
||||
codeVerifier: result.codeVerifier,
|
||||
nonce: result.nonce,
|
||||
scopes: [
|
||||
'openid',
|
||||
'email',
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
final idToken = tokenResult?.idToken;
|
||||
|
||||
if (idToken == null) {
|
||||
throw 'No idToken';
|
||||
}
|
||||
|
||||
await supabase.auth.signInWithIdToken(
|
||||
provider: Provider.google,
|
||||
idToken: idToken,
|
||||
nonce: rawNonce,
|
||||
);
|
||||
},
|
||||
onPressed: _googleSignIn,
|
||||
child: const Text('Google login'),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,14 +1,38 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
_fe_analyzer_shared:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "61.0.0"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.13.0"
|
||||
app_links:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: app_links
|
||||
sha256: "16725e716afd0634a5441654b1dda2b6c5557aa230884b5e1f41a5aa546a4cb6"
|
||||
sha256: eb83c2b15b78a66db04e95132678e910fcdb8dc3a9b0aed0c138f50b2bef0dae
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.3"
|
||||
version: "3.4.5"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.2"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -45,10 +69,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
|
||||
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.1"
|
||||
version: "1.17.2"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: convert
|
||||
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.1"
|
||||
crypto:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -57,14 +89,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: cupertino_icons
|
||||
sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -77,10 +101,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99
|
||||
sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
version: "2.1.0"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -98,10 +122,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_appauth
|
||||
sha256: a047019b525ab0e260776d1b0da1000b7416b45b9e8dd02d124c9da571fc1918
|
||||
sha256: "19031ea2438a513762e64ec7b58ae1f00cad6fb87f818e405325cd183dfb9fec"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.0"
|
||||
version: "6.0.2"
|
||||
flutter_appauth_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -114,10 +138,10 @@ packages:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_lints
|
||||
sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c
|
||||
sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
version: "2.0.3"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
@@ -136,14 +160,70 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.2"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: glob
|
||||
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
google_identity_services_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: google_identity_services_web
|
||||
sha256: "554748f2478619076128152c58905620d10f9c7fc270ff1d3a9675f9f53838ed"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.1+1"
|
||||
google_sign_in:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: google_sign_in
|
||||
sha256: f45038d27bcad37498f282295ae97eece23c9349fc16649154067b87b9f1fd03
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.5"
|
||||
google_sign_in_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: google_sign_in_android
|
||||
sha256: "6031f59074a337fdd81be821aba84cee3a41338c6e958499a5cd34d3e1db80ef"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.20"
|
||||
google_sign_in_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: google_sign_in_ios
|
||||
sha256: "974944859f9cd40eb8a15b3fe8efb2d47fb7e99438f763f61a1ccd28d74ff4ce"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.6.4"
|
||||
google_sign_in_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: google_sign_in_platform_interface
|
||||
sha256: "35ceee5f0eadc1c07b0b4af7553246e315c901facbb7d3dadf734ba2693ceec4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.2"
|
||||
google_sign_in_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: google_sign_in_web
|
||||
sha256: "939e9172a378ec4eaeb7f71eeddac9b55ebd0e8546d336daec476a68e5279766"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.0+5"
|
||||
gotrue:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: gotrue
|
||||
sha256: "3639beedcbf3d63d9b2a6ca856138aff03d9e09bddd4c5477de77c58f36c83ae"
|
||||
sha256: "3d15ee24bd05c6b4e5587f6dba727a7c031e5513424aa2ab4fb35b42f8c45ba7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.0"
|
||||
version: "1.12.2"
|
||||
hive:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -204,18 +284,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
|
||||
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.15"
|
||||
version: "0.12.16"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
|
||||
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
version: "0.5.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -232,6 +312,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_config
|
||||
sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -244,90 +332,106 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider
|
||||
sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2"
|
||||
sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.15"
|
||||
version: "2.1.1"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86"
|
||||
sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.27"
|
||||
version: "2.2.0"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_foundation
|
||||
sha256: "1995d88ec2948dac43edf8fe58eb434d35d22a2940ecee1a9fefcd62beee6eb3"
|
||||
sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.3"
|
||||
version: "2.3.1"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57
|
||||
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.11"
|
||||
version: "2.2.1"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec"
|
||||
sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.6"
|
||||
version: "2.1.1"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
sha256: "1cb68ba4cd3a795033de62ba1b7b4564dace301f952de6bfb3cd91b202b6ee96"
|
||||
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.7"
|
||||
version: "2.2.1"
|
||||
pigeon:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pigeon
|
||||
sha256: "5a79fd0b10423f6b5705525e32015597f861c31220b522a67d1e6b580da96719"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.0.1"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
|
||||
sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
version: "3.1.2"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc"
|
||||
sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
version: "2.1.6"
|
||||
postgrest:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: postgrest
|
||||
sha256: "78fd180ecd2274df7b04c406746495b5c627248856458f8f537bf5348de9c817"
|
||||
sha256: "87e35d3a59e327188321befbfbfcc5a7a2e71f0d0a13d975cbc7d169387ec712"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.2"
|
||||
process:
|
||||
version: "1.5.1"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: process
|
||||
sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
|
||||
name: pub_semver
|
||||
sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.4"
|
||||
version: "2.1.4"
|
||||
quiver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: quiver
|
||||
sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.1"
|
||||
realtime_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: realtime_client
|
||||
sha256: "0342f73f42345f3547e3cdcc804a0ed108fcd9142d1537d159aead94a213e248"
|
||||
sha256: bf25420988263ba3811f65756685bf4813a527277a784150b8b7de0a583e122f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
version: "1.2.3"
|
||||
retry:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -348,66 +452,66 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: "396f85b8afc6865182610c0a2fc470853d56499f75f7499e2a73a9f0539d23d0"
|
||||
sha256: b7f41bad7e521d205998772545de63ff4e6c97714775902c199353f8bf1511ac
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
version: "2.2.1"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: "6478c6bbbecfe9aced34c483171e90d7c078f5883558b30ec3163cf18402c749"
|
||||
sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
version: "2.2.1"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: e014107bb79d6d3297196f4f2d0db54b5d1f85b8ea8ff63b8e8b391a02700feb
|
||||
sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
version: "2.3.4"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
sha256: "9d387433ca65717bbf1be88f4d5bb18f10508917a8fa2fb02e0fd0d7479a9afa"
|
||||
sha256: c2eb5bf57a2fe9ad6988121609e47d3e07bb3bdca5b6f8444e4cf302428a128a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
version: "2.3.1"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
sha256: "23b052f17a25b90ff2b61aad4cc962154da76fb62848a9ce088efe30d7c50ab1"
|
||||
sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
version: "2.3.1"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
sha256: "74083203a8eae241e0de4a0d597dbedab3b8fef5563f33cf3c12d7e93c655ca5"
|
||||
sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
version: "2.2.1"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
sha256: "5e588e2efef56916a3b229c3bfe81e6a525665a454519ca51dbcc4236a274173"
|
||||
sha256: f763a101313bd3be87edffe0560037500967de9c394a714cd598d945517f694f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
version: "2.3.1"
|
||||
sign_in_with_apple:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sign_in_with_apple
|
||||
sha256: ac3b113767dfdd765078c507dad9d4d9fe96b669cc7bd88fc36fc15376fb3400
|
||||
sha256: "0975c23b9f8b30a80e27d5659a75993a093d4cb5f4eb7d23a9ccc586fea634e0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.3.0"
|
||||
version: "5.0.0"
|
||||
sign_in_with_apple_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -433,10 +537,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
|
||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
version: "1.10.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -449,10 +553,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: storage_client
|
||||
sha256: a3024569213b064587d616827747b766f9bc796e80cec99bd5ffb597b8aeb018
|
||||
sha256: "4bf2fc76f09c3698f0ba3f1a44d567995796f6aef76501f194631d0c03752ab7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.1"
|
||||
version: "1.5.2"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -473,18 +577,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: supabase
|
||||
sha256: d854acbcfcfe0b3b703854aedd05858d684b2e46f8dca70b262665d7f70ad3d8
|
||||
sha256: ee6ab8aa31438de59027888f87e53df97e138cdd00c0125d4f6f5244c31fc21e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.5"
|
||||
version: "1.11.6"
|
||||
supabase_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: supabase_flutter
|
||||
sha256: "2a32140420a3bd1a9e8c093c08d12bbd93ee9fbb8fc373397ef5a3ad9d93f55c"
|
||||
sha256: "27ae595efb3ef6612be1270bd368237916e41b2ba20ea99fedd2df220b424888"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.6"
|
||||
version: "1.10.19"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -497,10 +601,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
|
||||
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.1"
|
||||
version: "0.6.0"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -521,66 +625,66 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher
|
||||
sha256: eb1e00ab44303d50dd487aab67ebc575456c146c6af44422f9c13889984c00f3
|
||||
sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.11"
|
||||
version: "6.1.14"
|
||||
url_launcher_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_android
|
||||
sha256: "15f5acbf0dce90146a0f5a2c4a002b1814a6303c4c5c075aa2623b2d16156f03"
|
||||
sha256: b04af59516ab45762b2ca6da40fa830d72d0f6045cd97744450b73493fa76330
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.36"
|
||||
version: "6.1.0"
|
||||
url_launcher_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_ios
|
||||
sha256: "9af7ea73259886b92199f9e42c116072f05ff9bea2dcb339ab935dfc957392c2"
|
||||
sha256: "7c65021d5dee51813d652357bc65b8dd4a6177082a9966bc8ba6ee477baa795f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.4"
|
||||
version: "6.1.5"
|
||||
url_launcher_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_linux
|
||||
sha256: "207f4ddda99b95b4d4868320a352d374b0b7e05eefad95a4a26f57da413443f5"
|
||||
sha256: b651aad005e0cb06a01dbd84b428a301916dc75f0e7ea6165f80057fee2d8e8e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.5"
|
||||
version: "3.0.6"
|
||||
url_launcher_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_macos
|
||||
sha256: "91ee3e75ea9dadf38036200c5d3743518f4a5eb77a8d13fda1ee5764373f185e"
|
||||
sha256: b55486791f666e62e0e8ff825e58a023fd6b1f71c49926483f1128d3bbd8fe88
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.5"
|
||||
version: "3.0.7"
|
||||
url_launcher_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_platform_interface
|
||||
sha256: bfdfa402f1f3298637d71ca8ecfe840b4696698213d5346e9d12d4ab647ee2ea
|
||||
sha256: "95465b39f83bfe95fcb9d174829d6476216f2d548b79c38ab2506e0458787618"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
version: "2.1.5"
|
||||
url_launcher_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_web
|
||||
sha256: "6bb1e5d7fe53daf02a8fee85352432a40b1f868a81880e99ec7440113d5cfcab"
|
||||
sha256: "2942294a500b4fa0b918685aff406773ba0a4cd34b7f42198742a94083020ce5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.17"
|
||||
version: "2.0.20"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_windows
|
||||
sha256: "254708f17f7c20a9c8c471f67d86d76d4a3f9c1591aad1e15292008aceb82771"
|
||||
sha256: "95fef3129dc7cfaba2bc3d5ba2e16063bb561fc6d78e63eee16162bc70029069"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.6"
|
||||
version: "3.0.8"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -589,6 +693,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.4-beta"
|
||||
web_socket_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -601,50 +721,58 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter
|
||||
sha256: "789d52bd789373cc1e100fb634af2127e86c99cf9abde09499743270c5de8d00"
|
||||
sha256: c1ab9b81090705c6069197d9fdc1625e587b52b8d70cdde2339d177ad0dbb98e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.2"
|
||||
version: "4.4.1"
|
||||
webview_flutter_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_android
|
||||
sha256: "532135f6f6b8030cd039f30eab23f340d650350e29f38e9b37d2eaad028f1018"
|
||||
sha256: b0cd33dd7d3dd8e5f664e11a19e17ba12c352647269921a3b568406b001f1dff
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.8.0"
|
||||
version: "3.12.0"
|
||||
webview_flutter_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_platform_interface
|
||||
sha256: "656e2aeaef318900fffd21468b6ddc7958c7092a642f0e7220bac328b70d4a81"
|
||||
sha256: "6d9213c65f1060116757a7c473247c60f3f7f332cac33dc417c9e362a9a13e4f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
version: "2.6.0"
|
||||
webview_flutter_wkwebview:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_wkwebview
|
||||
sha256: "7b203961d6830f3e5ed1df4c4f0493fea8388a46c4d43716167c4a8ba8ecbe83"
|
||||
sha256: "30b9af6bdd457b44c08748b9190d23208b5165357cc2eb57914fee1366c42974"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.6.0"
|
||||
version: "3.9.1"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: dfdf0136e0aa7a1b474ea133e67cb0154a0acd2599c4f3ada3b49d38d38793ee
|
||||
sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.5"
|
||||
version: "5.0.9"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1
|
||||
sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
version: "1.0.3"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml
|
||||
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
yet_another_json_isolate:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -654,5 +782,5 @@ packages:
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
sdks:
|
||||
dart: ">=3.0.5 <4.0.0"
|
||||
flutter: ">=3.3.0"
|
||||
dart: ">=3.1.0 <4.0.0"
|
||||
flutter: ">=3.13.0"
|
||||
@@ -12,10 +12,10 @@ dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
cupertino_icons: ^1.0.2
|
||||
supabase_flutter: ^1.10.6
|
||||
supabase_flutter: ^1.10.19
|
||||
flutter_appauth: ^6.0.0
|
||||
crypto: ^3.0.3
|
||||
google_sign_in: ^6.1.5
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Reference in new issue
Block a user