Flutter

The Flutter SDK wraps the Lean Link SDK in a webview_flutter-backed widget with one named constructor per flow.

📘

Prerequisites

Before calling any SDK method you need to have completed the backend setup in the Getting Started section:

This page covers only the client-side SDK integration.

Installation

Add the dependency to your pubspec.yaml:

dependencies:
  lean_sdk_flutter: ^3.0.16

Then install:

flutter pub get

Package brings in webview_flutter, webview_flutter_wkwebview, webview_flutter_android, permission_handler, and url_launcher as transitive dependencies.

Demo

See the /example directory in link-sdk-flutter.

Usage

Instantiate the named constructor for the flow you want to launch and render the widget — for example inside a full-screen route, Dialog, or custom overlay.

import 'package:flutter/material.dart';
import 'package:lean_sdk_flutter/lean_sdk_flutter.dart';

class LeanConnectScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Lean.connect(
        appToken: "<YOUR_APP_TOKEN>",
        customerId: "CUSTOMER_ID",
        permissions: [
          LeanPermissions.identity,
          LeanPermissions.accounts,
          LeanPermissions.balance,
          LeanPermissions.transactions,
        ],
        accessToken: "<CUSTOMER_SCOPED_JWT>",
        isSandbox: true,
        country: LeanCountry.ae,
        language: LeanLanguage.en,
        callback: (LeanResponse response) {
          debugPrint("Status: ${response.status}");
          Navigator.of(context).pop();
        },
      ),
    );
  }
}

Shared constructor parameters

Every Lean.* constructor accepts these parameters in addition to its flow-specific fields:

ParameterTypeNotes
appTokenStringRequired. Your Lean application token.
callbackLeanCallback?Invoked with LeanResponse on close, success, or error. Status codes in LinkSDK statuses.
customizationMap<String, String>?Theme overrides — see Customisation.
actionCancelledLeanActionCancelled?Fires when the user cancels before any other callback runs.
isSandboxboolDefault true. Set false for production.
showLogsboolDefault false. Verbose dev logging.
versionStringDefault "latest".
envStringDefault "production". Internal.
countryLeanCountryLeanCountry.ae or LeanCountry.sa. Default ae.
languageLeanLanguageLeanLanguage.en or LeanLanguage.ar. Default en.

Available methods

Each flow has a dedicated named constructor on the Lean widget. Flow-specific parameter names use camelCase (e.g. customerId, paymentDestinationId).

Lean.connect()

Link a bank account for data access (and optionally payments).

Required: appToken, customerId, permissions.

Lean.connect(
  appToken: "<YOUR_APP_TOKEN>",
  customerId: "CUSTOMER_ID",
  permissions: [
    LeanPermissions.identity,
    LeanPermissions.accounts,
    LeanPermissions.balance,
    LeanPermissions.transactions,
  ],
  // Optional:
  accessTo: null,
  accessFrom: null,
  accessToken: null,
  bankIdentifier: null,
  failRedirectUrl: null,
  successRedirectUrl: null,
  paymentDestinationId: null,
  customerMetadata: null,
  showConsentExplanation: null,
  destinationAlias: null,
  destinationAvatar: null,
  callback: (response) { /* ... */ },
);

Lean.pay()

Initiate a bank-to-bank payment.

Required: appToken, paymentIntentId.

Lean.pay(
  appToken: "<YOUR_APP_TOKEN>",
  paymentIntentId: "PAYMENT_INTENT_ID",
  // Optional:
  accessToken: null,
  accountId: null,
  bankIdentifier: null,
  showBalances: false,
  failRedirectUrl: "https://yourapp.com/payment-failure",
  successRedirectUrl: "https://yourapp.com/payment-success",
  riskDetails: null,
  destinationAlias: null,
  destinationAvatar: null,
  callback: (response) { /* ... */ },
);

Lean.reconnect()

Restore a broken bank connection.

Required: appToken, reconnectId.

Lean.reconnect(
  appToken: "<YOUR_APP_TOKEN>",
  reconnectId: "RECONNECT_ID",
  // Optional:
  accessToken: null,
  customerMetadata: null,
  destinationAlias: null,
  destinationAvatar: null,
  callback: (response) { /* ... */ },
);

Lean.updatePaymentSource()

Add a beneficiary to an existing payment source.

Required: appToken, customerId. Pass paymentSourceId or entityId, plus paymentDestinationId.

Lean.updatePaymentSource(
  appToken: "<YOUR_APP_TOKEN>",
  customerId: "CUSTOMER_ID",
  paymentSourceId: "PAYMENT_SOURCE_ID",
  paymentDestinationId: "PAYMENT_DESTINATION_ID",
  // Optional:
  accessToken: null,
  failRedirectUrl: null,
  successRedirectUrl: null,
  destinationAlias: null,
  destinationAvatar: null,
  callback: (response) { /* ... */ },
);

Lean.checkout()

Open Finance guest payment — the customer authenticates at the bank and is redirected back to your successRedirectUrl / failRedirectUrl with status query parameters you feed into captureRedirect.

Required: appToken, paymentIntentId, failRedirectUrl, successRedirectUrl.

Lean.checkout(
  appToken: "<YOUR_APP_TOKEN>",
  paymentIntentId: "PAYMENT_INTENT_ID",
  failRedirectUrl: "https://yourapp.com/payment-failure",
  successRedirectUrl: "https://yourapp.com/payment-success",
  // Optional:
  accessToken: null,
  customerName: null,
  bankIdentifier: null,
  riskDetails: null,
  callback: (response) { /* ... */ },
);

Lean.authorizeConsent()

Grant a long-lived Open Finance consent. Redirects to the bank and returns to your successRedirectUrl / failRedirectUrl.

Required: appToken, customerId, consentId, failRedirectUrl, successRedirectUrl.

Lean.authorizeConsent(
  appToken: "<YOUR_APP_TOKEN>",
  customerId: "CUSTOMER_ID",
  consentId: "CONSENT_ID",
  failRedirectUrl: "https://yourapp.com/consent-failure",
  successRedirectUrl: "https://yourapp.com/consent-success",
  // Optional:
  accessToken: null,
  riskDetails: null,
  destinationAlias: null,
  destinationAvatar: null,
  callback: (response) { /* ... */ },
);

Lean.manageConsents()

Show a customer-facing consent management screen.

Required: appToken, customerId.

Lean.manageConsents(
  appToken: "<YOUR_APP_TOKEN>",
  customerId: "CUSTOMER_ID",
  accessToken: null,
  callback: (response) { /* ... */ },
);

Lean.captureRedirect()

Show the outcome screen after an Open Finance redirect. Extract the status query parameters Lean appends to your redirect URL (consentAttemptId, granularStatusCode, statusAdditionalInfo) and pass them in.

Required: appToken, customerId.

Lean.captureRedirect(
  appToken: "<YOUR_APP_TOKEN>",
  customerId: "<from_query_param>",
  consentAttemptId: "<from_query_param>",
  granularStatusCode: "<from_query_param>",
  statusAdditionalInfo: "<from_query_param>",
  accessToken: null,
  callback: (response) { /* ... */ },
);

Lean.verifyAddress()

Proof-of-address check.

Required: appToken, customerId, customerName, permissions (typically [LeanPermissions.identity]).

Lean.verifyAddress(
  appToken: "<YOUR_APP_TOKEN>",
  customerId: "CUSTOMER_ID",
  customerName: "Jane Doe",
  permissions: [LeanPermissions.identity],
  // Optional:
  accessToken: null,
  destinationAlias: null,
  destinationAvatar: null,
  callback: (response) { /* ... */ },
);

Deprecated methods

Lean.createBeneficiary()

⚠️

Deprecated — use Lean.updatePaymentSource().

Lean.createPaymentSource()

⚠️

Deprecated — use Lean.connect() with permissions: [LeanPermissions.payments].

Customisation

Pass a Map<String, String> to the customization parameter. Keys match the field names in Customisation (snake_case):

Lean.connect(
  appToken: "<YOUR_APP_TOKEN>",
  customerId: "CUSTOMER_ID",
  permissions: [LeanPermissions.identity, LeanPermissions.accounts],
  customization: {
    "theme_color": "#0080ff",
    "button_text_color": "#ffffff",
    "link_color": "#0080ff",
    "overlay_color": "rgba(0, 0, 0, 0.8)",
    "button_border_radius": "8",
    "dialog_mode": "contained",
    // Dark-mode overrides
    "theme_color_dark": "#1a1a1a",
    "button_text_color_dark": "#ffffff",
  },
);

Language

Pass language: LeanLanguage.ar to any constructor for Arabic (right-to-left). Omit for English.

Bank-list and account-selection shortcuts

Pass bankIdentifier to skip the bank-selection screen (see Create your own bank list). Pass accountId on Lean.pay(...) to skip the payment-source-selection screen.

Troubleshooting

iOS permissions

permission_handler handles camera and mic permissions requested by certain flows. Add the relevant Info.plist usage descriptions when your integration uses those flows:

<key>NSCameraUsageDescription</key>
<string>...</string>
<key>NSMicrophoneUsageDescription</key>
<string>...</string>

Android WebView rendering

The SDK uses webview_flutter_android. If you see rendering issues, confirm hardware acceleration is enabled for the host Activity and that android:usesCleartextTraffic isn't blocking the loader domain.

Related