ussd_handler 0.0.1 copy "ussd_handler: ^0.0.1" to clipboard
ussd_handler: ^0.0.1 copied to clipboard

A complete Flutter plugin for handling USSD codes on Android and iOS with advanced features including multi-USSD sessions.

example/lib/main.dart

import 'package:flutter/material.dart';

import 'dart:async';

import 'package:ussd_handler/ussd_handler.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String _platformVersion = 'Unknown';
  String _ussdResult = '';
  String _ussdDirectResult = '';

  String _systemInfo = '';
  bool _isUssdSupported = false;

  // Variables for multi-SIM
  int? _selectedSubscriptionId;
  List<Map<String, dynamic>> _simInfoList = [];
  int _simCount = 1;
  bool _supportsMultiSim = false;
  final _ussdController = TextEditingController();
  final _ussdDirectController = TextEditingController();
  final _ussdSessionController = TextEditingController();
  final _multiSessionController = TextEditingController();
  final _multiSessionMessageController = TextEditingController();

  // Variables for multi-session sessions
  String _multiSessionResult = '';
  bool _isMultiSessionActive = false;

  @override
  void initState() {
    super.initState();
    initPlatformState();
  }

  // Platform messages are asynchronous, so we initialize in an async method.
  Future<void> initPlatformState() async {
    String platformVersion;
    bool ussdSupported = false;

    // Platform messages may fail, so we use a try/catch PlatformException.
    try {
      platformVersion =
          await UssdHandler.getPlatformVersion() ?? 'Unknown platform version';
      ussdSupported = await UssdHandler.isUssdSupported();

      // Get system information including multi-SIM
      final systemInfo = await UssdHandler.getSystemInfo();
      if (systemInfo != null) {
        _simCount = systemInfo.simCount;
        _supportsMultiSim = systemInfo.supportsMultiSim;

        if (systemInfo.additionalInfo['simInfoList'] != null) {
          _simInfoList = List<Map<String, dynamic>>.from(
            systemInfo.additionalInfo['simInfoList'],
          );
        }
      }
    } catch (e) {
      platformVersion = 'Failed to get platform version.';
    }

    // If the widget was removed from the tree while the asynchronous platform
    // message was in flight, we want to discard the reply rather than calling
    // setState to update our non-existent appearance.
    if (!mounted) return;

    setState(() {
      _platformVersion = platformVersion;
      _isUssdSupported = ussdSupported;
    });
  }

  Future<void> _executeUssd() async {
    if (_ussdController.text.isEmpty) {
      setState(() {
        _ussdResult = 'Please enter a USSD code';
      });
      return;
    }

    try {
      final response = await UssdHandler.executeUssd(
        _ussdController.text,
        subscriptionId: _selectedSubscriptionId,
      );

      setState(() {
        if (response.success) {
          _ussdResult = 'Success: ${response.response ?? 'USSD code executed'}';
        } else {
          _ussdResult = 'Error: ${response.errorMessage ?? 'Unknown error'}';
        }
      });
    } catch (e) {
      setState(() {
        _ussdResult = 'Error: $e';
      });
    }
  }

  Future<void> _executeUssdDirect() async {
    if (_ussdDirectController.text.isEmpty) {
      setState(() {
        _ussdDirectResult = 'Please enter a USSD code';
      });
      return;
    }

    setState(() {
      _ussdDirectResult = 'Checking permissions and executing direct USSD...';
    });

    // Check permissions before executing
    final hasPermissions = await UssdHandler.hasUssdDirectPermissions();
    if (!hasPermissions) {
      setState(() {
        _ussdDirectResult =
            'Requesting necessary permissions...\n\n'
            'The system will ask you to grant permissions:\n'
            '• CALL_PHONE (required)\n'
            '• READ_PHONE_STATE (required)\n\n'
            'Accept both permissions to use direct USSD.';
      });
    }

    try {
      final response = await UssdHandler.executeUssdDirect(
        _ussdDirectController.text,
        timeoutSeconds: 30,
        subscriptionId: _selectedSubscriptionId,
        iosFallbackToStandard: true, // Enable fallback for iOS
      );

      setState(() {
        if (response != null) {
          _ussdDirectResult = 'Direct response: $response';
        } else {
          _ussdDirectResult =
              'No response received or an error occurred.\n\n'
              'Possible causes:\n'
              '• The USSD code is not valid\n'
              '• The carrier does not support this code\n'
              '• Network issues\n'
              '• Insufficient permissions\n\n'
              'Use "Get Detailed Information" for more details.';
        }
      });
    } catch (e) {
      setState(() {
        String errorMsg = e.toString();

        if (errorMsg.contains('PERMISSION_DENIED')) {
          _ussdDirectResult =
              'Permission Error:\n$e\n\n'
              'To use direct USSD you need to grant these permissions:\n'
              '• CALL_PHONE (required)\n'
              '• READ_PHONE_STATE (required)\n\n'
              'Go to Settings > Apps > Permissions to grant them manually.';
        } else if (errorMsg.contains('USSD_FAILED')) {
          _ussdDirectResult =
              'USSD Error:\n$e\n\n'
              'This may indicate that:\n'
              '• The USSD code is not valid for your carrier\n'
              '• The USSD service is not available\n'
              '• There are network connectivity issues';
        } else if (errorMsg.contains('UNSUPPORTED_VERSION')) {
          _ussdDirectResult =
              'Version Error:\n$e\n\n'
              'Direct USSD requires Android 8.0 (API 26) or higher.';
        } else if (errorMsg.contains('SIM_NOT_READY')) {
          _ussdDirectResult =
              'SIM Error:\n$e\n\n'
              'Check that your SIM is inserted and working correctly.';
        } else {
          _ussdDirectResult = 'Unknown error:\n$e';
        }
      });
    }
  }

  Future<void> _getSystemInfo() async {
    setState(() {
      _systemInfo = 'Getting system information...';
    });

    try {
      final systemInfo = await UssdHandler.getSystemInfo();
      setState(() {
        if (systemInfo != null) {
          _systemInfo = _formatSystemInfo(systemInfo);
        } else {
          _systemInfo = 'Could not get system information';
        }
      });
    } catch (e) {
      setState(() {
        _systemInfo = 'Error getting system information: $e';
      });
    }
  }

  String _formatSystemInfo(SystemInfo systemInfo) {
    final buffer = StringBuffer();

    buffer.writeln('📱 SYSTEM INFORMATION');
    buffer.writeln('═══════════════════════════════');
    buffer.writeln('');

    // Basic system information
    buffer.writeln('🔧 Operating System:');
    buffer.writeln('  • Platform: ${systemInfo.platform}');
    if (systemInfo.platform.toLowerCase() == 'android' &&
        systemInfo.androidVersion != null) {
      buffer.writeln('  • Android Version: API ${systemInfo.androidVersion}');
    } else if (systemInfo.platform.toLowerCase() == 'ios' &&
        systemInfo.iosVersion != null) {
      buffer.writeln('  • iOS Version: ${systemInfo.iosVersion}');
    }
    buffer.writeln('  • Device: ${systemInfo.deviceModel}');
    buffer.writeln('  • Name: ${systemInfo.deviceName}');
    buffer.writeln('');

    // USSD Support
    buffer.writeln('📞 USSD Support:');
    buffer.writeln(
      '  • Basic USSD: ${systemInfo.ussdSupported ? "✅ Supported" : "❌ Not supported"}',
    );
    buffer.writeln(
      '  • Direct USSD: ${systemInfo.ussdDirectSupported ? "✅ Supported" : "❌ Not supported"}',
    );
    buffer.writeln(
      '  • Multi-session: ${systemInfo.multiSessionSupported ? "✅ Supported" : "❌ Not supported"}',
    );
    buffer.writeln(
      '  • Accessibility Service: ${systemInfo.accessibilityServiceSupported ? "✅ Supported" : "❌ Not supported"}',
    );
    buffer.writeln('');

    // SIM Information
    buffer.writeln('📱 SIM Information:');
    buffer.writeln('  • Number of SIMs: ${systemInfo.simCount}');
    buffer.writeln(
      '  • Multi-SIM: ${systemInfo.supportsMultiSim ? "✅ Supported" : "❌ Not supported"}',
    );

    // Detailed SIM information if available
    final simInfoList = systemInfo.additionalInfo['simInfoList'];
    if (simInfoList != null && simInfoList is List && simInfoList.isNotEmpty) {
      buffer.writeln('');
      buffer.writeln('  📋 SIM Details:');
      for (var simInfo in simInfoList) {
        if (simInfo is Map<String, dynamic>) {
          final slotIndex = simInfo['slotIndex'] ?? 'N/A';
          final displayName = simInfo['displayName'] ?? 'No name';
          final carrierName = simInfo['carrierName'] ?? 'Unknown carrier';
          final countryIso = simInfo['countryIso'] ?? 'N/A';
          final isEmbedded = simInfo['isEmbedded'] ?? false;

          buffer.writeln('    🔸 SIM $slotIndex:');
          buffer.writeln('      • Name: $displayName');
          buffer.writeln('      • Carrier: $carrierName');
          buffer.writeln('      • Country: ${countryIso.toUpperCase()}');
          buffer.writeln(
            '      • Type: ${isEmbedded ? "eSIM" : "Physical SIM"}',
          );
        }
      }
    }

    buffer.writeln('');

    // Permissions
    buffer.writeln('🔐 Permission Status:');
    buffer.writeln(
      '  • Make calls: ${systemInfo.canMakePhoneCalls ? "✅ Can make calls" : "❌ Cannot make calls"}',
    );

    if (systemInfo.hasCallPermission != null) {
      buffer.writeln(
        '  • Call permission: ${systemInfo.hasCallPermission! ? "✅ Granted" : "❌ Denied"}',
      );
    }

    if (systemInfo.hasReadPhoneStatePermission != null) {
      buffer.writeln(
        '  • Phone state permission: ${systemInfo.hasReadPhoneStatePermission! ? "✅ Granted" : "❌ Denied"}',
      );
    }

    final hasAccessibilityPermission =
        systemInfo.additionalInfo['hasAccessibilityPermission'] ?? false;
    buffer.writeln(
      '  • Accessibility service: ${hasAccessibilityPermission ? "✅ Enabled" : "❌ Disabled"}',
    );

    // Limitations
    if (systemInfo.limitations != null && systemInfo.limitations!.isNotEmpty) {
      buffer.writeln('');
      buffer.writeln('⚠️ Limitations:');
      for (final limitation in systemInfo.limitations!) {
        buffer.writeln('  • $limitation');
      }
    }

    // Additional information
    if (systemInfo.additionalInfo.isNotEmpty) {
      buffer.writeln('');
      buffer.writeln('ℹ️ Additional Information:');

      systemInfo.additionalInfo.forEach((key, value) {
        // Skip already shown information
        if (!['simInfoList', 'hasAccessibilityPermission'].contains(key)) {
          buffer.writeln('  • $key: $value');
        }
      });
    }

    buffer.writeln('');
    buffer.writeln('═══════════════════════════════');
    buffer.writeln('⏰ Updated: ${DateTime.now().toString().split('.')[0]}');

    return buffer.toString();
  }

  // ==================== COMPLETE MULTI-SESSION FLOW METHOD ====================

  /// Executes a complete multi-session flow similar to ussd_advanced
  /// This method mimics the behavior of:
  /// 1. UssdAdvanced.multisessionUssd()
  /// 2. UssdAdvanced.sendMessage() (if there's a message)
  /// 3. UssdAdvanced.cancelSession()
  Future<void> _executeMultiSessionFlow() async {
    if (_multiSessionController.text.isEmpty) {
      setState(() {
        _multiSessionResult = 'Please enter a USSD code';
      });
      return;
    }

    setState(() {
      _multiSessionResult = 'Starting complete multi-session flow...\n';
      _isMultiSessionActive = true;
    });

    try {
      // Check accessibility service
      final isServiceEnabled =
          await UssdHandler.isAccessibilityServiceEnabled();
      if (!isServiceEnabled) {
        setState(() {
          _isMultiSessionActive = false;
          _multiSessionResult =
              'ERROR: Accessibility service is not enabled.\n\n'
              'To use multi-session you need:\n'
              '1. Enable the USSD accessibility service\n'
              '2. Grant phone permissions\n\n'
              'Press "Configure Accessibility" to open settings.';
        });
        return;
      }

      // STEP 1: Start multi-USSD session (equivalent to UssdAdvanced.multisessionUssd)
      setState(() {
        _multiSessionResult += '📱 Step 1: Starting multi-USSD session...\n';
      });

      final startResult = await UssdHandler.startMultiSessionUssd(
        _multiSessionController.text,
        subscriptionId: _selectedSubscriptionId,
      );

      if (startResult.success != true) {
        setState(() {
          _isMultiSessionActive = false;
          _multiSessionResult +=
              '❌ Error starting session: ${startResult.error ?? startResult.message}\n';
        });
        return;
      }

      setState(() {
        _multiSessionResult += '✅ Session started successfully\n';
        _multiSessionResult +=
            '📄 Initial response: ${startResult.message}\n\n';
      });

      // Wait a moment for the session to be established
      await Future.delayed(const Duration(seconds: 2));

      // STEP 2: Send message if specified (equivalent to UssdAdvanced.sendMessage)
      if (_multiSessionMessageController.text.isNotEmpty) {
        setState(() {
          _multiSessionResult +=
              '📤 Step 2: Sending message "${_multiSessionMessageController.text}"...\n';
        });

        // Try to send the message with automatic retries
        bool messageSent = false;
        int maxRetries = 5;
        int retryDelay = 2; // seconds

        for (
          int attempt = 1;
          attempt <= maxRetries && !messageSent;
          attempt++
        ) {
          setState(() {
            _multiSessionResult += '   🔄 Attempt $attempt of $maxRetries...\n';
          });

          final sendResult = await UssdHandler.sendMessageInSession(
            _multiSessionMessageController.text,
          );

          if (sendResult.success == true) {
            messageSent = true;
            setState(() {
              _multiSessionResult +=
                  '✅ Message sent successfully on attempt $attempt\n';
              _multiSessionResult += '📄 Response: ${sendResult.message}\n\n';
            });
          } else {
            final errorMsg = sendResult.error ?? sendResult.message;

            if (attempt < maxRetries) {
              setState(() {
                _multiSessionResult +=
                    '   ⏳ Error on attempt $attempt: $errorMsg\n';
                _multiSessionResult +=
                    '   ⏳ Waiting ${retryDelay}s before next attempt...\n';
              });
              await Future.delayed(Duration(seconds: retryDelay));
            } else {
              setState(() {
                _multiSessionResult +=
                    '❌ Final error after $maxRetries attempts: $errorMsg\n\n';
              });
            }
          }
        }

        // Wait a moment before canceling
        await Future.delayed(const Duration(seconds: 2));
      } else {
        setState(() {
          _multiSessionResult += '⏭️ Step 2: Skipped (no message to send)\n\n';
        });

        // If no message, wait a bit longer to see the initial response
        await Future.delayed(const Duration(seconds: 3));
      }

      // STEP 3: Cancel session (equivalent to UssdAdvanced.cancelSession)
      setState(() {
        _multiSessionResult += '🔚 Step 3: Canceling session...\n';
      });

      final cancelResult = await UssdHandler.cancelMultiSession();

      setState(() {
        _isMultiSessionActive = false;
        if (cancelResult.success == true) {
          _multiSessionResult += '✅ Session canceled successfully\n';
          _multiSessionResult += '📄 ${cancelResult.message}\n\n';
          _multiSessionResult +=
              '🎉 MULTI-SESSION FLOW COMPLETED SUCCESSFULLY!';
        } else {
          _multiSessionResult +=
              '⚠️ Error canceling session: ${cancelResult.error ?? cancelResult.message}\n\n';
          _multiSessionResult += '⚠️ Flow completed with warnings.';
        }
      });
    } catch (e) {
      setState(() {
        _isMultiSessionActive = false;
        _multiSessionResult += '\n❌ Error during flow: $e\n\n';
        _multiSessionResult += '💡 Make sure that:\n';
        _multiSessionResult += '• The accessibility service is enabled\n';
        _multiSessionResult += '• You have phone permissions\n';
        _multiSessionResult += '• The USSD code is valid for your carrier';
      });
    }
  }

  // ==================== METHODS FOR MULTI-SESSION ====================

  Future<void> _startMultiSession() async {
    if (_multiSessionController.text.isEmpty) {
      setState(() {
        _multiSessionResult = 'Please enter a USSD code';
      });
      return;
    }

    setState(() {
      _multiSessionResult = 'Starting multi-session...';
    });

    try {
      // Check if accessibility service is enabled
      final isServiceEnabled =
          await UssdHandler.isAccessibilityServiceEnabled();
      if (!isServiceEnabled) {
        setState(() {
          _multiSessionResult =
              'ERROR: Accessibility service is not enabled.\n\n'
              'To use multi-session you need:\n'
              '1. Enable the USSD accessibility service\n'
              '2. Grant phone permissions\n\n'
              'Press "Configure Accessibility" to open settings.';
        });
        return;
      }

      final result = await UssdHandler.startMultiSessionUssd(
        _multiSessionController.text,
        subscriptionId: _selectedSubscriptionId,
      );

      setState(() {
        if (result.success == true) {
          _isMultiSessionActive = true;
          _multiSessionResult =
              'Multi-session started successfully!\n'
              'Code: ${_multiSessionController.text}\n'
              'Message: ${result.message}\n\n'
              'Now you can send messages in the active session.';
        } else {
          _multiSessionResult =
              'Error starting multi-session:\n${result.error ?? result.message}';
        }
      });

      // Check session status after a moment
      Future.delayed(const Duration(seconds: 2), () async {
        _checkMultiSessionStatus();
      });
    } catch (e) {
      setState(() {
        _multiSessionResult = 'Error: $e';
      });
    }
  }

  Future<void> _sendMessageInSession() async {
    if (_multiSessionMessageController.text.isEmpty) {
      setState(() {
        _multiSessionResult = 'Please enter a message to send';
      });
      return;
    }

    setState(() {
      _multiSessionResult = 'Sending message in session...';
    });

    try {
      final result = await UssdHandler.sendMessageInSession(
        _multiSessionMessageController.text,
      );

      setState(() {
        if (result.success == true) {
          _multiSessionResult =
              'Message sent successfully!\n'
              'Message: ${_multiSessionMessageController.text}\n'
              'Response: ${result.message}\n\n'
              'You can continue sending more messages or cancel the session.';
          _multiSessionMessageController.clear();
        } else {
          _multiSessionResult =
              'Error sending message:\n${result.error ?? result.message}';
        }
      });
    } catch (e) {
      setState(() {
        _multiSessionResult = 'Error: $e';
      });
    }
  }

  Future<void> _cancelMultiSession() async {
    setState(() {
      _multiSessionResult = 'Canceling multi-session...';
    });

    try {
      final result = await UssdHandler.cancelMultiSession();

      setState(() {
        _isMultiSessionActive = false;
        if (result.success == true) {
          _multiSessionResult =
              'Multi-session canceled successfully!\n'
              'Message: ${result.message}';
        } else {
          _multiSessionResult =
              'Error canceling session:\n${result.error ?? result.message}';
        }
      });
    } catch (e) {
      setState(() {
        _isMultiSessionActive = false;
        _multiSessionResult = 'Error: $e';
      });
    }
  }

  Future<void> _checkMultiSessionStatus() async {
    try {
      final isActive = await UssdHandler.isMultiSessionActive();
      setState(() {
        _isMultiSessionActive = isActive;
        if (!isActive && _multiSessionResult.contains('successfully started')) {
          _multiSessionResult +=
              '\n\nNOTE: The session was automatically closed.';
        }
      });
    } catch (e) {
      // Ignore errors when checking status
    }
  }

  Future<void> _openAccessibilitySettings() async {
    try {
      final opened = await UssdHandler.openAccessibilitySettings();
      if (opened) {
        setState(() {
          _multiSessionResult =
              'Accessibility configuration opened.\n\n'
              'Steps to enable:\n'
              '1. Look for "USSD Handler" in the list\n'
              '2. Activate the service\n'
              '3. Accept permissions\n'
              '4. Return to the app and try again';
        });
      } else {
        setState(() {
          _multiSessionResult = 'Could not open accessibility configuration';
        });
      }
    } catch (e) {
      setState(() {
        _multiSessionResult = 'Error opening configuration: $e';
      });
    }
  }

  // ==================== BASIC METHODS ====================

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          title: const Text('USSD Handler - Essential Functions'),
          backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        ),
        body: SingleChildScrollView(
          padding: const .all(16.0),
          child: Column(
            children: [
              // ==================== SYSTEM INFORMATION ====================
              Card(
                child: Padding(
                  padding: const .all(16.0),
                  child: Column(
                    crossAxisAlignment: .start,
                    children: [
                      const Text(
                        'System Information',
                        style: TextStyle(fontSize: 18, fontWeight: .bold),
                      ),
                      const SizedBox(height: 16),
                      Text('Platform Version: $_platformVersion'),
                      Text(
                        'USSD Support: ${_isUssdSupported ? "✅ Supported" : "❌ Not supported"}',
                      ),
                      if (_supportsMultiSim) ...[
                        Text('SIMs detected: $_simCount'),
                        Text('Multi-SIM: ✅ Supported'),
                      ] else ...[
                        Text('Multi-SIM: ❌ Not supported or 1 SIM'),
                      ],

                      // ==================== SIM SELECTOR ====================
                      if (_supportsMultiSim && _simInfoList.isNotEmpty) ...[
                        const SizedBox(height: 16),
                        const Text(
                          'Select SIM:',
                          style: TextStyle(fontWeight: .bold),
                        ),
                        const SizedBox(height: 8),
                        DropdownButtonFormField<int?>(
                          decoration: const InputDecoration(
                            border: OutlineInputBorder(),
                            labelText: 'SIM to use',
                          ),
                          initialValue: _selectedSubscriptionId,
                          items: [
                            const DropdownMenuItem<int?>(
                              value: null,
                              child: Text('Automatic (native selector)'),
                            ),
                            ..._simInfoList.map((simInfo) {
                              final slotIndex = simInfo['slotIndex'] as int;
                              final displayName =
                                  simInfo['displayName'] as String;
                              final carrierName =
                                  simInfo['carrierName'] as String;
                              return DropdownMenuItem<int?>(
                                value: slotIndex,
                                child: Text(
                                  'SIM $slotIndex: $displayName ($carrierName)',
                                ),
                              );
                            }),
                          ],
                          onChanged: (value) {
                            setState(() {
                              _selectedSubscriptionId = value;
                            });
                          },
                        ),
                      ] else if (_supportsMultiSim) ...[
                        const SizedBox(height: 16),
                        DropdownButtonFormField<int?>(
                          decoration: const InputDecoration(
                            border: OutlineInputBorder(),
                            labelText: 'SIM to use',
                          ),
                          initialValue: _selectedSubscriptionId,
                          items: [
                            const DropdownMenuItem<int?>(
                              value: null,
                              child: Text('Automatic (native selector)'),
                            ),
                            for (int i = 1; i <= _simCount; i++)
                              DropdownMenuItem<int?>(
                                value: i,
                                child: Text('SIM $i'),
                              ),
                          ],
                          onChanged: (value) {
                            setState(() {
                              _selectedSubscriptionId = value;
                            });
                          },
                        ),
                      ],
                      const SizedBox(height: 16),
                      SizedBox(
                        width: .infinity,
                        child: ElevatedButton(
                          onPressed: _getSystemInfo,
                          child: const Text('Get Detailed Information'),
                        ),
                      ),
                      if (_systemInfo.isNotEmpty) ...[
                        const SizedBox(height: 16),
                        Container(
                          padding: const .all(12),
                          decoration: BoxDecoration(
                            color: Colors.grey[100],
                            borderRadius: .circular(8),
                            border: .all(color: Colors.grey[300]!),
                          ),
                          child: SingleChildScrollView(
                            child: Text(
                              _systemInfo,
                              style: const TextStyle(
                                fontFamily: 'monospace',
                                fontSize: 12,
                              ),
                            ),
                          ),
                        ),
                      ],
                    ],
                  ),
                ),
              ),

              const SizedBox(height: 16),

              // ==================== BASIC USSD ====================
              Card(
                child: Padding(
                  padding: const .all(16.0),
                  child: Column(
                    crossAxisAlignment: .start,
                    children: [
                      const Text(
                        'Basic USSD',
                        style: TextStyle(fontSize: 18, fontWeight: .bold),
                      ),
                      const SizedBox(height: 8),
                      Container(
                        padding: const .all(8),
                        decoration: BoxDecoration(
                          color: Colors.blue[50],
                          borderRadius: .circular(6),
                          border: .all(color: Colors.blue[200]!),
                        ),
                        child: const Text(
                          '📱 STANDARD METHOD: Uses the native Android USSD functionality. '
                          'Opens the system dialog and automatically handles the response.',
                          style: TextStyle(fontSize: 12, color: Colors.blue),
                        ),
                      ),
                      const SizedBox(height: 16),
                      TextField(
                        controller: _ussdController,
                        decoration: const InputDecoration(
                          labelText: 'USSD Code',
                          hintText: 'Ex: *#06# (for IMEI)',
                          border: OutlineInputBorder(),
                          prefixIcon: Icon(Icons.phone),
                        ),
                      ),
                      const SizedBox(height: 16),
                      SizedBox(
                        width: .infinity,
                        child: ElevatedButton(
                          onPressed: _executeUssd,
                          style: ElevatedButton.styleFrom(
                            backgroundColor: Colors.blue,
                            foregroundColor: Colors.white,
                          ),
                          child: const Text('📱 Execute USSD'),
                        ),
                      ),
                      if (_ussdResult.isNotEmpty) ...[
                        const SizedBox(height: 16),
                        Container(
                          padding: const .all(12),
                          decoration: BoxDecoration(
                            color: _ussdResult.contains('Success')
                                ? Colors.green[50]
                                : Colors.red[50],
                            borderRadius: .circular(8),
                            border: .all(
                              color: _ussdResult.contains('Success')
                                  ? Colors.green[200]!
                                  : Colors.red[200]!,
                            ),
                          ),
                          child: Column(
                            crossAxisAlignment: .start,
                            children: [
                              const Text(
                                'Basic USSD Result:',
                                style: TextStyle(fontWeight: .bold),
                              ),
                              const SizedBox(height: 8),
                              Text(_ussdResult),
                            ],
                          ),
                        ),
                      ],
                    ],
                  ),
                ),
              ),

              const SizedBox(height: 16),

              // ==================== DIRECT USSD ====================
              Card(
                child: Padding(
                  padding: const .all(16.0),
                  child: Column(
                    crossAxisAlignment: .start,
                    children: [
                      const Text(
                        'Direct USSD (No Dialog)',
                        style: TextStyle(fontSize: 18, fontWeight: .bold),
                      ),
                      const SizedBox(height: 8),
                      Container(
                        padding: const .all(8),
                        decoration: BoxDecoration(
                          color: Colors.orange[50],
                          borderRadius: .circular(6),
                          border: .all(color: Colors.orange[200]!),
                        ),
                        child: const Text(
                          '🚀 DIRECT METHOD: Executes USSD codes without showing the native dialog. '
                          'Requires special permissions (CALL_PHONE). Compatible with Android 8.0+.',
                          style: TextStyle(fontSize: 12, color: Colors.orange),
                        ),
                      ),
                      const SizedBox(height: 16),
                      TextField(
                        controller: _ussdDirectController,
                        decoration: const InputDecoration(
                          labelText: 'Direct USSD Code',
                          hintText: 'Ex: *#06# (IMEI without dialog)',
                          border: OutlineInputBorder(),
                          prefixIcon: Icon(Icons.flash_on),
                        ),
                      ),
                      const SizedBox(height: 16),
                      SizedBox(
                        width: .infinity,
                        child: ElevatedButton(
                          onPressed: _executeUssdDirect,
                          style: ElevatedButton.styleFrom(
                            backgroundColor: Colors.orange,
                            foregroundColor: Colors.white,
                          ),
                          child: const Text('🚀 Direct USSD'),
                        ),
                      ),
                      if (_ussdDirectResult.isNotEmpty) ...[
                        const SizedBox(height: 16),
                        Container(
                          padding: const .all(12),
                          decoration: BoxDecoration(
                            color: _ussdDirectResult.contains('Direct response')
                                ? Colors.green[50]
                                : Colors.red[50],
                            borderRadius: .circular(8),
                            border: .all(
                              color:
                                  _ussdDirectResult.contains('Direct response')
                                  ? Colors.green[200]!
                                  : Colors.red[200]!,
                            ),
                          ),
                          child: Column(
                            crossAxisAlignment: .start,
                            children: [
                              const Text(
                                'Direct USSD Result:',
                                style: TextStyle(fontWeight: .bold),
                              ),
                              const SizedBox(height: 8),
                              Text(_ussdDirectResult),
                            ],
                          ),
                        ),
                      ],
                    ],
                  ),
                ),
              ),

              const SizedBox(height: 16),

              // ==================== MULTI-USSD SESSIONS ====================
              Card(
                child: Padding(
                  padding: const .all(16.0),
                  child: Column(
                    crossAxisAlignment: .start,
                    children: [
                      const Text(
                        'Multi-USSD Sessions',
                        style: TextStyle(fontSize: 18, fontWeight: .bold),
                      ),
                      const SizedBox(height: 8),
                      Container(
                        padding: const .all(8),
                        decoration: BoxDecoration(
                          color: Colors.green[50],
                          borderRadius: .circular(6),
                          border: .all(color: Colors.green[200]!),
                        ),
                        child: const Text(
                          '🌟 MULTI-USSD: Handles multiple USSD sessions simultaneously. '
                          'Ideal for services that require continuous interaction.',
                          style: TextStyle(fontSize: 12, color: Colors.green),
                        ),
                      ),
                      const SizedBox(height: 16),
                      TextField(
                        controller: _multiSessionController,
                        decoration: const InputDecoration(
                          labelText: 'Multi-Session USSD Code',
                          hintText: 'Ex: *123# (balance and more)',
                          border: OutlineInputBorder(),
                          prefixIcon: Icon(Icons.group),
                        ),
                      ),
                      const SizedBox(height: 8),
                      TextField(
                        controller: _multiSessionMessageController,
                        decoration: const InputDecoration(
                          labelText: 'Message to Send (optional)',
                          hintText: 'Ex: 1 (for option 1)',
                          border: OutlineInputBorder(),
                          prefixIcon: Icon(Icons.message),
                        ),
                      ),
                      const SizedBox(height: 16),

                      // Main multi-session button (similar to ussd_advanced)
                      SizedBox(
                        width: .infinity,
                        child: ElevatedButton(
                          onPressed: _isMultiSessionActive
                              ? null
                              : () async {
                                  await _executeMultiSessionFlow();
                                },
                          style: ElevatedButton.styleFrom(
                            backgroundColor: Colors.green,
                            foregroundColor: Colors.white,
                            padding: const .symmetric(vertical: 16),
                          ),
                          child: Text(
                            _isMultiSessionActive
                                ? '🟢 Session in Progress...'
                                : '🚀 Execute Complete\nMulti-Session',
                            textAlign: .center,
                            style: const TextStyle(fontSize: 16),
                          ),
                        ),
                      ),

                      const SizedBox(height: 12),

                      // Individual buttons for manual control
                      const Text(
                        'Manual Control (Advanced):',
                        style: TextStyle(fontWeight: .bold, fontSize: 14),
                      ),
                      const SizedBox(height: 8),
                      Row(
                        children: [
                          Expanded(
                            child: ElevatedButton(
                              onPressed: _isMultiSessionActive
                                  ? null
                                  : _startMultiSession,
                              style: ElevatedButton.styleFrom(
                                backgroundColor: Colors.blue,
                                foregroundColor: Colors.white,
                              ),
                              child: const Text('▶️ Start'),
                            ),
                          ),
                          const SizedBox(width: 8),
                          Expanded(
                            child: ElevatedButton(
                              onPressed: _isMultiSessionActive
                                  ? _sendMessageInSession
                                  : null,
                              style: ElevatedButton.styleFrom(
                                backgroundColor: Colors.orange,
                                foregroundColor: Colors.white,
                              ),
                              child: const Text('📤 Send'),
                            ),
                          ),
                          const SizedBox(width: 8),
                          Expanded(
                            child: ElevatedButton(
                              onPressed: _isMultiSessionActive
                                  ? _cancelMultiSession
                                  : null,
                              style: ElevatedButton.styleFrom(
                                backgroundColor: Colors.red,
                                foregroundColor: Colors.white,
                              ),
                              child: const Text('❌ Cancel'),
                            ),
                          ),
                        ],
                      ),

                      const SizedBox(height: 8),
                      SizedBox(
                        width: .infinity,
                        child: ElevatedButton(
                          onPressed: _openAccessibilitySettings,
                          style: ElevatedButton.styleFrom(
                            backgroundColor: Colors.orange,
                            foregroundColor: Colors.white,
                          ),
                          child: const Text('⚙️ Configure Accessibility'),
                        ),
                      ),

                      if (_multiSessionResult.isNotEmpty) ...[
                        const SizedBox(height: 16),
                        Container(
                          padding: const .all(12),
                          decoration: BoxDecoration(
                            color:
                                _multiSessionResult.contains('successfully') ||
                                    _multiSessionResult.contains('Success')
                                ? Colors.green[50]
                                : _multiSessionResult.contains('ERROR') ||
                                      _multiSessionResult.contains('Error')
                                ? Colors.red[50]
                                : Colors.blue[50],
                            borderRadius: .circular(8),
                            border: .all(
                              color:
                                  _multiSessionResult.contains(
                                        'successfully',
                                      ) ||
                                      _multiSessionResult.contains('Success')
                                  ? Colors.green[200]!
                                  : _multiSessionResult.contains('ERROR') ||
                                        _multiSessionResult.contains('Error')
                                  ? Colors.red[200]!
                                  : Colors.blue[200]!,
                            ),
                          ),
                          child: Column(
                            crossAxisAlignment: .start,
                            children: [
                              Text(
                                _isMultiSessionActive
                                    ? 'Multi-USSD Session Status:'
                                    : 'Multi-Session Result:',
                                style: const TextStyle(fontWeight: .bold),
                              ),
                              const SizedBox(height: 8),
                              Text(_multiSessionResult),
                            ],
                          ),
                        ),
                      ],
                    ],
                  ),
                ),
              ),

              const SizedBox(height: 16),

              // ==================== RECOMMENDED CODES ====================
              const Card(
                child: Padding(
                  padding: .all(16.0),
                  child: Column(
                    crossAxisAlignment: .start,
                    children: [
                      Text(
                        'Recommended USSD Codes for Testing',
                        style: TextStyle(fontSize: 18, fontWeight: .bold),
                      ),
                      SizedBox(height: 8),
                      Text(
                        '🔥 UNIVERSAL CODES (work on all devices):',
                        style: TextStyle(
                          fontWeight: .bold,
                          color: Colors.green,
                        ),
                      ),
                      Text('• *#06# - Device IMEI (ALWAYS works)'),
                      Text('• *#*#4636#*#* - Phone information'),
                      SizedBox(height: 8),
                      Text(
                        '📱 CODES BY CARRIER (may vary):',
                        style: TextStyle(
                          fontWeight: .bold,
                          color: Colors.orange,
                        ),
                      ),
                      Text('Mexico:'),
                      Text('  • Telcel: *133# (balance), *264# (recharge)'),
                      Text('  • Movistar: *123# (balance), *611# (service)'),
                      Text('  • AT&T: *123# (balance)'),
                      SizedBox(height: 4),
                      Text('Colombia:'),
                      Text('  • Claro: *444# (balance), *611# (service)'),
                      Text('  • Movistar: *321# (balance)'),
                      Text('  • Tigo: *611# (information)'),
                      SizedBox(height: 4),
                      Text('Spain:'),
                      Text('  • Movistar: *133# (balance), *22123# (data)'),
                      Text('  • Vodafone: *111# (balance)'),
                      Text('  • Orange: *111# (balance)'),
                      SizedBox(height: 8),
                      Text(
                        '🚨 DO YOU GET ERROR CODE -1?',
                        style: TextStyle(fontWeight: .bold, color: Colors.red),
                      ),
                      Text(
                        'This error means that your carrier does NOT support the code you tried. '
                        'This is NORMAL and does NOT indicate a problem with the plugin.',
                        style: TextStyle(fontSize: 12, fontStyle: .italic),
                      ),
                      SizedBox(height: 4),
                      Text(
                        '✅ QUICK SOLUTION:',
                        style: TextStyle(fontWeight: .bold, color: Colors.blue),
                      ),
                      Text('1. Try with *#06# (IMEI) - always works'),
                      Text('2. Look for specific codes from your carrier'),
                      Text('3. Contact your carrier for valid codes'),
                      SizedBox(height: 8),
                      Text(
                        '⚠️ IMPORTANT NOTE:',
                        style: TextStyle(fontWeight: .bold, color: Colors.red),
                      ),
                      Text(
                        'If a USSD code fails, it does NOT mean the plugin is broken. '
                        'It means your carrier does not support that specific code. '
                        'Each carrier has different codes and some services '
                        'require prior activation.',
                        style: TextStyle(fontSize: 12),
                      ),
                      SizedBox(height: 8),
                      Text(
                        '💡 TIPS:',
                        style: TextStyle(fontWeight: .bold, color: Colors.blue),
                      ),
                      Text('• Contact your carrier for valid codes'),
                      Text('• Dial the code manually first to verify'),
                      Text('• Codes may change over time'),
                    ],
                  ),
                ),
              ),

              const SizedBox(height: 16),
            ],
          ),
        ),
      ),
    );
  }

  @override
  void dispose() {
    _ussdController.dispose();
    _ussdDirectController.dispose();
    _ussdSessionController.dispose();
    _multiSessionController.dispose();
    _multiSessionMessageController.dispose();
    super.dispose();
  }
}
7
likes
0
points
292
downloads

Publisher

unverified uploader

Weekly Downloads

A complete Flutter plugin for handling USSD codes on Android and iOS with advanced features including multi-USSD sessions.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on ussd_handler

Packages that implement ussd_handler