## Flutter quickstart This guide is a walkthrough on how to integrate Frontegg’s authentication magic into your app using the Frontegg Flutter SDK. Whether you need to add secure sign-ups, logins, multi-factor authentication, or even social logins, this guide has you covered with simple, step-by-step instructions to get things rolling in no time. Check out the sample package on GitHub br Or the package on pub.dev br Prerequisites Frontegg Hosted Login is required, and setting a [custom domain](/ciam/guides/env-settings/custom-domain) is highly recommended. iOS ≥ 14 Android ≥ 26 br ### Prepare your Frontegg workspace Navigate to [Frontegg Portal Settings](https://portal.frontegg.com/development/settings), If you don't have application follow the integration steps after signing up. Copy `FronteggDomain` to future steps from [Frontegg Portal Domain](https://portal.frontegg.com/development/settings/domains) br ![frontegg-domain](/assets/new-frontegg-domain.60f517108b4c93feb0326bd54894cfac97dcc0d10d29d12eb2cf4ef068816e5f.14341b2b.png) - Navigate to [Login Method Settings](https://portal.frontegg.com/development/authentication/hosted) - Toggle on Hosted login method for iOS: - Add `{{IOS_BUNDLE_IDENTIFIER}}://{{FRONTEGG_BASE_URL}}/ios/oauth/callback` - Toggle Hosted login method for Android: - Add `{{ANDROID_PACKAGE_NAME}}://{{FRONTEGG_BASE_URL}}/android/oauth/callback` - Add `https://{{FRONTEGG_BASE_URL}}/oauth/account/redirect/android/{{ANDROID_PACKAGE_NAME}}` - Replace `IOS_BUNDLE_IDENTIFIER` with your application identifier - Replace `FRONTEGG_BASE_URL` with your Frontegg base url, without **https**, i.e `FronteggDomain`. - Replace `ANDROID_PACKAGE_NAME` with your android package name ### Add Frontegg package to the project ``` dart pub add frontegg_flutter ``` br **Or manually:** br ``` dependencies: frontegg_flutter: ^1.0.0 ``` ### Setup iOS project To setup your SwiftUI application to communicate with Frontegg, you have to create a new file named `Frontegg.plist` under your root project directory, this file will store values to be used variables by Frontegg SDK: ``` baseUrl https://[DOMAIN_HOST_FROM_PREVIOUS_STEP] clientId [CLIENT_ID_FROM_PREVIOUS_STEP] ``` #### Multi-apps iOS support Prerequisites The use of this feature requires @frontegg/flutter@1.0.1 br This guide outlines the steps to configure your iOS application to support multiple applications. Modify the Frontegg.plist file and add `applicationId` to Frontegg.plist file: ``` applicationId your-application-id-uuid baseUrl https://your-domain.fronteg.com clientId your-client-id-uuid ``` #### Handle open app with URL To handle Login with magic link and other authentication methods that require to open the app with a URL, you have to add the following code to. ##### For `Objective-C` 1. Create `FronteggSwiftAdapter.swift` in your project and add the following code: ``` // FronteggSwiftAdapter.swift import Foundation import FronteggSwift @objc(FronteggSwiftAdapter) public class FronteggSwiftAdapter: NSObject { @objc public static let shared = FronteggSwiftAdapter() @objc public func handleOpenUrl(_ url: URL) -> Bool { return FronteggAuth.shared.handleOpenUrl(url) } } ``` br 1. Open `AppDelegate.m` file and import swift headers: ``` #import <[YOUR_PROJECT_NAME]-Swift.h> ``` br 1. Add URL handlers to `AppDelegate.m`: ``` #import <[YOUR_PROJECT_NAME]-Swift.h> // ...CODE... - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options { if([[FronteggSwiftAdapter shared] handleOpenUrl:url] ){ return TRUE; } return [RCTLinkingManager application:app openURL:url options:options]; } - (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray> * _Nullable))restorationHandler { if (userActivity.webpageURL != NULL){ if([[FronteggSwiftAdapter shared] handleOpenUrl:userActivity.webpageURL] ){ return TRUE; } } return [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler]; } ``` ##### For `Swift` 1. Open `AppDelegate.m` file and import swift headers: ``` import FronteggSwift ``` br 1. Add URL handlers to `AppDelegate.swift`: br ``` import UIKit import FronteggSwift @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { /* * Called when the app was launched with a url. Feel free to add additional processing here, * but if you want the App API to support tracking app url opens, make sure to keep this call */ func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { if(FronteggAuth.shared.handleOpenUrl(url, true)){ return true } return ApplicationDelegateProxy.shared.application(app, open: url, options: options) } /* * Called when the app was launched with an activity, including Universal Links. * Feel free to add additional processing here, but if you want the App API to support * tracking app url opens, make sure to keep this call */ func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { if let url = userActivity.webpageURL { if(FronteggAuth.shared.handleOpenUrl(url, true)){ return true } } return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler) } } ``` br #### Configure iOS associated domain Configuring your iOS associated domain is required for Magic Link authentication / Reset Password / Activate Account. 1. In order to add your iOS associated domain to your Frontegg application, you will need to update in each of your integrated Frontegg Environments the iOS associated domain that you would like to use with that Environment. Send a POST request to `https://api.frontegg.com/vendors/resources/associated-domains/v1/ios` with the following payload: ``` { "appId":[YOUR_ASSOCIATED_DOMAIN] } ``` br In order to use our API’s, follow [this guide](/ciam/api/overview) to generate a vendor token. 1. Next, you will need to add your associated domain to your iOS application. To do so, follow the steps below: - Open your project in Xcode. - Select your project in the Project Navigator. - Select your target. - Select the Signing & Capabilities tab. - Expand the Associated Domains section. - Click the + button. - Enter your associated domain in the format `applinks:[YOUR_ASSOCIATED_DOMAIN]`. - Enter your associated domain in the format `webcredentials:[YOUR_ASSOCIATED_DOMAIN]`. - Click Done. `[YOUR_ASSOCIATED_DOMAIN]` is the associated domain that you would like to use with your iOS application. For example, if you would like to use `https://example.com` as your associated domain, you would enter `applinks:example.com` and `webcredentials:example.com`. ### Setup Android project To set up your Android minimum sdk version, open root gradle file at`android/build.gradle`, and add/edit the `minSdkVersion` under `buildscript.ext`: ``` buildscript { ext { minSdkVersion = 26 // ... } } ``` #### Configure build config fields To set up your Android application on to communicate with Frontegg, you have to add `buildConfigField` property the gradle `android/app/build.gradle`. This property will store frontegg hostname (without https) and client id from previous step: ``` def fronteggDomain = "FRONTEGG_DOMAIN_HOST.com" // without protocol https:// def fronteggClientId = "FRONTEGG_CLIENT_ID" android { defaultConfig { manifestPlaceholders = [ "package_name" : applicationId, "frontegg_domain" : fronteggDomain, "frontegg_client_id": fronteggClientId ] buildConfigField "String", 'FRONTEGG_DOMAIN', "\"$fronteggDomain\"" buildConfigField "String", 'FRONTEGG_CLIENT_ID', "\"$fronteggClientId\"" buildConfigField "Boolean", 'FRONTEGG_USE_ASSETS_LINKS', "true" /** For using frontegg domain for deeplinks **/ buildConfigField "Boolean", 'FRONTEGG_USE_CHROME_CUSTOM_TABS', "true" /** For using custom chrome tab for social-logins **/ } } ``` br **NOTE:** `FRONTEGG_USE_ASSETS_LINKS` by default is `true`. `FRONTEGG_USE_CHROME_CUSTOM_TABS` by default is `true`. So, if you do not set up those values we will use the default ones. Add `bundleConfig=true` if it does not exist inside the android section inside the app gradle `android/app/build.gradle` ``` android { buildFeatures { buildConfig = true } } ``` br #### Add permissions to `AndroidManifest.xml` Add `INTERNET` permission to the app's manifest file. br ``` ``` br #### Configure Android AssetLinks Configuring your Android `AssetLinks` is required for Magic Link authentication / Reset Password / Activate Account / login with IdPs. br To add your `AssetLinks` to your Frontegg application, you will need to update in each of your integrated Frontegg Environments the `AssetLinks` that you would like to use with that Environment. Send a POST request to `https://api.frontegg.com/vendors/resources/associated-domains/v1/android` with the following payload: ``` { "packageName": "YOUR_APPLICATION_PACKAGE_NAME", "sha256CertFingerprints": ["YOUR_KEYSTORE_CERT_FINGERPRINTS"] } ``` br Each Android app has multiple certificate fingerprint, to get your `DEBUG` sha256CertFingerprint you have to run the below command. For Debug mode, run the following command and copy the `SHA-256` value. **NOTE:** make sure to choose the Variant and Config equals to `debug` ``` ./gradlew signingReport ################### # Example Output: ################### # Variant: debug # Config: debug # Store: /Users/davidfrontegg/.android/debug.keystore # Alias: AndroidDebugKey # MD5: 25:F5:99:23:FC:12:CA:10:8C:43:F4:02:7D:AD:DC:B6 # SHA1: FC:3C:88:D6:BF:4E:62:2E:F0:24:1D:DB:D7:15:36:D6:3E:14:84:50 # SHA-256: D9:6B:4A:FD:62:45:81:65:98:4D:5C:8C:A0:68:7B:7B:A5:31:BD:2B:9B:48:D9:CF:20:AE:56:FD:90:C1:C5:EE # Valid until: Tuesday, 18 June 2052 ``` br For Release mode, Extract the SHA256 using keytool from your `Release` keystore file: br ``` keytool -list -v -keystore /PATH/file.jks -alias YourAlias -storepass *** -keypass *** ``` br #### Enabling chrome custom tabs for social login To enable social login using Chrome Custom Tabs within your Android application, you need to modify the `android/app/build.gradle` file. Add a boolean `buildConfigField` for the `FRONTEGG_USE_CHROME_CUSTOM_TABS` flag and set it to true. By default, the SDK defaults to using the Chrome browser for social login. ``` def fronteggDomain = "FRONTEGG_DOMAIN_HOST.com" // without protocol https:// def fronteggClientId = "FRONTEGG_CLIENT_ID" android { defaultConfig { manifestPlaceholders = [ "package_name" : applicationId, "frontegg_domain" : fronteggDomain, "frontegg_client_id": fronteggClientId ] buildConfigField "String", 'FRONTEGG_DOMAIN', "\"$fronteggDomain\"" buildConfigField "String", 'FRONTEGG_CLIENT_ID', "\"$fronteggClientId\"" buildConfigField "Boolean", 'FRONTEGG_USE_CHROME_CUSTOM_TABS', "true" } } ``` br #### Multi-apps Android support Prerequisites The use of this feature requires minimum @frontegg/flutter@1.0.1 br Modify the `Build.gradle` file and add `FRONTEGG_APPLICATION_ID` buildConfigField into the `build.gradle` file: ``` def fronteggApplicationId = "your-application-id-uuid" ... android { ... buildConfigField "String", 'FRONTEGG_APPLICATION_ID', "\"$fronteggApplicationId\"" } ``` br ### Wrap your root widget with `FronteggProvider` ``` import 'package:flutter/material.dart'; import 'package:frontegg_flutter/frontegg_flutter.dart'; class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: FronteggProvider( child: const MainPage(), ), ); } } ``` br Or use `Provider` from the [plugin](https://pub.dev/packages/provider) or other suitable plugin. Make sure to `dispose` of the `FronteggFlutter`: ``` import 'package:flutter/material.dart'; import 'package:frontegg_flutter/frontegg_flutter.dart'; import 'package:provider/provider.dart'; class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: Provider( create: (_) => FronteggFlutter(), dispose: (_, frontegg) => frontegg.dispose(), child: const MainPage(), ), ); } } ``` ### Access the Frontegg instance To get the `FronteggFlutter` instance use the Frontegg BuildContext extension down the widget tree: ``` class MainPage extends StatelessWidget { const MainPage({super.key}); @override Widget build(BuildContext context) { final frontegg = context.frontegg; return const SizedBox(); } } ``` ### Login with Frontegg To login with Frontegg you can use the `context`.`frontegg` accessor and call `login` method: ``` class MainPage extends StatelessWidget { const MainPage({super.key}); @override Widget build(BuildContext context) { final frontegg = context.frontegg; return Scaffold( body: Center( child: ElevatedButton( child: const Text("Login"), onPressed: () async { await frontegg.login(); }, ), ), ); } } ``` br ### Switch user's tenant To switch a user's tenant you can use the `context`.`frontegg` accessor and call `switchTenant` method: ``` class MainPage extends StatelessWidget { const MainPage({super.key}); @override Widget build(BuildContext context) { final frontegg = context.frontegg; return Scaffold( body: Center( child: ElevatedButton( child: const Text("Login"), onPressed: () async { final tenantId = "TENANT_ID"; await frontegg.switchTenant(tenantId); }, ), ), ); } } ``` br ### Frontegg state `FronteggPlugin` has a `FronteggState` and this state is changing according to the plugin flowing: ``` class FronteggState { final String? accessToken; final String? refreshToken; final FronteggUser? user; final bool isAuthenticated; final bool isLoading; final bool initializing; final bool showLoader; final bool appLink; } ``` br To get the state of the `FronteggFlutter` you have two options: 1. Get `currentState`: ``` @override Widget build(BuildContext context) { final frontegg = context.frontegg; final fronteggState = frontegg.currentState; } ``` br 1. Listen to `stateChanged` stream: br ``` import 'package:flutter/material.dart'; import 'package:frontegg_flutter/frontegg_flutter.dart'; import 'package:frontegg_flutter_example/login_page.dart'; import 'package:frontegg_flutter_example/user_page.dart'; class MainPage extends StatelessWidget { const MainPage({super.key}); @override Widget build(BuildContext context) { final frontegg = context.frontegg; return Scaffold( body: Center( child: StreamBuilder( stream: frontegg.stateChanged, builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData) { final state = snapshot.data!; if (state.isAuthenticated && state.user != null) { return const UserPage(); } else if (state.initializing) { return const CircularProgressIndicator(); } else { return const LoginPage(); } } return const SizedBox(); }, ), ), ); } } ``` br ### Other Frontegg functionalities 1. `logout` - logs out from `FronteggFlutter`; 2. `refreshToken` - refreshes `accessToken` and `refreshToken` only if needed, returns `true` if refreshing succeeds; 3. `getConstants` - returns `Frontegg Flutter` initialization constants; 4. `directLoginAction` - direct login with `type` and `data`.