Flutter (Legacy)

This documentation will help you integrating IDWise SDK in your Flutter app. (For flutter version older than 3.3.0)

📘

Sample Project

You can find the sample project for sample code for Flutter Integration.

1. Requirements

1.1 iOS Requirements

The minimum deployment target for IDWiseSDK is iOS 12.0. In order to use the SDK your application's minimum deployment target should be iOS 12.0 or higher.
On your development machine, you need to have XCode and CocoaPods installed. Both Intel and M1 (Apple Silicon) based machines are supported. When working with Cocoapods you might have to run some commands through Rosetta Stone compatibility mode.

If you are building an iOS project on VSCode and using an M1 machine then It's recommended to use Xcode for iOS builds because VSCode does not support Rosetta mode and we need the Rosetta option on both terminal and Xcode to run our project without any Issues of linking architecture.
Flutter also has disabled support for builds through VSCode starting from iOS 14, so we need Xcode for iOS builds anyway.

1.2 Android Requirements

The minSdkVersion is 21 and targetSdkVersion is set to 31 in IDWise Android SDK. It is recommended to install the Latest Android Studio on your development machine.

2. Installation

Please go through the following steps in order to the SDK available inside your Flutter application.

2.1 iOS Installation

To install pods in your Flutter project, first, go to projectfolder/ios. You can create a podfile If does not exist already by running the command pod init on the terminal by going into this ios directory. If you already have a podfile, you can follow the following steps.

IDWiseSDK can be installed via CocoaPods package manager from the IDWise private Cocoapods repository.
To add IDWise SDK to your project, first, ensure you have these two lines at the top of your Podfile file:

source 'https://cdn.cocoapods.org/'
source 'https://github.com/idwise/ios-sdk'

This adds IDWise private Cocoapods repository as a source to install packages from

Next, add this line to your Podfile but this time underneath your target node for your project:

pod 'IDWise'

You can have a look at the example Podfile provided in the root of this repository to see an example Podfile with both the changes above completed

After adding our dependency in your Podfile run:

pod install

2.2 Android Installation

Add the following in your app level build.gradle file located at projectRoot/android/app/build.gradle

  android {
    ...
    defaultConfig {
      ...
      multiDexEnabled true
    }
    buildFeatures {
      ...
      dataBinding true
    }
  }

If you are using a version of IDWise SDK older than 4.5.0 then add the following reference to your repositories section in your project-level build.gradle file located at projectRoot/android/build.gradle:

repositories {
  maven { url 'https://mobile-sdk.idwise.ai/releases/' }
}

Finally, Add the following dependency in your app level build.gradle file located at projectRoot/android/app/build.gradle.

Replace the x.y.z with the Latest IDWise SDK version

dependencies {
 ...
 implementation 'com.idwise:android-sdk:x.y.z'
}

To support RTL, add the following attribute in application tag in your project’s AndroidManifest.xml file located at projectRoot/android/app/src/main/AndroidManifest.xml

android:supportsRtl="true"

3. Flutter Usage

Invoking IDWise SDK is very simple. First import IDWise package in your .dart file from where you want to invoke IDWise SDK:

3.1 Required Imports

import IDWise

Flutter Bridging to native platforms
We need to create a MethodChannel class object with some unique name assigned to channel. To use MethodChannel class, we need to import following dart services to our project.

import 'dart:async';  
import 'package:flutter/services.dart';

We will create object of MethodChannel class in our widget class as a variable. METHOD_CHANNEL_NAME can be any unique string.

static const platformChannel = MethodChannel("<METHOD_CHANNEL_NAME>");

Now, we will use methodChannel object to invoke our native platform code. Calling the native code is an asynchronous operation so we need to create a method which returns a Future and is an async method like below. This is all the code that we need to do on flutter side and everything else will be handled in native platform classes.

3.2 Initializing the SDK

In order to use the SDK, we need to initialize it first with the <CLIENT_KEY> provided by IDWise and a theme parameter. You can pre-load this on componentDidMount() if you want to. Here is how we can initialize the SDK.

Future<void> initializeSDK() async {
  try {
    const initializeArgs = {
      "clientKey":"<CLIENT_KEY>",
      "theme": "SYSTEM_DEFAULT",
    };

    platformChannel.invokeMethod('initialize', initializeArgs);
  } on PlatformException catch (e) {
    print("Failed : '${e.message}'.");
  }
}

initialize method takes two parameters:

  • clientKey: is a key provided to you by IDWise to authenticate your app with IDWise backend.
  • theme: is an enum to indicate the theme (Light or Dark) to use for IDWise's UI. it accepts the following values:
    • IDWiseSDKTheme.SYSTEM_DEFAULT: follows the system theme.
    • IDWiseSDKTheme.LIGHT: to force the theme to be in light mode.
    • IDWiseSDKTheme.DARK: to force the theme to be in dark mode.

3.3 Start a new journey

Once the SDK is initialized, it becomes ready to start an ID verification journey flow. To commence the flow, the SDK offers a startJourney method, as demonstrated below.

Future<void> startJourney() async {
    try {
       const startJourneyArgs = {
        "journeyDefinitionId": "<FLOW_ID>",
        "referenceNo": "<REFERENCE_NUMBER>",
        "locale": "en"
      };
      platformChannel.invokeMethod('startJourney', startJourneyArgs);

      platformChannel.setMethodCallHandler((handler) async {
        switch (handler.method) {
          case 'onJourneyStarted':
            print("Method: onJourneyStarted, ${handler.arguments.toString()}");
            break;
          case 'onJourneyFinished':
            print("Method: onJourneyFinished");
            break;
          case 'onJourneyCancelled':
            print("Method: onJourneyCancelled");
            break;
          case 'onJourneyResumed':
            print("Method: onJourneyResumed, ${handler.arguments.toString()}");
            break;
          case 'onError':
            print("Method: onError, ${handler.arguments.toString()}");
            break;
          default :
            print('Unknown method from MethodChannel: ${handler.method}');
            break;
        }
      });

    } on PlatformException catch (e) {
      print("Failed : '${e.message}'.");
    }
}

This method takes the following parameters:

  • flowId: (also called Journey Definition ID): This is a unique identifier that identifies your journey flow. IDWise shares this with you when you register to use IDWise system.
  • referenceNo: (Optional) This parameter allows you to attach a unique identifier (such as a reference number) with the user undergoing the current journey. It's beneficial for connecting the journey to the user and/or the application that initiated it. You will receive this reference number in the webhook request.
  • locale: (Optional)This refers to the ISO code representing the desired language for the user interface components. Please reach out to IDWise IDWise Support for the list of available languages.

3.4 Callbacks

Add an implementation for method call handler, MethodChannel.MethodCallHandler, to receive the callback events from the native code. Throughout the journey, IDWise SDK sends back some events to the Hosting app. Here we can listen to those events:

These are the supported events

  • onJourneyStarted: This event is triggered when a user's journey begins. It serves to notify your application that the process has been initiated. Useful for logging or implementing any preparatory actions necessary for the journey.
  • onJourneyResumed: This event is triggered when a user's journey resumed. It serves to notify your application that the process has been resumed.
  • onJourneyFinished: This callback is activated upon the completion of a user's journey. It signifies that the user has successfully navigated through all steps and finished the process. This is typically where you would implement any code required to handle the completion of the journey.
  • onJourneyCancelled: This event is fired if the user decides to cancel the journey midway. It indicates the user's choice to discontinue the process. This could be useful for providing user-specific feedback or to trigger re-engagement strategies.
  • onError: This event is invoked when an error occurs at any point during the journey. It allows your application to respond to any unexpected issues, ensuring smooth error handling and user experience. Bellow all possible errors.

The journeyId, received in onJourneyResumed & onJourneyStarted, can then be used by your backend code to securely get the result of the ID verification.

Here is a full sample of how to start an ID verification flow.

4. Native Code to Start Journey

In order to link the Flutter with the Native SDKs, you need to Copy the following code into the respective platform class files as described below.

4.1 Native code for iOS

4.1.1 Creating Method Channel

In Flutter project, we will navigate to projectfolder/ios/Runner and will work in AppDelegate.swift class. In this file, we will declare a FlutterMethodChannel variable to user later in code.

 var channel: FlutterMethodChannel? = nil

4.1.2: Implementing MethodCallHandler

We will work in AppDelegate.swift class as we did above in step 4.1.1. In this file, we will write our code in didFinishLaunchingWithOptions method. Just before returning from this method, we will add our code.

To start a new journey just provide the UIViewController from which you want the flow to start then call IDWiseSDK.initialize method first with client key passed from Dart code and then you can call IDWise.startJourney method. If initialization is failed for any reason, you will get an error object with a code and a message explaining the reason of the error. In the following example, we called initialize method and then called startJourney method.

As in flutter, we don't have a UIViewController so we will create controller object using FlutterViewController class as done in code below. Then we will create a FluterMethodChannel object with same channel name as we assigned on dart side while creating the channel.
Finally, we will implement channel's handler method and depending on method name called from flutter class end, we will take action and invoke our native methods. As we invoked two methods from flutter class so we are handling for two methods in our AppDelegate.swift file.

    // Native code bridging Swift -> Dart , calling iOS SDK here
      let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
      let channel = FlutterMethodChannel(name: "com.idwise.fluttersampleproject/idwise",
                                                binaryMessenger: controller.binaryMessenger)
      self.channel = channel
      channel.setMethodCallHandler({ [self]
          (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
          
          switch call.method {
          case "initialize":
              // receiving arguments from Dart side and consuming here
              
              var clientKey: String = "" 
              var sdkTheme: IDWiseSDKTheme = IDWiseSDKTheme.systemDefault 
              if let parameteres = call.arguments as? [String:Any] {
                  if let clientkey = parameteres["clientKey"] as? String {
                      clientKey = clientkey
                  }
                  if let theme = parameteres["theme"] as? String {
                      if theme == "LIGHT" {
                          sdkTheme = IDWiseSDKTheme.light
                      } else if theme == "DARK" {
                          sdkTheme = IDWiseSDKTheme.dark
                      } else  {
                          sdkTheme = IDWiseSDKTheme.systemDefault
                      }
                  }
                
              }
              IDWise.initialize(clientKey: clientKey,theme: sdkTheme) { error in
                  result("got some error")
                  if let err = error {
                      channel.invokeMethod(
                        "onError",
                        arguments: ["errorCode": err.code,"message": err.message] as [String : Any])
                  }
              }

          case "startJourney":
              // receiving arguments from Dart side and consuming here

              var referenceNo: String = "" // optional parameter
              var locale: String = "en"
              var journeyDefinitionId = ""
              if let parameteres = call.arguments as? [String:Any] {
                  if let refNo = parameteres["referenceNo"] as? String {
                      referenceNo = refNo
                  }
                  if let loc = parameteres["locale"] as? String {
                      locale = loc
                  }
                  if let journeyDefId = parameteres["journeyDefinitionId"] as? String {
                      journeyDefinitionId = journeyDefId
                  }
              }
              IDWise.startJourney(journeyDefinitionId: journeyDefinitionId,referenceNumber: referenceNo,locale: locale, journeyDelegate: self)
              result("successfully started journey")
          default:
              result(FlutterMethodNotImplemented)
          }
          
      })

This will make IDWise SDK show a UI with a wizard to guide the user through completing the onboarding journey

4.1.3 Extension to AppDelegate

Outside of our AppDelegate class, we will paste this code which is an extension to AppDelegate.swift file. This code is used to conform to IDWiseSDKJourneyDelegate protocol and then to invoke callbacks back to Dart.

For example we can implement the protocol as an extension on the AppDelegate class like so:

 extension AppDelegate:IDWiseSDKJourneyDelegate {
    func onJourneyResumed(journeyID: String) {
        channel?.invokeMethod("onJourneyResumed", arguments: journeyID)
    }
    
    
    func onError(error : IDWiseSDKError) {
        channel?.invokeMethod("onError",
                    arguments: ["errorCode": error.code,"message": error.message] as [String : Any])
    }
    
    func JourneyStarted(journeyID: String) {
        channel?.invokeMethod( "onJourneyStarted", arguments: journeyID)
    }
    
    func JourneyFinished() {
        channel?.invokeMethod( "onJourneyFinished", arguments: nil)
    }
    
    func JourneyCancelled() {
        channel?.invokeMethod("onJourneyCancelled", arguments: nil)
    }
   
}

When the journey is started it is assigned a unique id called Journey ID in IDWise system and this is provided as a parameter, journeyID with the triggering of JourneyStarted event.
This identifier can be used to fetch the data and status of the journey from IDWise Journey Fetch API once you get the webhook call to your backend.

The steps that compose part of the journey and the prompts that user see are all cutomisable through IDWise cloud system.

4.2 Native Code for Android

Inside your Activity which is extended from FlutterActivity, you need to override the configureFlutterEngine(flutterEngine: FlutterEngine) method. Add the following code inside this method to handle the initialize and startJourney requests from your flutter code e.g main.dart.

private var methodChannel: MethodChannel? = null

methodChannel = MethodChannel(
            flutterEngine.dartExecutor.binaryMessenger,
            "YOUR_CHANNEL_NAME" //Example: "com.idwise.fluttersampleproject/idwise"
)

methodChannel?.setMethodCallHandler { call, result ->
      when (call.method) {
        "initialize" -> {
            val clientKey = call.argument<String>("clientKey")
            val theme = when (call.argument<String>("theme")) {
                "LIGHT" -> IDWiseSDKTheme.LIGHT
                "DARK" -> IDWiseSDKTheme.DARK
                else -> IDWiseSDKTheme.SYSTEM_DEFAULT
            }

            IDWise.initialize(clientKey!!, theme /*Optional*/) { error ->
                Log.d("IDWiseSDKCallback", "onError ${error?.message}")
                result.error("ERROR", error!!.message, null)
                methodChannel?.invokeMethod("onError", Gson().toJson(error))
            }
        }

        "startJourney" -> {
            val journeyDefinitionId = call.argument<String>("journeyDefinitionId")
            val referenceNo = call.argument<String>("referenceNo")
            val locale = call.argument<String>("locale")
            
            IDWise.startJourney(
                this,
                journeyDefinitionId!!,
                referenceNo,
                locale,
                callback = object : IDWiseSDKCallback {
                    override fun onJourneyStarted(journeyInfo: JourneyInfo) {
                        Log.d("IDWiseSDKCallback", "onJourneyStarted")
                        methodChannel?.invokeMethod("onJourneyStarted", journeyInfo.journeyId)
                    }

                    override fun onJourneyResumed(journeyInfo: JourneyInfo) {
                        Log.d("IDWiseSDKCallback", "onJourneyResumed")
                        methodChannel?.invokeMethod("onJourneyResumed", journeyInfo.journeyId)
                    }

                    override fun onJourneyCompleted(
                        journeyInfo: JourneyInfo,
                        isSucceeded: Boolean
                    ) {
                        Log.d("IDWiseSDKCallback", "onJourneyCompleted")
                        methodChannel?.invokeMethod("onJourneyFinished", null)
                    }

                    override fun onJourneyCancelled(journeyInfo: JourneyInfo?) {
                        Log.d("IDWiseSDKCallback", "onJourneyCancelled")
                        methodChannel?.invokeMethod("onJourneyCancelled", null)
                    }

                    override fun onError(error: IDWiseSDKError) {
                        Log.d(
                            "IDWiseSDKCallback",
                            "onError ${error.message}"
                        )
                        //Strigify error using GSON or with any other approach.
                        methodChannel?.invokeMethod("onError", Gson().toJson(error))
                    }
                }
            )

        }

        else -> result.error("NO_SUCH_METHOD", "NO SUCH METHOD", null)
    }
}

Android Code Example

You can find the sample MainActivity.kt that showcases the integration with IDWise Android Native Framework.

iOS Code Example

You can find the sample AppDelegate.swift that showcases the integration with IDWise iOS Framework.