MRT Card Reader
A Flutter package for reading transaction data from Dhaka MRT (Mass Rapid Transit) metro rail cards using NFC. This package allows you to easily access card balance and transaction history.

Features
- Cross-platform support: Works on both Android and iOS
- Check NFC availability on the device
- Read Dhaka MRT card balance
- Retrieve transaction history (journeys and top-ups)
- Parse transaction details including:
- Transaction timestamps
- Origin and destination
- Journey costs
- Top-up amounts
- Current balance
- Typed exception handling for better error management
- Session management with cancellation and timeout support
- Configurable logging for debugging
- Data validation for card responses
Requirements
- Flutter 3.0.0 or higher
- Android device with NFC capabilities (API level 19+)
- iOS device with NFC capabilities (iOS 13+)
Platform Support
- Android: Fully supported using NFC-F (FeliCa) protocol via native
transceive()API - iOS: Fully supported using FeliCa
readWithoutEncryption()API. The implementation automatically converts between raw commands (Android) and higher-level FeliCa API (iOS), providing seamless cross-platform compatibility with identical transaction data output.
Installation
Add mrt_card_reader to your pubspec.yaml:
dependencies:
mrt_card_reader: ^0.1.0
Then run:
flutter pub get
Platform-specific setup
Android
Add the following permissions to your AndroidManifest.xml file:
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="true" />
iOS
Add the following to your Info.plist file:
<key>NFCReaderUsageDescription</key>
<string>This app needs access to NFC to read MRT card data.</string>
<key>com.apple.developer.nfc.readersession.formats</key>
<array>
<string>TAG</string>
</array>
You also need to enable the "Near Field Communication Tag Reading" capability in your Xcode project:
- Open your project in Xcode
- Select your target
- Go to "Signing & Capabilities" tab
- Click "+ Capability"
- Add "Near Field Communication Tag Reading"
Usage
Basic Example
import 'package:flutter/material.dart';
import 'package:mrt_card_reader/mrt_card_reader.dart';
class MrtCardReaderDemo extends StatefulWidget {
const MrtCardReaderDemo({Key? key}) : super(key: key);
@override
State<MrtCardReaderDemo> createState() => _MrtCardReaderDemoState();
}
class _MrtCardReaderDemoState extends State<MrtCardReaderDemo> {
String _status = 'Ready to scan';
int? _balance;
List<MrtTransaction> _transactions = [];
Future<void> _startScan() async {
// Check if NFC is available
final isNfcAvailable = await MrtCardReader.isAvailable();
if (!isNfcAvailable) {
setState(() {
_status = 'NFC is not available on this device';
});
return;
}
// Start NFC session to read the card
await MrtCardReader.startSession(
onStatus: (status) {
setState(() {
_status = status;
});
},
onBalance: (balance) {
setState(() {
_balance = balance;
});
},
onTransactions: (transactions) {
setState(() {
_transactions = transactions;
});
},
onError: (exception) {
setState(() {
_status = 'Error: ${exception.message}';
});
},
timeout: const Duration(seconds: 30),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('MRT Card Reader')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Status: $_status'),
if (_balance != null) Text('Balance: \$${_balance! / 100}'),
ElevatedButton(
onPressed: _startScan,
child: const Text('Scan MRT Card'),
),
if (_transactions.isNotEmpty) ...[
const SizedBox(height: 20),
const Text('Recent Transactions',
style: TextStyle(fontWeight: FontWeight.bold),
),
Expanded(
child: ListView.builder(
itemCount: _transactions.length,
itemBuilder: (context, index) {
final transaction = _transactions[index];
return ListTile(
title: Text(transaction.isTopup
? 'Top-up at ${transaction.fromStation}'
: 'Journey from ${transaction.fromStation} to ${transaction.toStation}'),
subtitle: Text(transaction.timestamp),
trailing: Text(
transaction.isTopup
? '+\$${transaction.cost ?? 0 / 100}'
: '-\$${transaction.cost ?? 0 / 100}',
style: TextStyle(
color: transaction.isTopup ? Colors.green : Colors.red,
),
),
);
},
),
),
],
],
),
),
);
}
}
Advanced Usage with Instance API
For more control, use the instance-based API:
final reader = MrtCardReaderInstance(
logger: ConsoleLogger(),
timeout: const Duration(seconds: 60),
);
await reader.startSession(
onStatus: (status) => print(status),
onBalance: (balance) => print('Balance: $balance'),
onTransactions: (transactions) => print(transactions),
onError: (exception) => print('Error: ${exception.toString()}'),
// Check if session is active
if (reader.isSessionActive) {
print('Reading in progress...');
}
// Cancel session if needed
await reader.cancelSession();
For a complete example, check out the example directory in the repository.
API Reference
MrtCardReader
static Future<bool> isAvailable()
Checks if NFC is available on the device.
Returns true if NFC is available and enabled, false otherwise.
static Future<void> startSession({required Function(String) onStatus, required Function(int?) onBalance, required Function(List<MrtTransaction>) onTransactions, Function(MrtException)? onError, Duration timeout})
Starts an NFC reading session to retrieve MRT card data.
Parameters:
onStatus: Callback that provides status updates during the reading process.onBalance: Callback that provides the current card balance after successful reading (in paisa).onTransactions: Callback that provides the list of transactions read from the card.onError: Optional callback for typed exceptions (recommended).timeout: Optional timeout duration (default: 30 seconds).
MrtTransaction
Represents a transaction record from an MRT card.
Properties
fixedHeader: Raw header data from the card's data block.timestamp: Timestamp of when the transaction occurred (YYYY-MM-DD HH:MM).transactionType: Transaction type in hexadecimal format.fromStation: Name of the origin station (or top-up location).toStation: Name of the destination station (may be empty for top-ups).balance: Card balance after this transaction (in paisa, Taka * 100).cost: Cost of the journey or amount topped up (in paisa, null if unknown).trailing: Trailing data from the card's data block.isTopup: Whether this transaction represents a top-up rather than a journey.
Methods
copyWith({...}): Creates a copy with updated fields.toMap(): Converts to a map for serialization.fromMap(Map<String, dynamic>): Factory constructor to create from a map.toString(): Returns a string representation.
Exception Types
The package provides typed exceptions for better error handling:
MrtException: Base exception classNfcNotAvailableException: NFC not available or disabledInvalidCardException: Invalid or unsupported cardDataCorruptionException: Corrupted or unreadable card dataNfcTimeoutException: Reading operation timed outSessionAlreadyActiveException: Session already in progress
Platform-Specific Notes
Android
- Uses native NFC-F (FeliCa)
transceive()for direct card communication - Compatible with Android API level 19+
iOS
- Uses FeliCa
readWithoutEncryption()API (iOS 13+) - Automatically handles platform differences internally
- Same transaction data output as Android
- Requires FeliCa-compatible iPhone (iPhone 7 or later, except iPhone X)
Troubleshooting
Common Issues
NFC not available: Ensure NFC is enabled in device settings and the device supports NFC. On iOS, ensure you have a compatible device (iPhone 7 or later).
Card reading timeout: Increase the timeout duration or check card proximity.
Invalid card errors: Ensure you're using a valid Dhaka MRT Line 6 card (FeliCa card).
Data corruption: Card may be damaged or incompatible with the reader.
iOS not reading card: Ensure you have enabled the NFC capability in Xcode and have a FeliCa-compatible iPhone model.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the terms of the GNU General Public License v3.0 (GPL-3.0). See the LICENSE file for details.
Acknowledgements
The core idea and logic for this project were inspired by MRT Buddy, created by Aniruddha Adhikary. Thanks to the team for the original work!
Libraries
- mrt_card_reader
- A Flutter package for reading transaction data from Dhaka MRT (Mass Rapid Transit) metro rail cards using NFC.