fundstrack_lite 0.0.5
fundstrack_lite: ^0.0.5 copied to clipboard
A Flutter plugin to extract and parse debit/credit bank SMS on Android. Supports UPI and bank notifications.
fundstrack_lite #
A Flutter plugin for parsing and extracting banking transaction details from SMS inbox on Android devices.
- Detects and parses both traditional bank and UPI SMS notifications.
- Extracts: Amount, Account Number, Date, Time, Transaction Type (debit/credit), and Sender.
⚠️ Android only: iOS support is not available (due to OS restrictions).
Features #
- Reads SMS inbox for debit/credit transaction messages.
- Extracts structured data: amount, account, sender, date, time, transaction type.
- Ready to use with custom ListView widgets and API integrations.
Installation #
Add to your pubspec.yaml:
dependencies:
fundstrack_lite: ^0.0.1
Of course! Here’s your requested section as clean, ready-to-use Markdown for your README.md:
Android Setup #
1. Required Permission #
Add this line to your android/app/src/main/AndroidManifest.xml (outside <application>):
<uses-permission android:name="android.permission.READ_SMS"/>
2. Request Permission #
You need to request SMS read permission at runtime using permission_handler:
import 'package:permission_handler/permission_handler.dart';
Future<void> requestSmsPermission() async {
await Permission.sms.request();
}
Call this before attempting to read SMS.
Usage #
import 'package:fundstrack_lite/fundstrack_lite.dart';
final smsList = await FundstrackLite.getBankSms([]);
// Returns: List<Map<String, dynamic>>
Each parsed message includes:
amount: Transaction amountaccount: Account number (often last 4-6 digits or masked)type:"debit"or"credit"sender: Bank sender (e.g., AXISBK, SBI)date: Transaction date (parsed from SMS, if available)time: Transaction time (parsed from SMS, if available)smsTimestamp: SMS received time (milliseconds since epoch)body: Full SMS text
UI Example #
ListView.builder(
itemCount: smsList.length,
itemBuilder: (context, i) {
final sms = smsList[i];
return ListTile(
title: Text("Amount: ${sms['amount'] ?? '--'}"),
subtitle: Text("Type: ${sms['type']} - Account: ${sms['account']}"),
);
},
);
API Integration #
You can send each SMS to your backend for storage:
import 'package:http/http.dart' as http;
import 'dart:convert';
Future<void> sendSmsToServer(Map<String, dynamic> sms) async {
await http.post(
Uri.parse('https://your-api-endpoint.com/sms'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode(sms),
);
}
Example App: Full user guide from installation of package from pub.dev to accessing the code functions #
- How to add the dependency in
pubspec.yaml - Permission setup
- Full working
main.dartfor a basic Flutter app that scans and lists transaction SMS - Clear instructions you can use directly in your documentation or as your package’s
example/lib/main.dart
1. Add to your pubspec.yaml: #
dependencies:
flutter:
sdk: flutter
fundstrack_lite: ^0.0.2
permission_handler: ^11.3.0
2. Android Permission Setup #
In android/app/src/main/AndroidManifest.xml (outside <application>):
<uses-permission android:name="android.permission.READ_SMS"/>
3. Example Flutter App (main.dart) #
Here’s a full working app:
import 'package:flutter/material.dart';
import 'package:fundstrack_lite/fundstrack_lite.dart';
import 'package:permission_handler/permission_handler.dart';
void main() {
runApp(const FundstrackLiteDemoApp());
}
class FundstrackLiteDemoApp extends StatelessWidget {
const FundstrackLiteDemoApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'fundstrack_lite Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
useMaterial3: true,
),
home: const BankSmsScreen(),
);
}
}
class BankSmsScreen extends StatefulWidget {
const BankSmsScreen({super.key});
@override
State<BankSmsScreen> createState() => _BankSmsScreenState();
}
class _BankSmsScreenState extends State<BankSmsScreen> {
List<Map<String, dynamic>>? _smsList;
String? _error;
bool _loading = false;
@override
void initState() {
super.initState();
_loadSms();
}
Future<void> _loadSms() async {
setState(() {
_loading = true;
_error = null;
});
try {
// 1. Request SMS permission
final perm = await Permission.sms.request();
if (!perm.isGranted) {
setState(() {
_error = 'SMS permission not granted. Please enable to continue.';
_loading = false;
});
return;
}
// 2. Fetch all transaction SMS
final smsList = await FundstrackLite.getBankSms([]);
setState(() {
_smsList = smsList;
_loading = false;
});
} catch (e) {
setState(() {
_error = e.toString();
_loading = false;
});
}
}
@override
Widget build(BuildContext context) {
Widget body;
if (_loading) {
body = const Center(child: CircularProgressIndicator());
} else if (_error != null) {
body = Center(child: Text('Error: $_error'));
} else if (_smsList == null || _smsList!.isEmpty) {
body = const Center(child: Text('No debit/credit SMS found.'));
} else {
body = _smsListView(_smsList!);
}
return Scaffold(
appBar: AppBar(
title: const Text('Bank SMS Transactions'),
actions: [
IconButton(
icon: const Icon(Icons.refresh),
onPressed: _loadSms,
tooltip: "Refresh",
),
],
),
body: body,
);
}
Widget _smsListView(List<Map<String, dynamic>> smsList) {
return ListView.separated(
itemCount: smsList.length,
separatorBuilder: (_, __) => const Divider(),
itemBuilder: (context, index) {
final sms = smsList[index];
final DateTime smsTime = DateTime.fromMillisecondsSinceEpoch(
(sms['smsTimestamp'] ?? 0) is int
? sms['smsTimestamp'] ?? 0
: int.tryParse(sms['smsTimestamp']?.toString() ?? '0') ?? 0,
);
final dateStr = (sms['date'] != null && (sms['date'] as String).isNotEmpty)
? sms['date']
: "${smsTime.day.toString().padLeft(2, '0')}/${smsTime.month.toString().padLeft(2, '0')}/${smsTime.year}";
final timeStr = (sms['time'] != null && (sms['time'] as String).isNotEmpty)
? sms['time']
: "${smsTime.hour.toString().padLeft(2, '0')}:${smsTime.minute.toString().padLeft(2, '0')}";
return Card(
margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
elevation: 2,
child: Padding(
padding: const EdgeInsets.all(14.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Amount: ${sms['amount'] ?? '--'}",
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
Text("Account Number: ${sms['account'] ?? '--'}"),
RichText(
text: TextSpan(
style: DefaultTextStyle.of(context).style,
children: [
const TextSpan(text: "Type: "),
TextSpan(
text: "${sms['type'] ?? '--'}",
style: TextStyle(
color: (sms['type'] == 'debit')
? Colors.red
: (sms['type'] == 'credit')
? Colors.green
: Colors.black,
fontWeight: FontWeight.bold,
),
),
],
),
),
Text("Sender: ${sms['sender'] ?? '--'}"),
Text("Date: $dateStr"),
Text("Time: $timeStr"),
// Uncomment to show full SMS
// Text("Full SMS: ${sms['body'] ?? ''}", style: TextStyle(color: Colors.grey.shade600, fontSize: 12)),
],
),
),
);
},
);
}
}
What This Example Does: #
- Requests SMS permission at startup.
- Fetches all debit/credit bank SMS using the plugin.
- Lists them with Amount, Account, Transaction Type, Sender, Date, Time.
- Shows “debit” in red, “credit” in green.
- Handles permissions, errors, and empty results.
- Has a Refresh button in the AppBar.
Known Error Log Notes: #
- If you hit an error as below:
Your project is configured with Android NDK 26.3.11579264, but the following plugin(s) depend on a different Android NDK version:
- fundstrack_lite requires Android NDK 27.0.12077973
- permission_handler_android requires Android NDK 27.0.12077973
Fix this issue by using the highest Android NDK version (they are backward compatible).
Add the following to /Users/paradox/Documents/ft/fundstrackexampleapp/android/app/build.gradle.kts:
android {
ndkVersion = "27.0.12077973"
...
}
the Dart compiler exited unexpectedly.
Use ndkVersion = "27.0.12077973" if the ndk gives issues; it worked on macos 2017 versions and flutter 3.29.0 and dart 3.7.2 with kotlin 1.8
Limitations #
- Android only.
- Only works if SMS read permission is granted.
- Some SMS formats may not be parsed; contribute improvements via PR!
Contributions #
Feel free to open an issue or PR to improve pattern recognition for new bank formats.