Flutter来电通知插件notification_incoming_call的使用

Flutter来电通知插件notification_incoming_call的使用

功能特性 ⭐

  • 显示来电
  • 启动拨出电话

安装 🚀

1. 添加依赖包

运行以下命令:

flutter pub add notification_incoming_call

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

dependencies:
  notification_incoming_call: any

2. 配置项目

Android

编辑 AndroidManifest.xml 文件:

<manifest...>
    ...
    <!-- 
        使用此权限从互联网加载图片
    -->
    <uses-permission android:name="android.permission.INTERNET"/>
</manifest>

iOS

编辑 Info.plist 文件:

<key>UIBackgroundModes</key>
<array>
    <string>processing</string>
    <string>remote-notification</string>
    <string>voip</string>
</array>

3. 使用

导入库

import 'package:notification_incoming_call/notification_incoming_call.dart';

接收来电

this._currentUuid = _uuid.v4();
var params = <String, dynamic>{
  'id': _currentUuid,
  'nameCaller': 'Hien Nguyen',
  'appName': 'Callkit',
  'avatar': 'https://i.pravatar.cc/100',
  'handle': '0123456789',
  'type': 0,
  'duration': 30000,
  'extra': <String, dynamic>{'userId': '1a2b3c4d'},
  'headers': <String, dynamic>{'apiKey': 'Abc@123!', 'platform': 'flutter'},
  'android': <String, dynamic>{
    'isCustomNotification': true,
    'isShowLogo': false,
    'backgroundColor': '#0955fa',
    'backgroundUrl': 'https://i.pravatar.cc/500',
    'actionColor': '#4CAF50'
  },
  'ios': <String, dynamic>{
    'iconName': 'AppIcon40x40',
    'handleType': 'generic',
    'supportsVideo': true,
    'maximumCallGroups': 2,
    'maximumCallsPerCallGroup': 1,
    'audioSessionMode': 'default',
    'audioSessionActive': true,
    'audioSessionPreferredSampleRate': 44100.0,
    'audioSessionPreferredIOBufferDuration': 0.005,
    'supportsDTMF': true,
    'supportsHolding': true,
    'supportsGrouping': false,
    'supportsUngrouping': false,
    'ringtonePath': 'Ringtone.caf'
  }
};
await NotificationIncomingCall.showCallkitIncoming(params);

启动拨出电话

this._currentUuid = _uuid.v4();
var params = <String, dynamic>{
  'id': this._currentUuid,
  'nameCaller': 'Hien Nguyen',
  'handle': '0123456789',
  'type': 1,
  'extra': <String, dynamic>{'userId': '1a2b3c4d'},
  'ios': <String, dynamic>{'handleType': 'generic'}
};
await NotificationIncomingCall.startCall(params);

结束来电或拨出电话

var params = <String, dynamic>{'id': this._currentUuid};
await NotificationIncomingCall.endCall(params);

结束所有通话

await NotificationIncomingCall.endAllCalls();

获取活跃通话

await NotificationIncomingCall.activeCalls();

输出:

[{"id": "8BAA2B26-47AD-42C1-9197-1D75F662DF78", ...}]

监听事件

NotificationIncomingCall.onEvent.listen((event) {
  switch (event!.name) {
    case CallEvent.ACTION_CALL_INCOMING:
      // TODO: 收到来电
      break;
    case CallEvent.ACTION_CALL_START:
      // TODO: 启动拨出电话
      // TODO: 在 Flutter 中显示呼叫界面
      break;
    case CallEvent.ACTION_CALL_ACCEPT:
      // TODO: 接听来电
      // TODO: 在 Flutter 中显示呼叫界面
      break;
    case CallEvent.ACTION_CALL_DECLINE:
      // TODO: 拒接来电
      break;
    case CallEvent.ACTION_CALL_ENDED:
      // TODO: 结束来电或拨出电话
      break;
    case CallEvent.ACTION_CALL_TIMEOUT:
      // TODO: 未接听来电
      break;
    case CallEvent.ACTION_CALL_CALLBACK:
      // TODO: 仅 Android - 点击漏接来电通知中的回拨按钮
      break;
    case CallEvent.ACTION_CALL_TOGGLE_HOLD:
      // TODO: 仅 iOS
      break;
    case CallEvent.ACTION_CALL_TOGGLE_MUTE:
      // TODO: 仅 iOS
      break;
    case CallEvent.ACTION_CALL_TOGGLE_DMTF:
      // TODO: 仅 iOS
      break;
    case CallEvent.ACTION_CALL_TOGGLE_GROUP:
      // TODO: 仅 iOS
      break;
    case CallEvent.ACTION_CALL_TOGGLE_AUDIO_SESSION:
      // TODO: 仅 iOS
      break;
  }
});

从原生调用(iOS PushKit)

var info = [String: Any?]()
info["id"] = "44d915e1-5ff4-4bed-bf13-c423048ec97a"
info["nameCaller"] = "Hien Nguyen"
info["handle"] = "0123456789"
SwiftNotificationIncomingCallPlugin.sharedInstance?.showCallkitIncoming(notification_incoming_call.Data(args: info), fromPushKit: true)

示例代码

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:notification_incoming_call/notification_incoming_call.dart';
import 'dart:async';
import 'package:uuid/uuid.dart';

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

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

class _MyAppState extends State<MyApp> {
  var _uuid = Uuid();
  var _currentUuid;
  var textEvents = "";

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

  // 平台消息是异步的,所以我们初始化时使用异步方法。
  Future<void> listenerEvent() async {
    // 平台消息可能会失败,所以我们使用带有 PlatformException 的 try/catch。
    // 我们还处理消息可能返回 null 的情况。
    try {
      NotificationIncomingCall.onEvent.listen((event) {
        print(event);
        if (!mounted) return;
        switch (event!.name) {
          case CallEvent.ACTION_CALL_INCOMING:
            // TODO: 收到来电
            break;
          case CallEvent.ACTION_CALL_START:
            // TODO: 启动拨出电话
            // TODO: 在 Flutter 中显示呼叫界面
            break;
          case CallEvent.ACTION_CALL_ACCEPT:
            // TODO: 接听来电
            // TODO: 在 Flutter 中显示呼叫界面
            break;
          case CallEvent.ACTION_CALL_DECLINE:
            // TODO: 拒接来电
            break;
          case CallEvent.ACTION_CALL_ENDED:
            // TODO: 结束来电或拨出电话
            break;
          case CallEvent.ACTION_CALL_TIMEOUT:
            // TODO: 未接听来电
            break;
          case CallEvent.ACTION_CALL_CALLBACK:
            // TODO: 仅 Android - 点击漏接来电通知中的回拨按钮
            break;
          case CallEvent.ACTION_CALL_TOGGLE_HOLD:
            // TODO: 仅 iOS
            break;
          case CallEvent.ACTION_CALL_TOGGLE_MUTE:
            // TODO: 仅 iOS
            break;
          case CallEvent.ACTION_CALL_TOGGLE_DMTF:
            // TODO: 仅 iOS
            break;
          case CallEvent.ACTION_CALL_TOGGLE_GROUP:
            // TODO: 仅 iOS
            break;
          case CallEvent.ACTION_CALL_TOGGLE_AUDIO_SESSION:
            // TODO: 仅 iOS
            break;
        }
        setState(() {
          textEvents += "${event.toString()}\n";
        });
      });
    } on Exception {}
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('插件示例应用'),
          actions: <Widget>[
            IconButton(
              icon: Icon(
                Icons.call,
                color: Colors.white,
              ),
              onPressed: () async {
                this.makeFakeCallInComing();
              },
            ),
            IconButton(
              icon: Icon(
                Icons.call_end,
                color: Colors.white,
              ),
              onPressed: () async {
                this.endCurrentCall();
              },
            ),
            IconButton(
              icon: Icon(
                Icons.call_made,
                color: Colors.white,
              ),
              onPressed: () async {
                this.startOutGoingCall();
              },
            ),
            IconButton(
              icon: Icon(
                Icons.call_merge,
                color: Colors.white,
              ),
              onPressed: () async {
                this.activeCalls();
              },
            )
          ],
        ),
        body: LayoutBuilder(
          builder: (BuildContext context, BoxConstraints viewportConstraints) {
            return SingleChildScrollView(
              child: ConstrainedBox(
                constraints: BoxConstraints(
                  minHeight: viewportConstraints.maxHeight,
                ),
                child: Text('$textEvents'),
              ),
            );
          },
        ),
      ),
    );
  }

  Future<void> makeFakeCallInComing() async {
    await Future.delayed(const Duration(seconds: 7), () async {
      this._currentUuid = _uuid.v4();
      var params = <String, dynamic>{
        'id': _currentUuid,
        'nameCaller': 'Hien Nguyen',
        'appName': 'Callkit',
        'avatar': 'https://i.pravatar.cc/100',
        'handle': '0123456789',
        'type': 0,
        'duration': 30000,
        'extra': <String, dynamic>{'userId': '1a2b3c4d'},
        'headers': <String, dynamic>{'apiKey': 'Abc@123!', 'platform': 'flutter'},
        'android': <String, dynamic>{
          'isCustomNotification': true,
          'isShowLogo': false,
          // 'ringtonePath': 'ringtone_default',
          'backgroundColor': '#0955fa',
          'backgroundUrl': 'https://i.pravatar.cc/500',
          'actionColor': '#4CAF50'
        },
        'ios': <String, dynamic>{
          'iconName': 'AppIcon40x40',
          'handleType': '',
          'supportsVideo': true,
          'maximumCallGroups': 2,
          'maximumCallsPerCallGroup': 1,
          'audioSessionMode': 'default',
          'audioSessionActive': true,
          'audioSessionPreferredSampleRate': 44100.0,
          'audioSessionPreferredIOBufferDuration': 0.005,
          'supportsDTMF': true,
          'supportsHolding': true,
          'supportsGrouping': false,
          'supportsUngrouping': false,
          'ringtonePath': 'Ringtone.caf'
        }
      };
      await NotificationIncomingCall.showCallkitIncoming(params);
    });
  }

  Future<void> endCurrentCall() async {
    var params = <String, dynamic>{'id': this._currentUuid};
    await NotificationIncomingCall.endCall(params);
  }

  Future<void> startOutGoingCall() async {
    this._currentUuid = _uuid.v4();
    var params = <String, dynamic>{
      'id': this._currentUuid,
      'nameCaller': 'Hien Nguyen',
      'handle': '0123456789',
      'type': 1,
      'extra': <String, dynamic>{'userId': '1a2b3c4d'},
      'ios': <String, dynamic>{'handleType': 'number'} // number/email
    };
    await NotificationIncomingCall.startCall(params);
  }

  Future<void> activeCalls() async {
    var calls = await NotificationIncomingCall.activeCalls();
    print(calls);
  }
}

更多关于Flutter来电通知插件notification_incoming_call的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

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


当然,以下是如何在Flutter项目中使用notification_incoming_call插件来处理来电通知的一个基本示例。这个插件允许你显示一个全屏的来电界面,并在来电时处理一些基本的通知逻辑。

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

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

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

接下来,在你的Flutter项目中,你需要进行一些配置和代码实现。以下是一个基本的示例:

  1. 初始化插件

在你的主应用文件(通常是main.dart)中,初始化NotificationIncomingCall插件。

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

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  NotificationIncomingCall().initialize(); // 初始化插件
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Incoming Call Notification Example'),
        ),
        body: Center(
          child: Text('Tap the button to simulate an incoming call'),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () => simulateIncomingCall(),
          tooltip: 'Incoming Call',
          child: Icon(Icons.phone),
        ),
      ),
    );
  }

  void simulateIncomingCall() {
    NotificationIncomingCall().showNotification(
      title: "Incoming Call",
      body: "John Doe is calling",
      payload: "call_payload_123", // 你可以传递任何你需要的附加数据
      // 其他可选参数,如头像URL、铃声等
    );

    // 模拟接听或挂断的逻辑
    Future.delayed(Duration(seconds: 5), () {
      NotificationIncomingCall().cancelNotification();
    });
  }
}
  1. 处理来电通知的显示和取消

上面的代码示例中,我们定义了一个简单的Flutter应用,其中包含一个按钮,点击按钮时会模拟一个来电通知。通知会显示5秒钟后自动取消。

  1. 处理来电的实际逻辑

在实际应用中,你可能需要在接收到来电时执行更多逻辑,比如更新UI、播放铃声、处理接听/挂断操作等。这通常涉及到与你的后端服务进行通信,以及处理用户交互。

  1. 权限处理

请注意,显示全屏通知通常需要一些特定的系统权限。确保你的应用已经请求并获得了这些权限。在Android上,你可能需要在AndroidManifest.xml中声明相关权限,并在运行时请求它们。

  1. 平台特定配置

对于iOS,你可能需要在Info.plist中添加一些配置来支持全屏通知。对于Android,你可能需要在AndroidManifest.xml中进行一些配置,并确保你的应用具有显示系统级通知的权限。

  1. 插件的具体使用

notification_incoming_call插件提供了多种配置选项,如设置铃声、振动模式、全屏通知的布局等。你可以查阅插件的官方文档来了解更多详细信息和高级用法。

请注意,由于插件和Flutter框架的更新,上述代码可能需要根据你的Flutter环境和插件版本进行调整。始终参考插件的官方文档和示例代码来获取最新和最准确的信息。

回到顶部