Flutter通话结束后弹窗实现求助(Call Log包报错)
问题:Flutter实现通话结束弹窗时call_log包报
Fatal: No callback registered错误 我想在Flutter中实现类似Truecaller的通话结束后弹出弹窗功能,使用call_log包获取通话记录时触发错误:Fatal: No callback registered。
实现代码
import 'package:call_log/call_log.dart'; import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatefulWidget { const MyApp({Key? key}) : super(key: key); @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { @override void initState() { super.initState(); } // Call Future<void> fetchCallLogs() async { var callLogs = await CallLog.query(); if (callLogs.isNotEmpty) { final lastCall = callLogs.first; if (lastCall.callType == CallType.incoming) { showAlertDialog(context); } } } @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: Scaffold( appBar: AppBar( title: Text('Call Dialog'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [], ), ), ), ); } showAlertDialog(BuildContext context) { // Create button Widget okButton = TextButton( child: Text("OK"), onPressed: () { Navigator.of(context).pop(); }, ); // Create AlertDialog AlertDialog alert = AlertDialog( title: Text("Simple Alert"), content: Text("This is an alert message."), actions: [ okButton, ], ); // show the dialog showDialog( context: context, builder: (BuildContext context) { return alert; }, ); } }
运行日志
D/me.sodipto.phone_state_background(10136): New broadcast event received... W/FlutterJNI(10136): FlutterJNI.loadLibrary called more than once W/FlutterJNI(10136): FlutterJNI.prefetchDefaultFontManager called more than once I/ResourceExtractor(10136): Found extracted resources res_timestamp-1-1700557316079 W/FlutterJNI(10136): FlutterJNI.init called more than once D/me.sodipto.phone_state_background(10136): Phone State event PHONE_RINGING number: 6505551212 E/me.sodipto.phone_state_background(10136): Fatal: No callback registered
解决方案
错误原因
Fatal: No callback registered是因为call_log包的后台广播接收器触发通话状态事件时,Flutter侧未注册对应的回调处理逻辑;同时你的代码仅实现了主动查询通话记录,未监听通话状态变化,无法在通话结束时自动触发弹窗。
修复步骤
- 添加权限到AndroidManifest.xml
在android/app/src/main/AndroidManifest.xml中添加必要权限:
<uses-permission android:name="android.permission.READ_CALL_LOG" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
- 修改代码添加监听逻辑
引入权限处理包,并注册通话状态流监听,在通话结束时触发弹窗:
import 'package:call_log/call_log.dart'; import 'package:flutter/material.dart'; import 'package:permission_handler/permission_handler.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatefulWidget { const MyApp({Key? key}) : super(key: key); @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { Stream<CallEvent>? _callStream; @override void initState() { super.initState(); _initCallListener(); } // 初始化权限与通话监听 Future<void> _initCallListener() async { // 请求必要权限 final phonePerm = await Permission.phone.request(); final callLogPerm = await Permission.callLogs.request(); if (phonePerm.isGranted && callLogPerm.isGranted) { // 注册通话状态流监听,解决回调未注册问题 _callStream = CallLog.stream(); _callStream?.listen((CallEvent event) { // 监听通话结束事件 if (event.event == CallEventTypes.callEnded) { _fetchLastCallAndShowDialog(); } }); } } // 获取最后一条通话记录并弹窗 Future<void> _fetchLastCallAndShowDialog() async { final callLogs = await CallLog.query(); if (callLogs.isNotEmpty) { final lastCall = callLogs.first; if (lastCall.callType == CallType.incoming && mounted) { showAlertDialog(context); } } } @override void dispose() { _callStream?.cancel(); // 销毁时取消监听,避免内存泄漏 super.dispose(); } @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, title: 'Flutter Demo', theme: ThemeData(primarySwatch: Colors.blue), home: Scaffold( appBar: AppBar(title: const Text('Call Dialog')), body: const Center( child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [], ), ), ), ); } void showAlertDialog(BuildContext context) { final okButton = TextButton( child: const Text("OK"), onPressed: () => Navigator.of(context).pop(), ); final alert = AlertDialog( title: const Text("通话结束提示"), content: const Text("您有一个来电已结束。"), actions: [okButton], ); showDialog(context: context, builder: (_) => alert); } }
- 添加依赖到pubspec.yaml
在pubspec.yaml中补充权限处理包:
dependencies: call_log: ^latest_version permission_handler: ^latest_version
额外说明
- 使用
mounted判断可避免Widget已销毁时调用showDialog引发的错误 - call_log包的广播接收器通常会自动注册,若出现问题可手动在AndroidManifest.xml中添加:
<receiver android:name="com.sodipto.call_log.CallLogReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.intent.action.PHONE_STATE" /> <action android:name="android.intent.action.NEW_OUTGOING_CALL" /> </intent-filter> </receiver>
内容的提问来源于stack exchange,提问作者Flutter
相关产品推荐
相关产品推荐

