fundstrack_lite 0.0.5 copy "fundstrack_lite: ^0.0.5" to clipboard
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 amount
  • account: 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.dart for 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.


0
likes
0
points
21
downloads

Publisher

verified publisherfundsroom.com

Weekly Downloads

A Flutter plugin to extract and parse debit/credit bank SMS on Android. Supports UPI and bank notifications.

Homepage
View/report issues

License

unknown (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on fundstrack_lite

Packages that implement fundstrack_lite