banking_dcb_sdk_flutter 3.4.0
banking_dcb_sdk_flutter: ^3.4.0 copied to clipboard
Partner UI Integration Library for Flutter
example/lib/main.dart
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:app_links/app_links.dart';
import 'package:banking_dcb_sdk_flutter/banking_dcb_sdk_flutter.dart';
import 'package:banking_dcb_sdk_flutter_example/DeepLinkTest.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:android_id/android_id.dart';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:url_launcher/url_launcher.dart';
import 'DeepLinkTest2.dart';
class MyHttpOverrides extends HttpOverrides {
@override
HttpClient createHttpClient(SecurityContext? context) {
return super.createHttpClient(context)
..badCertificateCallback =
(X509Certificate cert, String host, int port) => true;
}
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await dotenv.load(fileName: "lib/.env");
HttpOverrides.global = MyHttpOverrides();
await BankingDcbSdkFlutter.init(
dotenv.env['BASE_URL']!,
whitelistedDomains: [
"razorpay.com",
"m2pfintech.com",
"https://capture.kyc.idfy.com"
],
deviceBindingEnabled: false,
);
runApp(MyApp());
}
final GlobalKey<NavigatorState> navKey = GlobalKey<NavigatorState>();
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final _appLinks = AppLinks();
StreamSubscription<Uri>? _sub;
Uri? _pendingUri; // for when navigator isn't ready yet
@override
void initState() {
super.initState();
_setupDeepLinks();
}
Future<void> _setupDeepLinks() async {
// Cold start
final initial = await _appLinks.getInitialLink();
if (initial != null) {
_handleDeepLink(initial);
}
// While running / in background
_sub = _appLinks.uriLinkStream.listen(
(uri) => _handleDeepLink(uri),
onError: (e) => debugPrint("DeepLink error: $e"),
);
}
void _handleDeepLink(Uri uri) {
debugPrint("DeepLink received: $uri");
if (uri.scheme == "dcbexample" && uri.host == "deeplink_test") {
_pendingUri = uri;
WidgetsBinding.instance.addPostFrameCallback((_) {
final nav = navKey.currentState;
if (nav == null) {
debugPrint("Navigator not ready yet, keeping pendingUri");
return;
}
// Clear pending uri
final toOpen = _pendingUri;
_pendingUri = null;
if (toOpen != null) {
debugPrint("Navigating to DeepLinkTest for $toOpen");
nav.push(
MaterialPageRoute(builder: (_) => DeepLinkTest()),
);
}
});
}
}
@override
void dispose() {
_sub?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: navKey,
debugShowCheckedModeBanner: false,
home: FirstPage(),
);
}
}
class FirstPage extends StatelessWidget {
Future<Map<String, dynamic>> _collectDeviceDetails() async {
final deviceInfo = DeviceInfoPlugin();
final packageInfo = await PackageInfo.fromPlatform();
final Map<String, dynamic> details = {
'app': {
'appName': packageInfo.appName,
'packageName': packageInfo.packageName,
'version': packageInfo.version,
'buildNumber': packageInfo.buildNumber,
},
};
if (Platform.isAndroid) {
final android = await deviceInfo.androidInfo;
// Use the Secure Android ID (e.g. d9785c61691d4d23) like the SDK's
// DeviceInfoManager does, NOT android.id which returns the build ID.
String androidId;
try {
androidId = await const AndroidId().getId() ?? 'unknown';
} catch (e) {
androidId = 'unknown';
}
details['platform'] = 'android';
details['device'] = {
'uuid': androidId,
'model': android.model,
'manufacturer': android.manufacturer,
'brand': android.brand,
'device': android.device,
'product': android.product,
'osVersion': android.version.release,
'sdkInt': android.version.sdkInt,
'fingerprint': android.fingerprint,
'isPhysicalDevice': android.isPhysicalDevice,
};
} else if (Platform.isIOS) {
final ios = await deviceInfo.iosInfo;
details['platform'] = 'ios';
details['device'] = {
'uuid': ios.identifierForVendor,
'name': ios.name,
'model': ios.model,
'systemName': ios.systemName,
'osVersion': ios.systemVersion,
'machine': ios.utsname.machine,
'isPhysicalDevice': ios.isPhysicalDevice,
};
} else {
details['platform'] = Platform.operatingSystem;
}
return details;
}
Future<void> _copyDeviceDetails(BuildContext context) async {
try {
final details = await _collectDeviceDetails();
final prettyJson = const JsonEncoder.withIndent(' ').convert(details);
await Clipboard.setData(ClipboardData(text: prettyJson));
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Device details copied to clipboard')),
);
} catch (e) {
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to copy device details: $e')),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Welcome'),
),
body: Column(children: [
Center(
child: ElevatedButton(
child: Text('Go to Home'),
onPressed: () {
// If you don't want to come back to this screen on back press:
Navigator.push(
context,
MaterialPageRoute(builder: (_) => MyHome()),
);
},
),
),
Center(
child: ElevatedButton.icon(
icon: const Icon(Icons.copy),
label: const Text('Copy Device Details'),
onPressed: () => _copyDeviceDetails(context),
),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => DeepLinkTest(),
));
},
child: Text('Deeplink Test'))
]),
);
}
}
class MyHome extends StatefulWidget {
@override
_MyHomeState createState() => _MyHomeState();
}
class _MyHomeState extends State<MyHome> {
final _formKey = GlobalKey<FormState>();
final _nameController = TextEditingController();
final _emailController = TextEditingController();
final _phoneController = TextEditingController();
final _prospectIDController = TextEditingController();
final _partnerUserIdController = TextEditingController();
String name = '';
String email = '';
String phone = '';
// final String? clientId = dotenv.env['KID'];
String baseUrl = '';
String token = '';
@override
void initState() {
baseUrl = dotenv.env['BASE_URL']!;
super.initState();
// _setupDeepLinks();
_checkSession((WebViewCallback action) {
switch (action.type) {
case WebViewCallbackType.redirect:
print("Redirected with status: ${action.status}");
break;
case WebViewCallbackType.logout:
print("User logged out");
break;
}
});
}
Future<void> _checkSession(WebViewCallbackFunction callback) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? name = prefs.getString('name');
String? email = prefs.getString('email');
String? phone = prefs.getString('phone');
setState(() {
_nameController.text = name ?? '';
_emailController.text = email ?? '';
_phoneController.text = phone ?? '';
});
}
Future<void> createToken(
//DCB
String module,
String photo,
WebViewCallbackFunction callback,
) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString('name', name);
await prefs.setString('email', email);
await prefs.setString('phone', phone);
final String clientId = dotenv.env['KID']!;
print("clientId $clientId");
final Map<String, Object> headers = {
'kid': clientId,
'typ': 'JWT',
'alg': 'HS256',
};
final Map<String, String> attributes = {
'name': name.trim(),
'photo': photo,
"partner_user_id": _partnerUserIdController.text.trim() ??"1234"
};
const Duration expirationDuration = Duration(milliseconds: 300000);
final clientSecret = dotenv.env['CLIENT_SECRET'];
print("clientSecret $clientSecret");
final jwt = JWT(
{
'attributes': attributes,
"prospect_id": _prospectIDController.text.trim(),
"device_binded": true,
},
header: headers,
);
token = await jwt.sign(
SecretKey(clientSecret!),
expiresIn: expirationDuration,
);
await BankingDcbSdkFlutter.instance.open(context, module, token, callback);
}
@override
void dispose() {
_nameController.dispose();
_emailController.dispose();
_phoneController.dispose();
_prospectIDController.dispose();
_partnerUserIdController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Spacer(),
SizedBox(
height: 48,
child: TextButton(
style: TextButton.styleFrom(
foregroundColor: Colors.black,
),
onPressed: () {
if (_formKey.currentState!.validate()) {
createToken(dotenv.env['SPENSE_MODULE']!, '',
(WebViewCallback action) {
switch (action.type) {
case WebViewCallbackType.redirect:
print(
"Redirected with status: ${action.status}");
break;
case WebViewCallbackType.logout:
print("User logged out");
break;
}
});
}
},
child: const Text(
'Dashboard',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
),
),
],
),
SizedBox(height: MediaQuery.of(context).size.height * 0.02),
const Text(
'User Details',
style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
),
SizedBox(height: MediaQuery.of(context).size.height * 0.02),
TextFormField(
controller: _prospectIDController,
decoration: const InputDecoration(
fillColor: Colors.grey,
labelText: 'Prospect ID',
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your prospect ID';
}
return null;
},
),
SizedBox(
height: 48,
child: TextButton(
style: TextButton.styleFrom(
foregroundColor: Colors.black,
),
onPressed: () {
if (_formKey.currentState!.validate()) {
createToken('${dotenv.env['MODULE']!}/pay_bill', '',
(WebViewCallback action) {
switch (action.type) {
case WebViewCallbackType.redirect:
print("Redirected with status: ${action.status}");
break;
case WebViewCallbackType.logout:
print("User logged out");
break;
}
});
}
},
child: const Text(
'Pay Bill',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
),
),
SizedBox(
height: 48,
child: TextButton(
style: TextButton.styleFrom(
foregroundColor: Colors.black,
),
onPressed: () {
createToken(
'/banking/dcb/credit_card/ZET/card_limits', '',
(WebViewCallback action) {
switch (action.type) {
case WebViewCallbackType.redirect:
print("Redirected with status: ${action.status}");
break;
case WebViewCallbackType.logout:
print("User logged out");
break;
}
});
},
child: const Text(
'Manage Card',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
),
),
],
),
),
),
),
);
}
}