saafe_aa_sdk 1.0.1
saafe_aa_sdk: ^1.0.1 copied to clipboard
Flutter SDK for integrating SAAFE's secure authentication and financial experience
SAAFE SDK for Flutter #
This Flutter SDK provides a simple interface to integrate SAAFE's secure authentication and financial experience into your Flutter applications.
Features #
- 🔒 Secure authentication and redirection flows
- 📱 Platform detection (iOS, Android, Flutter)
- 🌓 Advanced theme support (light, dark, system) with automatic status bar styling
- 📏 Forced portrait orientation during flows
- 🔗 External links handling with intelligent fallbacks
- ✅ Confirmation dialogs for closing flows
- 🖥️ High-resolution WebView with optimized text rendering
- 🎛️ Customizable display modes (bottom sheet or full page)
- 🔘 Optional close button visibility control
- 📞 Enhanced callback handling with structured data for AA flow events
- 🎯 Client-controlled navigation (SDK doesn't auto-close)
Installation #
Add the SDK to your pubspec.yaml
:
dependencies:
saafe_aa_sdk: ^1.0.1
Usage #
Initialize the SDK #
Initialize the SDK in your main.dart
file before calling runApp()
:
import 'package:saafe_aa_sdk/saafe_sdk.dart';
void main() async {
// Initialize with default settings (sandbox environment)
await SaafeSdk.initialize();
// Or specify sandbox/production environment
// await SaafeSdk.initialize(useSandbox: true); // For sandbox (default)
// await SaafeSdk.initialize(useSandbox: false); // For production
// Or with a custom redirect URL
// await SaafeSdk.initialize(customRedirectUrl: 'https://your-custom-url.example.com/login');
runApp(const MyApp());
}
## Enhanced Callback Handling
The SDK now provides structured callback data for AA flow events:
### Success Callbacks (onComplete)
**Consent Approved:**
```json
{
"type": "AA",
"status": "approved",
"flowCompleted": true,
"consentHandle": "consent_handle_value",
"userRating": 5,
"redirectUrl": "redirect_url",
"timestamp": "2023-12-01T10:00:00.000Z"
}
Consent Rejected:
{
"type": "AA",
"status": "rejected",
"flowCompleted": true,
"consentHandle": "consent_handle_value",
"rejectionReason": "reason",
"rejectedByFips": ["fip1", "fip2"],
"userFeedback": "feedback_text",
"customFeedback": "custom_feedback_if_any",
"redirectUrl": "redirect_url",
"timestamp": "2023-12-01T10:00:00.000Z"
}
Error Callbacks (onError) #
{
"type": "error",
"status": "error",
"flowCompleted": false,
"errorCode": "ERROR_CODE",
"errorMessage": "Detailed error message",
"errorType": "network|validation|unknown",
"statusCode": 400,
"timestamp": "2023-12-01T10:00:00.000Z",
"userAgent": "browser_info",
"url": "current_url",
"stack": "error_stack_trace",
"consentHandle": "consent_handle_if_available",
"redirectUrl": "redirect_url_if_available"
}
Theme Support #
The SDK automatically adapts to system theme changes:
Dark Mode (#051923
header background) #
- Status bar icons: White
- Close button: White
- Automatic detection of system dark mode
Light Mode (#f9fafb
header background) #
- Status bar icons: Dark
- Close button: Dark
- Automatic detection of system light mode
The theme is automatically applied based on the device's current theme setting.
Client-Controlled Navigation #
⚠️ Important Change in v1.0.1: The SDK no longer automatically closes itself after callbacks. Your app now controls navigation:
final redirectInstance = await SaafeSdk.triggerRedirect(
context,
SaafeRedirectOptions(
fi: fi,
reqdate: reqdate,
ecreq: ecreq,
onComplete: (data) {
// Parse the completion data
final responseData = jsonDecode(data);
if (responseData['status'] == 'approved') {
// Close SDK and navigate to success screen
Navigator.of(context).pop();
Navigator.pushNamed(context, '/success');
} else if (responseData['status'] == 'rejected') {
// Handle rejection - maybe don't close immediately
_showRejectionDialog(responseData['rejectionReason']);
}
},
onError: (error) {
// Parse error details
final errorData = jsonDecode(error);
// Decide whether to close SDK or show retry option
if (errorData['errorType'] == 'network') {
_showRetryDialog(); // Keep SDK open for retry
} else {
Navigator.of(context).pop(); // Close SDK for other errors
_showErrorScreen();
}
},
onCancel: () {
// User cancelled - close SDK
Navigator.of(context).pop();
},
),
);
Environment Configuration #
The SDK supports two environments:
- Sandbox (default): Uses
https://stage-redirection.saafe.in/login
- For development and testing - Production: Uses
https://app-redirection.saafe.in/login
- For live applications
To switch environments, use the useSandbox
parameter during initialization:
// For sandbox (development/testing)
await SaafeSdk.initialize(useSandbox: true);
// For production (live environment)
await SaafeSdk.initialize(useSandbox: false);
Display Modes #
The SDK supports two display modes for the WebView:
- Bottom Sheet (default): Shows the WebView in a modal bottom sheet
- Full Page: Shows the WebView as a full page with left-to-right transition
// Bottom sheet mode (default)
final redirectInstance = await SaafeSdk.triggerRedirect(
context,
SaafeRedirectOptions(
// ... other parameters
displayMode: DisplayMode.bottomSheet,
),
);
// Full page mode
final redirectInstance = await SaafeSdk.triggerRedirect(
context,
SaafeRedirectOptions(
// ... other parameters
displayMode: DisplayMode.fullPage,
),
);
Close Button Visibility #
You can control whether the close button is shown in the WebView:
final redirectInstance = await SaafeSdk.triggerRedirect(
context,
SaafeRedirectOptions(
// ... other parameters
showCloseButton: true, // Show close button (default)
// showCloseButton: false, // Hide close button
),
);
Note: In full page mode, the close button is optional since users can use the system back button to exit. In bottom sheet mode, the close button provides an additional way to close the modal alongside the drag-to-dismiss gesture.
Both display modes support system back navigation with confirmation dialog to prevent accidental exits.
The SDK will automatically trigger the onComplete
callback with the received data.
Trigger the Redirect Flow #
Trigger the redirect flow using the parameters from your generated consent response:
// First, get the consent response from SAAFE API
// The response will look like this:
/*
{
"status": "success",
"code": "OperationSuccess",
"msg": "Operation Success",
"request_id": 7788,
"txn_id": "daa53abbc1-7fe7-4984-8879-197883daba9d",
"url": "https://sandbox.redirection.saafe.in/login/?fi=WU9AUEBGV0dXUB5QWUc=&reqdate=160520250536024&ecreq=PuH3d0DSVsNCH3tfu86D4iNKTOYZyyftje7GLsc84gwMcmjFifpO9Pf4v8gDZ0lN4pD7Koskq-CkrHFGRpkvh16HVVjCjmhHiQaKRGuRo1MjoxiqicJJcQP0k-MJwDYR6krtMHFr3RP_W4Trlj2aAxeoucAvoJhGI1Lm5fnsCnnU64BHl7vPHGyXovHZIXl4sr8_HLQPuBkgLUy5oeu0fDDY62ajnztF40KufzuNe5MLI0Aut2_OjR-S94rdFrkbM6IseaBsPbpZnnibU8GuKL3A9idlvBDbLRNvEtgnjKy6AKGMAWQtEC1l6waJKeedhl0j4qCpNLtNBOCXb3VS5sf5kafDitfuSgpJ0pfuA-8=",
"consent_handle": [
"8bce1c3b-f289-4c1c-8d4b-7ed2e0d3a31b",
"b6bc69f8-c77d-4070-a9ec-0d4ad7c72be2"
],
"vua": "8682807087@dashboard-aa-preprod",
"fi": "WU9AUEBGV0dXUB5QWUc=",
"reqdate": "160520250536024",
"ecreq": "PuH3d0DSVsNCH3tfu86D4iNKTOYZyyftje7GLsc84gwMcmjFifpO9Pf4v8gDZ0lN4pD7Koskq-CkrHFGRpkvh16HVVjCjmhHiQaKRGuRo1MjoxiqicJJcQP0k-MJwDYR6krtMHFr3RP_W4Trlj2aAxeoucAvoJhGI1Lm5fnsCnnU64BHl7vPHGyXovHZIXl4sr8_HLQPuBkgLUy5oeu0fDDY62ajnztF40KufzuNe5MLI0Aut2_OjR-S94rdFrkbM6IseaBsPbpZnnibU8GuKL3A9idlvBDbLRNvEtgnjKy6AKGMAWQtEC1l6waJKeedhl0j4qCpNLtNBOCXb3VS5sf5kafDitfuSgpJ0pfuA-8="
}
*/
// Extract the required parameters from the consent response
final consentResponse = await yourApiCallToGetConsent();
// Now use these parameters with the SDK
final redirectInstance = await SaafeSdk.triggerRedirect(
context,
SaafeRedirectOptions(
fi: consentResponse['fi'], // Use the 'fi' value from consent response
reqdate: consentResponse['reqdate'], // Use the 'reqdate' value from consent response
ecreq: consentResponse['ecreq'], // Use the 'ecreq' value from consent response
theme: 'light', // Optional: 'light', 'dark', or null for system default
displayMode: DisplayMode.bottomSheet, // Optional: DisplayMode.bottomSheet (default) or DisplayMode.fullPage
showCloseButton: true, // Optional: true (default) to show close button, false to hide
onComplete: (data) {
print('Flow completed: $data');
// Handle successful completion
},
onCancel: () {
print('Flow cancelled by user');
// Handle user cancellation
},
onError: (error) {
print('Error during flow: $error');
// Handle errors
},
onLoad: () {
print('WebView loaded successfully');
// Optional: handle WebView load event
},
),
);
Close the Redirect Flow #
You can programmatically close the flow:
// This will show a confirmation dialog before closing
redirectInstance.close();
URL Parameters #
The SDK automatically adds the following parameters to the redirect URL:
Platform Detection #
platform=ios
- For iOS devicesplatform=android
- For Android devicesplatform=flutter
- For other Flutter platforms
Theme Support #
theme=light
- Light themetheme=dark
- Dark theme
External URL Handling #
The SDK intelligently handles external URLs by:
- Detecting external links in the WebView
- Automatically opening them in the system's default browser
- Providing multiple fallback mechanisms if the default method fails
- Showing a useful error dialog with copy functionality if all launch attempts fail
You can also use the URL launcher directly in your app:
// Launch a URL in the external browser
await SaafeSdk.launchExternalUrl(
'https://example.com',
context: context, // Optional: provide context to show error dialog if needed
);
Example #
See the example
folder for a complete implementation example.
License #
This project is licensed under the MIT License - see the LICENSE file for details.