Flutter开发VoIP通话应用:如何在锁屏显示通话操作Widget?
Hey there! Building a VoIP app with lock-screen call controls (so users can answer/decline without unlocking) is totally feasible in Flutter, but you’ll need to lean on platform-specific native APIs since lock-screen behavior is tightly governed by iOS and Android. Let me walk you through a practical, step-by-step solution:
Flutter doesn’t have direct access to lock-screen UI controls out of the box, so we’ll use a combination of:
- Native platform frameworks: Android’s
InCallServiceand iOS’sCallKit(these are the only ways to get system-level lock-screen call interfaces that OSes trust) - Flutter plugins: A well-maintained plugin to bridge native functionality to your Dart code, avoiding writing full native code from scratch
flutter_callkit_incoming This plugin wraps both Android’s InCallService and iOS’s CallKit, giving you a unified API to handle incoming calls, lock-screen UI, and call state callbacks. It’s the most popular choice for Flutter VoIP apps.
Step 1: Set Up Dependencies
Add the plugin to your pubspec.yaml:
dependencies: flutter_callkit_incoming: ^latest_version # Check pub.dev for the latest version
Run flutter pub get to install it.
Step 2: Platform-Specific Configuration
Android
- Add Permissions & Service to
AndroidManifest.xml:
Openandroid/app/src/main/AndroidManifest.xmland add these lines inside the<manifest>tag:<!-- Required permissions --> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CALLS" /> <!-- Register InCallService --> <application ...> <service android:name="com.hiennv.flutter_callkit_incoming.CallkitIncomingService" android:permission="android.permission.BIND_INCALL_SERVICE"> <meta-data android:name="android.telecom.IN_CALL_SERVICE_UI" android:value="true" /> <intent-filter> <action android:name="android.telecom.InCallService" /> </intent-filter> </service> </application> - Dynamic Permission Request:
For Android 13+, you need to request thePOST_NOTIFICATIONSpermission at runtime. Add this code to your Flutter init logic:import 'package:permission_handler/permission_handler.dart'; Future<void> requestPermissions() async { if (await Permission.notification.isDenied) { await Permission.notification.request(); } }
iOS
- Enable Background Modes:
Open your iOS project in Xcode, go to your target’s Signing & Capabilities tab, add the Background Modes capability, and check Voice over IP. - Update
Info.plist:
Add these entries toios/Runner/Info.plistto request necessary permissions:<key>NSMicrophoneUsageDescription</key> <string>Need microphone access to make and receive calls</string> <key>UIBackgroundModes</key> <array> <string>voip</string> </array>
Step 3: Flutter Layer Implementation
Initialize the Plugin
Call this when your app starts (e.g., in main.dart after runApp):
import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart'; void initCallKit() async { await FlutterCallkitIncoming.init( appName: 'My VoIP App', iconName: 'ic_launcher', // Use your app's launcher icon name ringtonePath: 'system_ringtone', // Or a custom ringtone path // Optional: Customize UI colors, vibration, etc. ); }
Trigger an Incoming Call
When your app receives a VoIP push or incoming call signal, call this to show the lock-screen call UI:
void showIncomingCall(String callId, String callerName, String callerNumber) async { final callParams = CallKitParams( id: callId, // Unique ID for the call nameCaller: callerName, handle: callerNumber, type: 0, // 0 = voice call, 1 = video call hasVideo: false, duration: 30000, // Ring duration in ms ); await FlutterCallkitIncoming.showCallkitIncoming(callParams); }
Listen for Call Actions
Set up a listener to handle answer/decline/end events from the lock screen:
void setupCallListeners() { FlutterCallkitIncoming.onEvent.listen((event) { if (event == null) return; switch (event.event) { case EventAction.ACTION_CALL_ACCEPT: // Handle call acceptance (e.g., connect WebRTC session) String callId = event.data['id']; acceptCall(callId); break; case EventAction.ACTION_CALL_DECLINE: // Handle call decline (e.g., send busy signal to caller) String callId = event.data['id']; declineCall(callId); break; case EventAction.ACTION_CALL_ENDED: // Handle call end (e.g., clean up WebRTC resources) String callId = event.data['id']; endCall(callId); break; // Handle other events like call timeout, mute, etc. as needed } }); }
Step 4: Handle VoIP Push Notifications
To receive incoming calls when your app is in the background or terminated, you’ll need to implement VoIP push notifications:
- For iOS: Use Apple’s VoIP push service (APNs with VoIP payload)
- For Android: Use Firebase Cloud Messaging (FCM) with high-priority messages
The plugin will automatically wake your app when a VoIP push is received, allowing you to trigger the showIncomingCall method.
- Android: Using
InCallServiceensures your call UI is treated as a system-level call, so it will show on the lock screen without extra permissions. Avoid custom lock-screen overlays (they requireSYSTEM_ALERT_WINDOWpermission which is restricted on newer Android versions). - iOS: Apple strictly enforces using CallKit for VoIP calls—any custom lock-screen UI will be blocked. CallKit integrates seamlessly with iOS’s native call log and lock screen.
- Testing: Always test lock-screen behavior on physical devices (emulators/simulators may not replicate lock-screen UI correctly).
内容的提问来源于stack exchange,提问作者Akas Antony




