Flutter VoIP通信插件flutter_voip_kit的使用

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

Flutter VoIP通信插件 flutter_voip_kit 的使用

flutter_voip_kit 是一个用于在Flutter应用中实现VoIP(Voice over Internet Protocol)通信的插件。它利用iOS的CallKit和Android的Telecom库来创建和接收具有原生功能的呼叫,例如在用户的锁屏界面上弹出呼叫通知。

开始使用

添加依赖

首先,在你的pubspec.yaml文件中添加flutter_voip_kit作为依赖项:

dependencies:
  flutter_voip_kit: ^latest_version

记得替换^latest_version为最新的版本号。

设置

iOS设置

  1. 在Xcode中为项目添加VoIP后台模式:
    • 打开Xcode项目。
    • 选择你的项目目标。
    • 转到“Signing & Capabilities”选项卡。
    • 点击左上角的“+ Capability”按钮。
    • 添加“Background Modes”,然后勾选“Voice over IP”。

Android设置

  1. AndroidManifest.xml中添加权限:

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.CALL_PHONE" />
    
  2. 添加服务声明:

    <service android:name="com.example.flutter_voip_kit.VoipConnectionService"
      android:label="VoipConnectionService"
      android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
        <intent-filter>
            <action android:name="android.telecom.ConnectionService" />
        </intent-filter>
    </service>
    

API 使用

初始化插件

初始化flutter_voip_kit插件,并传入一个处理所有VOIP事件的回调函数:

FlutterVoipKit.init(callStateChangeHandler: callStateChangeHandler);

示例回调函数

下面是一个处理不同呼叫状态的示例回调函数:

Future<bool> myCallStateChangeHandler(Call call) async {
  dev.log("call state changed listener: $call");

  switch (call.callState) {
    case CallState.connecting:
      await Future.delayed(const Duration(seconds: 3));
      return true;
    case CallState.active:
      return true;
    case CallState.ended:
      return true;
    case CallState.failed:
      return true;
    case CallState.held:
      return true;
    default:
      return false;
  }
}

监听活跃呼叫列表

监听活跃呼叫列表的变化,并更新UI:

FlutterVoipKit.callListStream.listen((allCalls) {
  setState(() {
    calls = allCalls;
  });
});

报告来电

报告设备收到一个新的来电:

FlutterVoipKit.reportIncomingCall(handle: "1234567890", uuid: Uuid().v4());

启动呼叫

启动一个新的外呼:

FlutterVoipKit.startCall("1234567890");

结束呼叫

结束当前呼叫:

call.end();

持有/恢复呼叫

将呼叫置于保持状态或从中恢复:

call.hold(onHold: !(call.callState == CallState.held));

完整示例Demo

以下是完整的示例代码,展示了如何集成flutter_voip_kit插件并实现基本的VoIP功能:

import 'dart:developer' as dev;
import 'dart:math';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter_voip_kit/call.dart';
import 'package:flutter_voip_kit/flutter_voip_kit.dart';
import 'package:uuid/uuid.dart';

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

class MyApp extends StatefulWidget {
  [@override](/user/override)
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  [@override](/user/override)
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  List<Call> calls = [];
  bool hasPermission = false;

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

    FlutterVoipKit.init(callStateChangeHandler: callStateChangeHandler);

    checkPermissionsUntilGranted();

    FlutterVoipKit.callListStream.listen((allCalls) {
      setState(() {
        calls = allCalls;
      });
    });
  }

  Future<bool> callStateChangeHandler(Call call) async {
    dev.log("call state changed listener: $call");
    setState(() {});

    switch (call.callState) {
      case CallState.connecting:
        dev.log("---------------> Call connecting");
        await Future.delayed(const Duration(seconds: 3));
        return true;
      case CallState.active:
        dev.log("---------> Call active");
        return true;
      case CallState.ended:
        dev.log("---------> Call ended");
        await Future.delayed(const Duration(seconds: 1));
        return true;
      case CallState.failed:
        dev.log("---------> Call failed");
        return true;
      case CallState.held:
        dev.log("---------> Call held");
        return true;
      default:
        return false;
    }
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter Voip Kit Example'),
      ),
      body: !hasPermission
          ? Center(
              child: ElevatedButton(
                child: Text("Grant Phone Permissions"),
                onPressed: () {
                  FlutterVoipKit.checkPermissions(openSettings: true)
                      .then((value) => setState(() {
                            hasPermission = value;
                          }));
                },
              ),
            )
          : SafeArea(
              child: Column(
                children: [
                  ElevatedButton(
                    child: Text("Simulate incoming call"),
                    onPressed: () {
                      Future.delayed(const Duration(seconds: 2)).then((value) {
                        FlutterVoipKit.reportIncomingCall(
                            handle: "${Random().nextInt(10)}" * 9,
                            uuid: Uuid().v4());
                      });
                    },
                  ),
                  ElevatedButton(
                    child: Text("Start outgoing call"),
                    onPressed: () {
                      FlutterVoipKit.startCall(
                        "${Random().nextInt(10)}" * 9,
                      );
                    },
                  ),
                  Expanded(
                    child: ListView.builder(
                      itemBuilder: (context, index) {
                        final call = calls[index];
                        return Container(
                          color: call.callState == CallState.active
                              ? Colors.green[300]
                              : call.callState == CallState.held
                                  ? Colors.yellow[800]
                                  : (call.callState == CallState.connecting
                                      ? Colors.yellow[200]
                                      : Colors.red),
                          padding: EdgeInsets.all(16.0),
                          child: Row(
                            crossAxisAlignment: CrossAxisAlignment.center,
                            children: [
                              Expanded(
                                child: Text("Number: ${call.address}"),
                              ),
                              if (call.callState == CallState.connecting)
                                CircularProgressIndicator(),
                              if (call.callState != CallState.connecting &&
                                  call.callState != CallState.incoming)
                                ElevatedButton(
                                  onPressed: () {
                                    call.hold(
                                        onHold: !(call.callState ==
                                            CallState.held));
                                  },
                                  child: Text(call.callState == CallState.held
                                      ? "Resume"
                                      : "Hold"),
                                ),
                              if (call.callState == CallState.active) ...[
                                IconButton(
                                  icon: Icon(
                                    Icons.phone_disabled_sharp,
                                    size: 30,
                                    color: Colors.red,
                                  ),
                                  onPressed: () {
                                    call.end();
                                  },
                                ),
                                IconButton(
                                    icon: Icon(
                                      call.muted ? Icons.mic : Icons.mic_off,
                                      size: 30,
                                    ),
                                    onPressed: () {
                                      call.mute(muted: !call.muted);
                                    })
                              ]
                            ],
                          ),
                        );
                      },
                      itemCount: calls.length,
                    ),
                  )
                ],
              ),
            ),
    );
  }

  void checkPermissionsUntilGranted() {
    Future.delayed(const Duration(milliseconds: 100)).then((value) async {
      hasPermission =
          await FlutterVoipKit.checkPermissions(openSettings: false);
      bool first = true;
      while (!hasPermission) {
        await showDialog(
          context: context,
          builder: (BuildContext context) {
            return AlertDialog(
              title: Text("No Permissions"),
              content: Text(
                  "You need to allow this app to use your phone and add it to the phone list in settings."),
              actions: <Widget>[
                ElevatedButton(
                  onPressed: () {
                    Navigator.pop(context);
                  },
                  child: Text("Ok"),
                )
              ],
            );
          },
        );

        hasPermission =
            await FlutterVoipKit.checkPermissions(openSettings: !first);
        first = false;
        if (!hasPermission) {
          await Future.delayed(const Duration(seconds: 1));
        }
      }
      setState(() {});
    });
  }
}

更多关于Flutter VoIP通信插件flutter_voip_kit的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter VoIP通信插件flutter_voip_kit的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在使用Flutter进行VoIP通信时,flutter_voip_kit 是一个强大的插件,它允许开发者在Flutter应用中集成VoIP功能。以下是一个基本的代码案例,展示如何设置和使用 flutter_voip_kit 进行VoIP通信。

1. 安装依赖

首先,你需要在你的Flutter项目中添加 flutter_voip_kit 依赖。在你的 pubspec.yaml 文件中添加以下依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_voip_kit: ^x.y.z  # 请替换为最新版本号

然后运行 flutter pub get 来安装依赖。

2. 配置iOS和Android

iOS配置

  • ios/Runner/Info.plist 中添加必要的权限,例如麦克风和通知权限。
  • ios/Runner/AppDelegate.swift 中配置VoIP推送:
import UIKit
import Flutter
import UserNotifications
import CallKit

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)
    
    // 配置VoIP推送
    UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in
      if granted {
        DispatchQueue.main.async {
          application.registerForRemoteNotifications()
        }
      }
    }
    
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
  
  override func application(_ application: UIApplication,
                            didReceiveRemoteNotification userInfo: [AnyHashable : Any],
                            fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    if let message = userInfo[AnyHashable("google.c.a.c_l")] as? String {
      // 处理VoIP推送消息
      // 通常这里会启动CallKit界面
      // 示例代码略,因为涉及复杂的CallKit集成
    }
    completionHandler(.newData)
  }
  
  // 其他必要的方法,比如处理VoIP推送唤醒等
}

Android配置

  • android/app/src/main/AndroidManifest.xml 中添加必要的权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<application
    ... >
    <service
        android:name=".MyVoipService"
        android:permission="android.permission.BIND_INCALL_SERVICE">
        <intent-filter>
            <action android:name="android.telecom.InCallService" />
        </intent-filter>
    </service>
    ...
</application>
  • 创建 MyVoipService 类来处理VoIP通话(示例代码略,因为涉及复杂的CallKit/ConnectionService集成)。

3. 使用 flutter_voip_kit 进行VoIP通信

在你的Flutter代码中,你可以按照以下方式使用 flutter_voip_kit

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter VoIP Demo'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              ElevatedButton(
                onPressed: () async {
                  // 注册VoIP推送
                  await FlutterVoipKit.registerVoipPushToken();
                },
                child: Text('Register VoIP Push Token'),
              ),
              ElevatedButton(
                onPressed: () async {
                  // 发起VoIP通话
                  await FlutterVoipKit.startCall(
                    callerId: 'caller123',
                    handle: 'callee456',
                    headers: {
                      'custom-header': 'value',
                    },
                  );
                },
                child: Text('Start VoIP Call'),
              ),
              ElevatedButton(
                onPressed: () async {
                  // 结束VoIP通话
                  await FlutterVoipKit.endCall();
                },
                child: Text('End VoIP Call'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

注意事项

  1. 权限处理:确保在运行时请求并处理必要的权限。
  2. 推送证书:iOS上需要配置VoIP推送证书。
  3. CallKit/ConnectionService:为了在后台处理VoIP通话,需要集成CallKit(iOS)和ConnectionService(Android)。
  4. 错误处理:在实际应用中,需要添加错误处理和用户反馈机制。

以上代码提供了一个基本的框架,展示了如何在Flutter中使用 flutter_voip_kit 进行VoIP通信。根据具体需求,你可能需要扩展和修改这些代码。

回到顶部