diff --git a/packages/console/src/assets/docs/guides/native-flutter/README.mdx b/packages/console/src/assets/docs/guides/native-flutter/README.mdx index 8abd349a3..b96419945 100644 --- a/packages/console/src/assets/docs/guides/native-flutter/README.mdx +++ b/packages/console/src/assets/docs/guides/native-flutter/README.mdx @@ -4,9 +4,19 @@ import Step from '@/mdx-components/Step'; import Tabs from '@mdx/components/Tabs'; import TabItem from '@mdx/components/TabItem'; import InlineNotification from '@/ds-components/InlineNotification'; +import Checkpoint from '../../fragments/_checkpoint.md'; +import RedirectUrisNative, { defaultRedirectUri } from '../../fragments/_redirect-uris-native.mdx'; - + + + + +The Logto Flutter SDK is compatible with Android and iOS platforms only. + +For Dart v2.x users, please use Logto Flutter SDK v1.x. Logto Flutter SDK v2.x requires Dart v3.0.0 or higher. + + @@ -16,124 +26,165 @@ You can install the `logto_dart_sdk package` directly using the pub package mana Run the following command under your project root: ```sh - flutter pub get logto_dart_sdk +flutter pub get logto_dart_sdk ``` - + If you prefer to fork your own version of the SDK, you can clone the repository directly from GitHub. ```sh - git clone https://github.com/logto-io/dart +git clone https://github.com/logto-io/dart ``` - - - +### Dependencies and Android settings
flutter_secure_storage -

- We use [flutter_secure_storage](https://pub.dev/packages/flutter_secure_storage) to implement the cross-platform persistent secure token storage. - Keychain is used for iOS - AES encryption is used for Android. -### Config Android version: +**Config Android version** -In `[project]/android/app/build.gradle` set minSdkVersion to >= 18. +Set the `android:minSdkVersion` to 18 in your project's `android/app/build.gradle` file. -```gradle +```kotlin title="android/app/build.gradle" android { - ... - - defaultConfig { - ... - minSdkVersion 18 - ... - } + {/* ... */} + defaultConfig { + {/* ... */} + minSdkVersion 18 + } } ``` +**Disable autobackup** -### Disable autobackup: +By default Android may backup data on Google Drive automatically. It can cause the exception `java.security.InvalidKeyException: Failed to unwrap key`. - - By default Android backups data on Google Drive. It can cause exception - `java.security.InvalidKeyException:Failed` to unwrap key. -
- You will need to either disable `autobackup` or exclude `sharedprefs` used by the - FlutterSecureStorage plugin. -
+To avoid this, you can disable auto backup for your app or exclude `sharedprefs` from the `FlutterSecureStorage`. -1. To disable `autobackup`, go to your app manifest file and set the boolean value `android:allowBackup`: +1. To disable auto backup, go to your app manifest file and set the `android:allowBackup` and `android:fullBackupContent` attributes to `false`. - ```xml - - ... - - ... - - + ```xml title="AndroidManifest.xml" + + + + + + + ``` - ``` +2. Exclude `sharedprefs` from `FlutterSecureStorage`. -2. To exclude `sharedprefs` for FlutterSecureStorage. + If you need to keep the `android:fullBackupContent` for your app rather than disabling it, you can exclude the `sharedprefs` directory from the backup. - If you need to enable the `android:fullBackupContent` for your app. Set up a backup rule to [exclude](https://developer.android.com/guide/topics/data/autobackup#IncludingFiles) the prefs used by the plugin: + See more details in the [Android documentation](https://developer.android.com/identity/data/autobackup#IncludingFiles). - ```xml - - - ``` + In your `AndroidManifest.xml` file, add the `android:fullBackupContent` attribute to the `` element, as shown in the following example. This attribute points to an XML file that contains backup rules. - ```xml - - - - - ``` + ```xml title="AndroidManifest.xml" + + + + + + + ``` + + Create an XML file called `@xml/backup_rules` in the `res/xml/` directory. In this file, add rules with the `` and `` elements. The following sample backs up all shared preferences except device.xml: + + ```xml title="res/xml/backup_rules.xml" + + + + + ``` Please check [flutter_secure_storage](https://pub.dev/packages/flutter_secure_storage#configure-android-version) for more details. -

-
+
+
flutter_web_auth -

+[flutter_web_auth](https://pub.dev/packages/flutter_web_auth) is used behind Logto's flutter SDK. We rely on its webview-based interaction interface to authenticate users. -[flutter_web_auth](https://pub.dev/packages/flutter_web_auth) is used behind Logto's flutter SDK. We rely on its webview-based interaction interface to open Logto's authorization pages. +This plugin uses `ASWebAuthenticationSession` on iOS 12+ and macOS 10.15+, `SFAuthenticationSession` on iOS 11, `Chrome Custom Tabs` on Android and opens a new window on Web. - +

-Under the hood, this plugin uses `ASWebAuthenticationSession` on iOS 12+ and macOS 10.15+, -`SFAuthenticationSession` on iOS 11, Chrome Custom Tabs on Android and opens a new window on Web. -You can build it with iOS 8+, but it is currently only supported by iOS 11 or higher. +
- + -Andorid: +Import the `logto_dart_sdk` package and initialize the `LogtoClient` instance at the root state of your application. -In order to capture the callback url from Logto's sign-in web page, you will need to register your sign-in redirectUri to the `AndroidManifest.xml`. + + {`import 'package:logto_dart_sdk/logto_dart_sdk.dart'; +import 'package:http/http.dart' as http; -```xml +class MyHomePage extends StatefulWidget { + const MyHomePage({super.key, required this.title}); + final String title; + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + final logtoConfig = LogtoConfig( + endpoint: 'your_logto_endpoint', // Replace with your Logto endpoint + appId: 'your_app_id', // Replace with your App ID + ); + + late LogtoClient logtoClient; + + void _init() async { + logtoClient = LogtoClient( + config: logtoConfig, + httpClient: http.Client(), // Optional: Custom HTTP client + ); + } + + @override + void initState() { + super.initState(); + _init(); + } + + // ... +}`} + + + + + + + + +- For iOS, the redirect URI scheme does not really matter since the iOS will listen to the redirect URI regardless of if it's registered. + +- For Android, in order to capture the callback url from Logto's sign-in web page, you will need to register your sign-in redirectUri to the `AndroidManifest.xml`. + +```xml title="AndroidManifest.xml" @@ -143,72 +194,40 @@ In order to capture the callback url from Logto's sign-in web page, you will nee ``` - -

- - -
- + -Import the `logto_dart_sdk` package and initialize the `LogtoClient` instance at the root of your application. +Let's implement the `signIn` and `signOut` buttons in your application. - - {` -import 'package:logto_dart_sdk/logto_dart_sdk.dart'; -import 'package:http/http.dart' as http; + +{`class _MyHomePageState extends State { + // ... -late LogtoClient logtoClient; + final redirectUri = '${props.redirectUris[0] ?? defaultRedirectUri}'; -// LogtoConfig -final logtoConfig = const LogtoConfig( - endpoint: '${props.endpoint}', // Your Logto endpoint - appId: '${props.app.id}', // Your App ID -); + @override + Widget build(BuildContext context) { + Widget signInButton = TextButton( + onPressed: () async { + await logtoClient.signIn(redirectUri); + }, + child: const Text('Sign In'), + ); -void init() async { - logtoClient = LogtoClient( - config: logtoConfig, - httpClient: http.Client(), // Optional http client - ); -} -`} + Widget signOutButton = TextButton( + onPressed: () async { + await logtoClient.signOut(); + }, + child: const Text('Sign Out'), + ); + + // ... + } +}`} - - - -### Configure Redirect URI - - - In the following steps, we assume your app has configured using `io.logo` as your schema . - - -Let's switch to the Application details page of Logto Admin Console. Add a Redirect URI `io.logto://callback` and click "Save changes". - - - -### Implement a sign-in method - - - {`void signIn() async { - await logtoClient.signIn('${props.redirectUris[0] ?? 'io.logto://callback'}'); -} - `} - - - - - - -### Implement a sign-out method - -```dart -void signOut() async { - await logtoClient.signOut(); -} -``` +
The `signOut` method will clear the user's session and remove the token from the secure storage. @@ -216,85 +235,39 @@ void signOut() async {
- + -In Logto SDK, you can use `logtoClient.isAuthenticated` to check the authentication status, if the -user is signed in, the value will be `true`, otherwise, the value will be `false`. +In Logto SDK, you can use `logtoClient.isAuthenticated` to check the authentication status, if the user is signed in, the value will be `true`, otherwise, the value will be `false`. -```dart - bool isAuthenticated = await logtoClient.isAuthenticated; -``` +Define a boolean variable `isAuthenticated` in the state of your application to keep track of the authentication status. - +```dart title="lib/main.dart" +class _MyHomePageState extends State{ + bool isAuthenticated = false; - + // ... + + void render() async { + if (await logtoClient.isAuthenticated()) { + setState(() { + isAuthenticated = true; + }); -Now let's wrap up the implementation and test your application. + return; + } - - {`import 'package:logto_dart_sdk/logto_dart_sdk.dart'; -import 'package:http/http.dart' as http; - -void main() { - runApp(const MyApp()); -} - -class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return const MaterialApp( - title: 'Flutter Demo', - home: MyHomePage(title: 'Logto Demo Home Page'), - ); - } -} - -class MyHomePage extends StatefulWidget { - const MyHomePage({Key? key, required this.title}) : super(key: key); - final String title; - - @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - late LogtoClient logtoClient; - bool? isAuthenticated = false; - - // Update the authentication state - void render() { - setState(() async { - isAuthenticated = await logtoClient.isAuthenticated; + setState(() { + isAuthenticated = false; }); } - // LogtoConfig - final logtoConfig = const LogtoConfig( - endpoint: '${props.endpoint}', - appId: '${props.app.id}', - ); - - void _init() { - logtoClient = LogtoClient( - config: logtoConfig, - httpClient: http.Client(), // Optional http client - ); - render(); - } - - @override - void initState() { - super.initState(); - _init(); - } - - @override + @overwrite Widget build(BuildContext context) { - Widget signInButton = TextButton( + // ... + + Widget signInButton = TextButton( onPressed: () async { - await logtoClient.signIn('${props.redirectUris[0] ?? 'io.logto://callback'}'); + await logtoClient.signIn(redirectUri); render(); }, child: const Text('Sign In'), @@ -316,7 +289,6 @@ class _MyHomePageState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - SelectableText('My Demo App'), isAuthenticated ? signOutButton : signInButton, ], ), @@ -324,8 +296,13 @@ class _MyHomePageState extends State { ); } } -`} - +``` + + + + + + diff --git a/packages/console/src/assets/docs/guides/native-ios-swift/README.mdx b/packages/console/src/assets/docs/guides/native-ios-swift/README.mdx index 3e1773e1d..5c75931bf 100644 --- a/packages/console/src/assets/docs/guides/native-ios-swift/README.mdx +++ b/packages/console/src/assets/docs/guides/native-ios-swift/README.mdx @@ -5,7 +5,7 @@ import InlineNotification from '@/ds-components/InlineNotification'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; import Checkpoint from '../../fragments/_checkpoint.md'; -import RedirectUrisNative from '../../fragments/_redirect-uris-native.mdx'; +import RedirectUrisNative, { defaultRedirectUri } from '../../fragments/_redirect-uris-native.mdx'; @@ -110,7 +110,7 @@ For example, in a SwiftUI app: Task { [self] in do { try await client.signInWithBrowser(redirectUri: "${ - props.redirectUris[0] ?? 'io.logto://callback' + props.redirectUris[0] ?? defaultRedirectUri }") isAuthenticated = true } catch let error as LogtoClientErrors.SignIn {