Flutter USSD指令发射插件ussd_launcher的使用

发布于 1周前 作者 wuwangju 来自 Flutter

Flutter USSD指令发射插件 ussd_launcher 的使用

ussd_launcher 是一个用于在 Android 设备上直接从应用程序中启动 USSD 请求并管理多步 USSD 会话的 Flutter 插件。以下是该插件的详细使用说明。

特性

  • 发射简单的 USSD 请求
  • 管理多步 USSD 会话
  • 检查和请求必要的辅助功能权限
  • 兼容 API 级别 26 及以上的 Android 设备
  • 优雅地处理 USSD 响应和错误
  • 如果服务未启用,则打开辅助功能设置
  • 获取 SIM 卡信息

安装

pubspec.yaml 文件中添加 ussd_launcher 作为依赖项:

dependencies:
  ussd_launcher: ^latest_version

请确保将 ^latest_version 替换为实际可用的最新版本号。

配置

Android

你需要在 Android 清单文件中添加以下权限:

<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>

多会话 USSD 支持

在 Android 清单文件中添加 USSD 对话辅助功能服务:

<application>
    ...
    <service
        android:name="com.kavina.ussd_launcher.UssdAccessibilityService"
        android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
        android:exported="false">
        <intent-filter>
            <action android:name="android.accessibilityservice.AccessibilityService" />
        </intent-filter>
        <meta-data
            android:name="android.accessibilityservice"
            android:resource="@xml/accessibility_service_config" />
    </service>
</application>

使用方法

示例代码

以下是一个完整的示例应用,展示了如何使用 ussd_launcher 插件进行单步和多步 USSD 请求。

import 'package:flutter/material.dart';
import 'package:ussd_launcher/ussd_launcher.dart';
import 'package:permission_handler/permission_handler.dart';

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

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

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: DefaultTabController(
        length: 2,
        child: Scaffold(
          appBar: AppBar(
            title: const Text('USSD Launcher Demo'),
            bottom: const TabBar(
              tabs: [
                Tab(text: 'Single Session'),
                Tab(text: 'Multi Session'),
              ],
            ),
          ),
          body: const TabBarView(
            children: [
              SingleSessionTab(),
              MultiSessionTab(),
            ],
          ),
        ),
      ),
    );
  }
}

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

  [@override](/user/override)
  _SingleSessionTabState createState() => _SingleSessionTabState();
}

class _SingleSessionTabState extends State<SingleSessionTab> {
  final TextEditingController _controller = TextEditingController();
  String _ussdResponse = '';
  List<Map<String, dynamic>> _simCards = [];
  int? _selectedSimId;

  [@override](/user/override)
  void initState() {
    super.initState();
    _loadSimCards();
  }

  Future<void> _loadSimCards() async {
    var status = await Permission.phone.request();
    if (status.isGranted) {
      try {
        final simCards = await UssdLauncher.getSimCards();
        setState(() {
          _simCards = simCards;
          if (simCards.isNotEmpty) {
            _selectedSimId = simCards[0]['subscriptionId'] as int?;
          }
        });
      } catch (e) {
        print("Erreur lors du chargement des cartes SIM: $e");
      }
    } else {
      print("Permission téléphone non accordée");
    }
  }

  Future<void> _sendUssdRequest() async {
    setState(() {
      _ussdResponse = 'Envoi de la requête USSD...';
    });

    try {
      String? response = await UssdLauncher.sendUssdRequest(
        ussdCode: _controller.text,
        subscriptionId: _selectedSimId ?? -1,
      );
      setState(() {
        _ussdResponse = response ?? 'Aucune réponse reçue';
      });
    } catch (e) {
      setState(() {
        _ussdResponse = 'Erreur: ${e.toString()}';
      });
    }
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        children: [
          DropdownButton<int>(
            value: _selectedSimId,
            hint: const Text('Sélectionner SIM'),
            items: _simCards.map((sim) {
              return DropdownMenuItem<int>(
                value: sim['subscriptionId'],
                child: Text("${sim['displayName']} (${sim['carrierName']})"),
              );
            }).toList(),
            onChanged: (value) {
              setState(() {
                _selectedSimId = value;
              });
            },
          ),
          const SizedBox(height: 16),
          TextField(
            controller: _controller,
            decoration: const InputDecoration(labelText: 'Entrer le code USSD'),
          ),
          const SizedBox(height: 16),
          ElevatedButton(
            onPressed: _sendUssdRequest,
            child: const Text('Envoyer USSD'),
          ),
          const SizedBox(height: 16),
          const Text('Réponse USSD&nbsp;:'),
          Text(
            _ussdResponse,
            style: const TextStyle(color: Colors.blue, fontWeight: FontWeight.bold),
          ),
        ],
      ),
    );
  }

  [@override](/user/override)
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

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

  [@override](/user/override)
  _MultiSessionTabState createState() => _MultiSessionTabState();
}

class _MultiSessionTabState extends State<MultiSessionTab> {
  final TextEditingController _ussdController = TextEditingController();
  final List<TextEditingController> _optionControllers = [];
  List<String> _ussdMessages = []; // Liste pour stocker les messages USSD
  bool _isLoading = false;
  List<Map<String, dynamic>> _simCards = [];
  int? _selectedSlotIndex;

  String _sessionStatus = '';

  [@override](/user/override)
  void initState() {
    super.initState();
    _loadSimCards();

    // Configurer le listener pour les messages USSD
    UssdLauncher.setUssdMessageListener(_onUssdMessageReceived);
  }

  /// Méthode appelée lorsque un message USSD est reçu.
  void _onUssdMessageReceived(String message) {
    print("Message USSD reçu: $message"); // Journalisation
    setState(() {
      // Ne garder que le dernier message
      _ussdMessages = [message];
      if (message.contains("completed") || message.contains("cancelled")) {
        _sessionStatus = "Session USSD terminée.";
      }
    });
  }

  Future<void> _loadSimCards() async {
    var statut = await Permission.phone.request();
    if (statut.isGranted) {
      final simCards = await UssdLauncher.getSimCards();
      setState(() {
        _simCards = simCards;
        if (simCards.isNotEmpty) {
          _selectedSlotIndex = simCards[0]['slotIndex'];
        }
      });
    } else {
      print("Permission téléphone non accordée");
    }
  }

  void _launchMultiSessionUssd() async {
    setState(() {
      _isLoading = true;
      _ussdMessages.clear(); // Réinitialiser la liste au début d'une nouvelle session
      _sessionStatus = '';
    });

    try {
      List<String> options = _optionControllers.map((controller) => controller.text).toList();

      await UssdLauncher.multisessionUssd(
        code: _ussdController.text,
        slotIndex: (_selectedSlotIndex ?? 0),
        options: options,
      );
      // Aucun besoin de gérer 'res1' ici, les messages sont reçus via le listener
    } catch (e) {
      _updateUssdMessages('\nErreur&nbsp;: ${e.toString()}');
    } finally {
      setState(() {
        _isLoading = false;
      });
    }
  }

  void _updateUssdMessages(String newText) {
    setState(() {
      _ussdMessages.add(newText);
    });
  }

  void _addOptionField() {
    setState(() {
      _optionControllers.add(TextEditingController());
    });
  }

  void _removeOptionField() {
    setState(() {
      if (_optionControllers.isNotEmpty) {
        _optionControllers.removeLast().dispose();
      }
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: SingleChildScrollView(
        child: Column(
          children: [
            // Affichage en temps réel de slotIndex
            Text(
              'Slot SIM sélectionné&nbsp;: ${_selectedSlotIndex ?? "Aucun"}',
              style: const TextStyle(fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 16),
            DropdownButton<int>(
              value: _selectedSlotIndex,
              hint: const Text('Sélectionner SIM'),
              items: _simCards.map((sim) {
                return DropdownMenuItem<int>(
                  value: sim['slotIndex'],
                  child: Text("${sim['displayName']} (${sim['carrierName']})"),
                );
              }).toList(),
              onChanged: (value) {
                setState(() {
                  _selectedSlotIndex = value;
                });
              },
            ),
            const SizedBox(height: 16),
            TextField(
              controller: _ussdController,
              decoration: const InputDecoration(labelText: 'Entrer le code USSD'),
            ),
            ..._optionControllers.asMap().entries.map((entry) {
              return Padding(
                padding: const EdgeInsets.only(top: 8.0),
                child: TextField(
                  controller: entry.value,
                  keyboardType: TextInputType.number,
                  decoration: InputDecoration(labelText: 'Option ${entry.key + 1}'),
                ),
              );
            }),
            const SizedBox(height: 16),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                ElevatedButton(
                  onPressed: _addOptionField,
                  child: const Text('Ajouter Option'),
                ),
                ElevatedButton(
                  onPressed: _optionControllers.isNotEmpty ? _removeOptionField : null,
                  child: const Text('Retirer Option'),
                ),
              ],
            ),
            const SizedBox(height: 16),
            ElevatedButton(
              onPressed: _isLoading ? null : _launchMultiSessionUssd,
              child: const Text('Lancer USSD Multi-Session'),
            ),
            const SizedBox(height: 16),
            const Text('Réponses USSD&nbsp;:'),
            Container(
              height: 200,
              decoration: BoxDecoration(
                border: Border.all(color: Colors.blueAccent),
                borderRadius: BorderRadius.circular(8.0),
              ),
              padding: const EdgeInsets.all(8.0),
              child: ListView.builder(
                itemCount: _ussdMessages.length,
                itemBuilder: (context, index) {
                  final isLastMessage = index == _ussdMessages.length - 1;
                  return Text(
                    _ussdMessages[index],
                    style: TextStyle(
                      color: isLastMessage ? Colors.red : Colors.blue,
                      fontWeight: isLastMessage ? FontWeight.bold : FontWeight.normal,
                      fontSize: isLastMessage ? 16 : 14,
                    ),
                  );
                },
              ),
            ),
            const SizedBox(height: 16),
            if (_sessionStatus.isNotEmpty)
              Text(
                _sessionStatus,
                style: const TextStyle(color: Colors.green, fontWeight: FontWeight.bold),
              ),
          ],
        ),
      ),
    );
  }

  [@override](/user/override)
  void dispose() {
    // Nettoyer les contrôleurs de texte
    for (var controller in _optionControllers) {
      controller.dispose();
    }
    _ussdController.dispose();
    super.dispose();
  }
}

更多关于Flutter USSD指令发射插件ussd_launcher的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter USSD指令发射插件ussd_launcher的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter项目中使用ussd_launcher插件来发射USSD指令的示例代码。这个插件允许你通过Flutter应用发送USSD请求,并接收响应。

首先,确保你已经在pubspec.yaml文件中添加了ussd_launcher依赖:

dependencies:
  flutter:
    sdk: flutter
  ussd_launcher: ^最新版本号  # 请替换为当前最新版本号

然后,运行flutter pub get来获取依赖。

接下来,在你的Flutter项目中,你可以按照以下步骤使用ussd_launcher插件:

1. 导入插件

在你的Dart文件中导入ussd_launcher

import 'package:ussd_launcher/ussd_launcher.dart';

2. 请求USSD代码

你可以使用UssdLauncher().launch方法来发送USSD请求。下面是一个完整的示例,展示如何在一个按钮点击事件中发送USSD请求并处理响应:

import 'package:flutter/material.dart';
import 'package:ussd_launcher/ussd_launcher.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'USSD Launcher Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String _ussdResponse = "";

  void _launchUssdCode() async {
    String code = "*100#";  // 替换为你想要发送的USSD代码
    try {
      String result = await UssdLauncher().launch(code);
      setState(() {
        _ussdResponse = result;
      });
    } catch (e) {
      setState(() {
        _ussdResponse = "Error: ${e.message}";
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('USSD Launcher Demo'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'USSD Response:',
              style: TextStyle(fontSize: 20),
            ),
            SizedBox(height: 10),
            Text(
              _ussdResponse,
              style: TextStyle(fontSize: 18),
              textAlign: TextAlign.center,
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: _launchUssdCode,
              child: Text('Launch USSD Code'),
            ),
          ],
        ),
      ),
    );
  }
}

3. 运行应用

将上述代码添加到你的Flutter项目中,然后运行应用。点击按钮后,应用将尝试发送指定的USSD代码,并在界面上显示响应结果。

注意事项

  • 并非所有设备和运营商都支持通过编程方式发送USSD请求。在某些设备上,可能需要用户手动确认权限。
  • 在实际开发中,处理异常和错误响应是非常重要的,以确保用户有良好的体验。
  • 请确保你遵守所有适用的隐私政策和法律要求,尤其是在处理用户信息和通信数据时。

希望这个示例代码能帮助你理解如何在Flutter项目中使用ussd_launcher插件来发射USSD指令。

回到顶部