sim_reader 1.0.2
sim_reader: ^1.0.2 copied to clipboard
A powerful Flutter plugin for reading SIM card information including carrier name, country code, phone number, network details, and more. Supports both single and dual SIM devices across Android and i [...]
SIM Reader 📱 #
Publisher Name | Pub Score | Pub Version |
---|---|---|
A Flutter plugin to read SIM card details like carrier, country code, phone number, and network info. Works on single & dual SIM devices for Android and iOS.
✨ Features #
- 📋 Comprehensive SIM Information: Carrier name, country code, MCC/MNC, phone number
- 📱 Dual SIM Support: Read information from multiple SIM cards
- 🌐 Network Details: Network operator, type (2G/3G/4G/5G), availability status
- 🔒 Permission Management: Built-in permission handling and requests
- 🚀 Cross Platform: Works on both Android and iOS
- ⚡ Easy Integration: Simple API with comprehensive error handling
- 🛡️ Privacy Focused: Respects platform limitations and user privacy
🚀 Quick Start #
Installation #
Add this to your package's pubspec.yaml
file:
dependencies:
sim_reader: ^1.0.0
permission_handler: ^10.4.3 # For runtime permissions
Then run:
flutter pub get
Basic Usage #
import 'package:sim_reader/sim_reader.dart';
import 'package:permission_handler/permission_handler.dart';
// Request permission first
await Permission.phone.request();
// Check if device has SIM card
bool hasSimCard = await SimReader.hasSimCard();
// Get primary SIM card information
SimInfo? simInfo = await SimReader.getSimInfo();
// Get all SIM cards (for dual SIM devices)
List<SimInfo> allSimCards = await SimReader.getAllSimInfo();
// Get network information
NetworkInfo? networkInfo = await SimReader.getNetworkInfo();
Complete Example #
import 'package:flutter/material.dart';
import 'package:sim_reader/sim_reader.dart';
import 'package:permission_handler/permission_handler.dart';
class SimReaderDemo extends StatefulWidget {
@override
_SimReaderDemoState createState() => _SimReaderDemoState();
}
class _SimReaderDemoState extends State<SimReaderDemo> {
List<SimInfo> simCards = [];
NetworkInfo? networkInfo;
bool isLoading = true;
String? error;
@override
void initState() {
super.initState();
loadSimInfo();
}
Future<void> loadSimInfo() async {
try {
// Request permission
PermissionStatus status = await Permission.phone.request();
if (!status.isGranted) {
setState(() {
error = 'Phone permission is required';
isLoading = false;
});
return;
}
// Get SIM information
final allSimInfo = await SimReader.getAllSimInfo();
final netInfo = await SimReader.getNetworkInfo();
setState(() {
simCards = allSimInfo;
networkInfo = netInfo;
isLoading = false;
});
} on SimReaderException catch (e) {
setState(() {
error = e.message;
isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('SIM Reader Demo')),
body: isLoading
? Center(child: CircularProgressIndicator())
: error != null
? Center(child: Text('Error: $error'))
: ListView.builder(
itemCount: simCards.length,
itemBuilder: (context, index) {
final sim = simCards[index];
return Card(
child: ListTile(
leading: Icon(Icons.sim_card),
title: Text(sim.carrierName ?? 'Unknown'),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Country: ${sim.countryCode?.toUpperCase()}'),
if (sim.phoneNumber != null)
Text('Phone: ${sim.phoneNumber}'),
Text('MCC/MNC: ${sim.mobileCountryCode}/${sim.mobileNetworkCode}'),
],
),
),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: loadSimInfo,
child: Icon(Icons.refresh),
),
);
}
}
🛠️ Platform Setup #
Android Configuration #
Add the following permissions to your android/app/src/main/AndroidManifest.xml
:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Required permissions -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- Optional: For phone number access on Android 10+ -->
<uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />
<application>
<!-- Your app configuration -->
</application>
</manifest>
iOS Configuration #
No additional configuration required. The plugin uses the CoreTelephony framework which is automatically available.
📱 iOS Permission Requirements #
Good News: No Explicit Permissions Required! #
Unlike Android, iOS doesn't require explicit permissions in Info.plist
for accessing SIM card information through the CoreTelephony framework. The SIM Reader plugin uses only public APIs that are automatically available.
🛠️ iOS Setup Steps (If needed, please use it. And if the SIM is not being detected, you can try using it.) #
1. Info.plist Configuration (Optional but Recommended) #
While not required, you can add usage descriptions for better App Store review process:
File: ios/Runner/Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- Your existing configuration -->
<!-- Optional: Add these for App Store transparency -->
<key>NSPhoneNumberUsageDescription</key>
<string>This app needs to access SIM card information to display carrier details and network information.</string>
<key>NSContactsUsageDescription</key>
<string>This app may access SIM card information for carrier and network details.</string>
<!-- Minimum iOS version -->
<key>MinimumOSVersion</key>
<string>9.0</string>
<!-- Your other app configurations -->
</dict>
</plist>
2. No Runtime Permission Requests Needed #
Unlike Android, you don't need to request runtime permissions for SIM access:
// ❌ NOT NEEDED on iOS
// await Permission.phone.request();
// ✅ Direct access works on iOS
List<SimInfo> simCards = await SimReader.getAllSimInfo();
3. iOS-Specific Implementation #
Here's how to handle iOS in your app:
import 'dart:io';
import 'package:sim_reader/sim_reader.dart';
class SimReaderHelper {
static Future<List<SimInfo>> getSimInfo() async {
try {
if (Platform.isIOS) {
// iOS - Direct access, no permissions needed
return await SimReader.getAllSimInfo();
} else {
// Android - Request permissions first
await Permission.phone.request();
return await SimReader.getAllSimInfo();
}
} catch (e) {
print('Error getting SIM info: $e');
return [];
}
}
}
🔒 iOS Privacy and Limitations #
What Works on iOS: #
- ✅ Carrier name
- ✅ Country code (ISO)
- ✅ Mobile Country Code (MCC)
- ✅ Mobile Network Code (MNC)
- ✅ Network operator name
- ✅ Network type detection
- ✅ Multiple SIM detection (iOS 12+)
What Doesn't Work on iOS: #
- ❌ Phone number (Apple privacy restriction)
- ❌ SIM serial number (not available via public APIs)
- ❌ Subscriber ID/IMSI (not available via public APIs)
- ❌ Detailed signal strength
iOS Versions Support: #
- iOS 9.0+: Basic SIM information
- iOS 12.0+: Enhanced dual SIM support
- iOS 14.1+: 5G network type detection
📋 Complete iOS Setup Example #
1. Update Info.plist #
<!-- ios/Runner/Info.plist -->
<key>CFBundleName</key>
<string>SIM Reader Example</string>
<key>CFBundleDisplayName</key>
<string>SIM Reader</string>
<!-- Optional: Usage descriptions -->
<key>NSPhoneNumberUsageDescription</key>
<string>Access SIM card information to display carrier and network details</string>
<!-- Minimum iOS version -->
<key>MinimumOSVersion</key>
<string>9.0</string>
📚 API Reference #
SimReader Class #
Static Methods
Method | Return Type | Description |
---|---|---|
getSimInfo() |
Future<SimInfo?> |
Get primary SIM card information |
getAllSimInfo() |
Future<List<SimInfo>> |
Get all available SIM cards |
hasSimCard() |
Future<bool> |
Check if device has SIM card |
getNetworkInfo() |
Future<NetworkInfo?> |
Get network operator information |
SimInfo Class #
Represents SIM card information:
class SimInfo {
final String? carrierName; // Carrier/operator name
final String? countryCode; // ISO country code (e.g., "US", "IN")
final String? mobileCountryCode; // Mobile Country Code (MCC)
final String? mobileNetworkCode; // Mobile Network Code (MNC)
final String? phoneNumber; // Phone number (limited availability)
final String? simSerialNumber; // SIM serial number/ICCID
final String? subscriberId; // Subscriber ID/IMSI (Android only)
final int? simSlotIndex; // SIM slot index (0, 1, etc.)
final bool isNetworkRoaming; // Whether device is roaming
}
Example Usage
SimInfo? simInfo = await SimReader.getSimInfo();
if (simInfo != null) {
print('Carrier: ${simInfo.carrierName}');
print('Country: ${simInfo.countryCode}');
print('Phone: ${simInfo.phoneNumber}');
print('MCC: ${simInfo.mobileCountryCode}');
print('MNC: ${simInfo.mobileNetworkCode}');
print('Roaming: ${simInfo.isNetworkRoaming}');
}
NetworkInfo Class #
Represents network information:
class NetworkInfo {
final String? networkOperatorName; // Network operator name
final String? networkOperator; // Network operator code
final String? networkType; // Network type (2G, 3G, 4G, 5G, LTE, etc.)
final bool isNetworkAvailable; // Network availability status
final int? signalStrength; // Signal strength (limited support)
}
Example Usage
NetworkInfo? networkInfo = await SimReader.getNetworkInfo();
if (networkInfo != null) {
print('Operator: ${networkInfo.networkOperatorName}');
print('Type: ${networkInfo.networkType}');
print('Available: ${networkInfo.isNetworkAvailable}');
}
SimReaderException #
Custom exception for SIM Reader specific errors:
try {
final simInfo = await SimReader.getSimInfo();
} on SimReaderException catch (e) {
print('SIM Reader Error: ${e.message}');
} catch (e) {
print('General Error: $e');
}
🔐 Permission Handling #
Automatic Permission Requests #
import 'package:permission_handler/permission_handler.dart';
Future<bool> requestSimPermissions() async {
PermissionStatus status = await Permission.phone.status;
if (status.isDenied) {
status = await Permission.phone.request();
}
if (status.isPermanentlyDenied) {
// Open app settings
await openAppSettings();
return false;
}
return status.isGranted;
}
// Usage
if (await requestSimPermissions()) {
final simInfo = await SimReader.getAllSimInfo();
// Handle SIM information
} else {
// Handle permission denied
}
Permission Status Checking #
Future<void> checkPermissionStatus() async {
PermissionStatus status = await Permission.phone.status;
switch (status) {
case PermissionStatus.granted:
print('Permission granted');
break;
case PermissionStatus.denied:
print('Permission denied');
break;
case PermissionStatus.permanentlyDenied:
print('Permission permanently denied');
break;
case PermissionStatus.restricted:
print('Permission restricted');
break;
default:
print('Permission status: $status');
}
}
🌍 Platform Differences #
Android Capabilities #
- ✅ Full SIM information access
- ✅ Phone number (when available and permitted)
- ✅ SIM serial number and subscriber ID
- ✅ Complete dual SIM support
- ✅ Detailed network type information
- ✅ Roaming status
iOS Capabilities #
- ✅ Carrier information (name, MCC, MNC)
- ✅ Country code
- ✅ Basic network information
- ✅ Multiple SIM detection (iOS 12+)
- ❌ Phone number (Apple privacy restriction)
- ❌ SIM serial number (not available via public APIs)
- ❌ Subscriber ID (not available via public APIs)
- ⚠️ Limited roaming information
🔧 Advanced Usage #
Dual SIM Handling #
Future<void> handleDualSim() async {
List<SimInfo> allSims = await SimReader.getAllSimInfo();
if (allSims.length > 1) {
print('Device has dual SIM');
for (int i = 0; i < allSims.length; i++) {
SimInfo sim = allSims[i];
print('SIM ${i + 1}:');
print(' Slot: ${sim.simSlotIndex}');
print(' Carrier: ${sim.carrierName}');
print(' Country: ${sim.countryCode}');
}
} else if (allSims.length == 1) {
print('Device has single SIM');
} else {
print('No SIM cards found');
}
}
Network Type Detection #
Future<void> checkNetworkType() async {
NetworkInfo? networkInfo = await SimReader.getNetworkInfo();
if (networkInfo != null) {
String networkType = networkInfo.networkType ?? 'Unknown';
switch (networkType.toUpperCase()) {
case 'LTE':
case '4G':
print('4G/LTE network detected');
break;
case '5G':
case 'NR':
print('5G network detected');
break;
case '3G':
case 'UMTS':
case 'HSDPA':
case 'HSUPA':
case 'HSPA':
print('3G network detected');
break;
case '2G':
case 'GSM':
case 'GPRS':
case 'EDGE':
print('2G network detected');
break;
default:
print('Network type: $networkType');
}
}
}
Error Handling Best Practices #
class SimReaderHelper {
static Future<List<SimInfo>> getSafeSimInfo() async {
try {
// Check permission first
if (!await Permission.phone.isGranted) {
throw SimReaderException('Permission not granted');
}
// Check if SIM card exists
if (!await SimReader.hasSimCard()) {
throw SimReaderException('No SIM card found');
}
// Get SIM information
List<SimInfo> simCards = await SimReader.getAllSimInfo();
if (simCards.isEmpty) {
throw SimReaderException('SIM information not available');
}
return simCards;
} on SimReaderException catch (e) {
print('SIM Reader specific error: ${e.message}');
rethrow;
} catch (e) {
print('Unexpected error: $e');
throw SimReaderException('Failed to get SIM information: $e');
}
}
}
🐛 Troubleshooting #
Common Issues #
1. Permission Denied
- Cause: Required permissions not granted
- Solution: Request
READ_PHONE_STATE
permission at runtime
2. No SIM Information
- Cause: SIM card not present or information restricted
- Solution: Check
hasSimCard()
first, test on real device
3. Build Errors
- Android: Update compileSdkVersion to 33+
- iOS: Run
pod install
in ios directory
4. Emulator Issues
- Solution: Test on real device with active SIM card
Debug Helper #
Future<void> debugSimReader() async {
print('=== SIM Reader Debug ===');
try {
// Permission check
bool hasPermission = await Permission.phone.isGranted;
print('Permission granted: $hasPermission');
if (!hasPermission) {
print('Requesting permission...');
PermissionStatus status = await Permission.phone.request();
print('Permission status: $status');
}
// SIM card check
bool hasSim = await SimReader.hasSimCard();
print('Has SIM card: $hasSim');
if (hasSim) {
// Get all SIM info
List<SimInfo> sims = await SimReader.getAllSimInfo();
print('SIM count: ${sims.length}');
for (int i = 0; i < sims.length; i++) {
SimInfo sim = sims[i];
print('SIM $i: ${sim.carrierName} (${sim.countryCode})');
}
// Network info
NetworkInfo? network = await SimReader.getNetworkInfo();
if (network != null) {
print('Network: ${network.networkOperatorName} (${network.networkType})');
}
}
} catch (e) {
print('Debug error: $e');
}
print('=== Debug Complete ===');
}
📋 Requirements #
- Flutter: 3.0.0 or higher
- Dart: 3.0.0 or higher
- Android: API level 16+ (Android 4.1+)
- iOS: iOS 9.0 or higher
🤝 Contributing #
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
Development Setup #
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add some amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
📄 License #
This project is licensed under the BSD-3-Clause License - see the LICENSE file for details.
🙏 Acknowledgments #
- Flutter team for the excellent plugin architecture
- Contributors and users who provided feedback and suggestions
- Mobile platform teams for providing SIM card access APIs
📞 Support #
- Issues: GitHub Issues
- Documentation: API Documentation
- Examples: Check the
/example
directory for complete working examples
🔗 Related Packages #
permission_handler
- For handling runtime permissionsdevice_info_plus
- For additional device informationconnectivity_plus
- For network connectivity status
Made with ❤️ by the SIM Reader team