banking_dcb_sdk_flutter 1.0.9
banking_dcb_sdk_flutter: ^1.0.9 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: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';
import 'device_info_manager.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,
partnerName: 'cre'
);
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
final DeviceInfoManager _deviceInfoManager = DeviceInfoManager();
@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(deviceInfoManager: _deviceInfoManager), // ✅ fixed
);
}
}
class FirstPage extends StatelessWidget {
final DeviceInfoManager deviceInfoManager;
const FirstPage({super.key, required this.deviceInfoManager});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Welcome'),
),
body: Column(children: [
Center(
child: ElevatedButton(
child: Text('Go to DCB CRE'),
onPressed: () {
// If you don't want to come back to this screen on back press:
Navigator.push(
context,
MaterialPageRoute(builder: (_) => MyHome()),
);
},
),
),
ElevatedButton(
onPressed: () {
showDeviceInfoToast(
context: context,
getDeviceInfo: () => deviceInfoManager.getDeviceInfo(),
);
},
child: const Text('Get Device Info'),
),
]),
);
}
}
Future<void> showDeviceInfoToast({
required BuildContext context,
required Future<Map<String, Object?>> Function() getDeviceInfo,
}) async {
// 1) Fetch info
final deviceInfo = await getDeviceInfo();
final packageInfo = await PackageInfo.fromPlatform();
// 2) Build params (key-value)
final kv = <String, String>{
Constants.MANUFACTURER: '${deviceInfo[Constants.MANUFACTURER] ?? ''}',
Constants.MODEL: '${deviceInfo[Constants.MODEL] ?? ''}',
Constants.DEVICE_UUID: '${deviceInfo[Constants.DEVICE_UUID] ?? ''}',
Constants.OS: Platform.isAndroid ? "Android" : "iOS",
Constants.OS_VERSION: '${deviceInfo[Constants.OS_VERSION] ?? ''}',
Constants.APP_VERSION: packageInfo.version,
};
// 3) Format as key:value block
final formatted = kv.entries.map((e) => '${e.key}: ${e.value}').join('\n');
// Print to console also (optional)
debugPrint('----- DEVICE INFO -----\n$formatted\n-----------------------');
// 4) Show "toast" with copy action
if (context is! Element || !context.mounted) return; // ✅ compatible check
ScaffoldMessenger.of(context)
..clearSnackBars()
..showSnackBar(
SnackBar(
content: Text(
formatted,
maxLines: 6,
overflow: TextOverflow.ellipsis,
),
duration: const Duration(seconds: 8),
action: SnackBarAction(
label: 'COPY ALL',
onPressed: () async {
await Clipboard.setData(ClipboardData(text: formatted));
if (context is! Element || !context.mounted) return; // ✅ compatible check
ScaffoldMessenger.of(context)
..clearSnackBars()
..showSnackBar(const SnackBar(content: Text('Copied to clipboard')));
},
),
),
);
}
class Constants {
static const String MANUFACTURER = 'manufacturer';
static const String MODEL = 'model';
static const String DEVICE_UUID = 'device_uuid';
static const String OS = 'os';
static const String OS_VERSION = 'os_version';
static const String APP_VERSION = 'app_version';
static const String VERSION = 'version';
}
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(
//SBM
String module,
String photo,
WebViewCallbackFunction callback,
{String? nameInput,
String? emailInput,
String? phoneInput}) async {
setState(() {
name = nameInput?.isNotEmpty == true ? nameInput! : _nameController.text;
email =
emailInput?.isNotEmpty == true ? emailInput! : _emailController.text;
phone =
phoneInput?.isNotEmpty == true ? phoneInput! : _phoneController.text;
});
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,
// 'module': '/banking/dcb/credit_card/CRE',
// "redirect_url": "https://credilio.in",
"prospect_id": _prospectIDController.text.trim(),
"device_binded": true,
},
header: headers,
);
token = await jwt.sign(
SecretKey(clientSecret!),
expiresIn: expirationDuration,
);
print("Token generated FROM client side is: $token for module $module");
final sdkLoginResponse = await BankingDcbSdkFlutter.instance
.sdkLogin(context, token: token, partner: 'dcb');
print("sdkLoginResponse $sdkLoginResponse");
//do some activity
final publicApiResponse = await BankingDcbSdkFlutter.instance.publicCallAPI(
'',
'POST',
body: {'token': token},
);
print("Public API response: ${publicApiResponse}");
//do some activity
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(
'Dash',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
),
),
SizedBox(
height: 48,
child: TextButton(
style: TextButton.styleFrom(
foregroundColor: Colors.black,
),
onPressed: () {
if (_formKey.currentState!.validate()) {
createToken('${dotenv.env['SPENSE_MODULE']!}/all_transactions', '',
(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(
'all txn',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
),
),
//card_limits
SizedBox(
height: 48,
child: TextButton(
style: TextButton.styleFrom(
foregroundColor: Colors.black,
),
onPressed: () {
if (_formKey.currentState!.validate()) {
createToken('${dotenv.env['SPENSE_MODULE']!}/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(
'MC',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
),
),
// pay_bill
SizedBox(
height: 48,
child: TextButton(
style: TextButton.styleFrom(
foregroundColor: Colors.black,
),
onPressed: () {
if (_formKey.currentState!.validate()) {
createToken('${dotenv.env['SPENSE_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(
'PB',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
),
),
//enhance
SizedBox(
height: 48,
child: TextButton(
style: TextButton.styleFrom(
foregroundColor: Colors.black,
),
onPressed: () {
if (_formKey.currentState!.validate()) {
createToken('${dotenv.env['SPENSE_MODULE']!}/enhance', '',
(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(
'all txn',
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;
},
),
],
),
),
),
),
);
}
}