0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-30 20:33:54 -05:00

docs(console): update flutter intergration guide (#6125)

improve content

---------

Co-authored-by: Gao Sun <gao@silverhand.io>
This commit is contained in:
simeng-li 2024-07-01 16:43:37 +08:00 committed by GitHub
parent 685a97476a
commit ac063f9140
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 172 additions and 195 deletions

View file

@ -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';
<Steps>
<Step title="Install Logto SDK">
<Step title="Install SDK">
<InlineNotification severity="alert">
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.
</InlineNotification>
<Tabs>
@ -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
```
</TabItem>
<TabItem value="github" label="github">
<TabItem value="github" label="GitHub">
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
```
</TabItem>
</Tabs>
</Step>
<Step title="Dependency settings" subtitle="2 steps">
### Dependencies and Android settings
<details>
<summary>flutter_secure_storage</summary>
<p>
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`.
<InlineNotification>
By default Android backups data on Google Drive. It can cause exception
`java.security.InvalidKeyException:Failed` to unwrap key.
<br />
You will need to either disable `autobackup` or exclude `sharedprefs` used by the
FlutterSecureStorage plugin.
</InlineNotification>
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
<manifest ... >
...
<application
android:allowBackup="false"
android:fullBackupContent="false"
...
>
...
</application>
</manifest>
```xml title="AndroidManifest.xml"
<manifest>
<!-- ...other attributes -->
<application
android:allowBackup="false"
android:fullBackupContent="false"
>
<!-- ... -->
</application>
</manifest>
```
```
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
<application ...
android:fullBackupContent="@xml/backup_rules">
</application>
```
In your `AndroidManifest.xml` file, add the `android:fullBackupContent` attribute to the `<application>` element, as shown in the following example. This attribute points to an XML file that contains backup rules.
```xml
<?xml version="1.0" encoding="utf-8"?>
<full-backup-content>
<exclude domain="sharedpref" path="FlutterSecureStorage"/>
</full-backup-content>
```
```xml title="AndroidManifest.xml"
<manifest>
<!-- ...other attributes -->
<application
android:fullBackupContent="@xml/backup_rules"
>
<!-- ... -->
</application>
</manifest>
```
Create an XML file called `@xml/backup_rules` in the `res/xml/` directory. In this file, add rules with the `<include>` and `<exclude>` elements. The following sample backs up all shared preferences except device.xml:
```xml title="res/xml/backup_rules.xml"
<?xml version="1.0" encoding="utf-8"?>
<full-backup-content>
<exclude domain="sharedpref" path="FlutterSecureStorage"/>
</full-backup-content>
```
Please check [flutter_secure_storage](https://pub.dev/packages/flutter_secure_storage#configure-android-version) for more details.
</p>
</details>
<br />
<details>
<summary>flutter_web_auth</summary>
<p>
[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.
<InlineNotification>
</details>
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.
</Step>
</InlineNotification>
<Step title="Init Client" subtitle="1 step">
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`.
<Code className="language-dart" title="lib/main.dart">
{`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<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
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();
}
// ...
}`}
</Code>
</Step>
<Step title="Configure redirect URIs" subtitle="2 steps">
<RedirectUrisNative />
- 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"
<activity android:name="com.linusu.flutter_web_auth.CallbackActivity" android:exported="true">
<intent-filter android:label="flutter_web_auth">
<action android:name="android.intent.action.VIEW"/>
@ -143,72 +194,40 @@ In order to capture the callback url from Logto's sign-in web page, you will nee
</intent-filter>
</activity>
```
</p>
</details>
</Step>
<Step title="Init LogtoClient" subtitle="1 step">
<Step title="Implement sign-in and sign-out" subtitle="2 steps">
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.
<Code className="language-dart">
{`
import 'package:logto_dart_sdk/logto_dart_sdk.dart';
import 'package:http/http.dart' as http;
<Code className="language-dart" title="lib/main.dart">
{`class _MyHomePageState extends State<MyHomePage> {
// ...
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'),
);
// ...
}
}`}
</Code>
</Step>
<Step title="Sign In" subtitle="2 steps">
### Configure Redirect URI
<InlineNotification>
In the following steps, we assume your app has configured using `io.logo` as your schema .
</InlineNotification>
Let's switch to the Application details page of Logto Admin Console. Add a Redirect URI `io.logto://callback` and click "Save changes".
<UriInputField name="redirectUris" />
### Implement a sign-in method
<Code className="language-dart">
{`void signIn() async {
await logtoClient.signIn('${props.redirectUris[0] ?? 'io.logto://callback'}');
}
`}
</Code>
</Step>
<Step title="Sign Out" subtitle="1 step">
### Implement a sign-out method
```dart
void signOut() async {
await logtoClient.signOut();
}
```
<br />
<InlineNotification>
The `signOut` method will clear the user's session and remove the token from the secure storage.
@ -216,85 +235,39 @@ void signOut() async {
</Step>
<Step title="Handle authentication state" subtitle="1 step">
<Step title="Handle authentication status" subtitle="1 step">
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.
</Step>
```dart title="lib/main.dart"
class _MyHomePageState extends State<MyHomePage>{
bool isAuthenticated = false;
<Step title="Checkpoint: Test your application" subtitle="1 step">
// ...
void render() async {
if (await logtoClient.isAuthenticated()) {
setState(() {
isAuthenticated = true;
});
Now let's wrap up the implementation and test your application.
return;
}
<Code className="language-dart">
{`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<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
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<MyHomePage> {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SelectableText('My Demo App'),
isAuthenticated ? signOutButton : signInButton,
],
),
@ -324,8 +296,13 @@ class _MyHomePageState extends State<MyHomePage> {
);
}
}
`}
</Code>
```
</Step>
<Step title="Checkpoint: Test your application">
<Checkpoint />
</Step>

View file

@ -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';
<Steps>
@ -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 {