Flutter
The Flutter SDK wraps the Lean Link SDK in a webview_flutter-backed widget with one named constructor per flow.
PrerequisitesBefore calling any SDK method you need to have completed the backend setup in the Getting Started section:
- Create your Lean application in the developer dashboard to obtain an
app_token.- Authentication — OAuth flow for minting customer-scoped
access_tokens.- Creating a customer —
POST /customers/v1to obtain acustomer_id.- Sandbox testing — test users and mock bank.
This page covers only the client-side SDK integration.
Installation
Add the dependency to your pubspec.yaml:
dependencies:
lean_sdk_flutter: ^3.0.16Then install:
flutter pub getPackage 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:
| Parameter | Type | Notes |
|---|---|---|
appToken | String | Required. Your Lean application token. |
callback | LeanCallback? | Invoked with LeanResponse on close, success, or error. Status codes in LinkSDK statuses. |
customization | Map<String, String>? | Theme overrides — see Customisation. |
actionCancelled | LeanActionCancelled? | Fires when the user cancels before any other callback runs. |
isSandbox | bool | Default true. Set false for production. |
showLogs | bool | Default false. Verbose dev logging. |
version | String | Default "latest". |
env | String | Default "production". Internal. |
country | LeanCountry | LeanCountry.ae or LeanCountry.sa. Default ae. |
language | LeanLanguage | LeanLanguage.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 — useLean.updatePaymentSource().
Lean.createPaymentSource()
Deprecated — useLean.connect()withpermissions: [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
Updated about 5 hours ago
