I’m utilizing the flutter_callkit_incoming package deal in my Flutter app to deal with incoming name notifications. The difficulty is that whereas notifications seem when the app is within the foreground or background, it doesn’t work as anticipated when the app is in a Kill state. Incoming name notifications present up, however tapping on them doesn’t set off the app to open or deal with the decision.
I’ve adopted the package deal documentation and configured the required capabilities (Push Notifications, Background Modes, and VoIP). This concern appears to happen solely on iOS. On Android, every little thing works positive.
What could possibly be inflicting this drawback, and the way can I make sure the app handles incoming calls accurately even within the Kill state?
void primary() async {
AwesomeNotifications().initialize(
null,
[
NotificationChannel(
channelKey: 'basic_channel',
channelName: 'Basic notifications',
channelDescription: 'Notification channel for basic tests',
defaultColor: const Color(0xFF9D50DD),
ledColor: Colors.white,
),
NotificationChannel(
channelKey: 'call_channel',
channelName: 'Call notifications',
channelDescription: 'Notification channel for call notifications',
defaultColor: Colors.orange,
ledColor: Colors.white,
importance: NotificationImportance.Max,
channelShowBadge: true,
locked: false,
playSound: true,
defaultRingtoneType: DefaultRingtoneType.Ringtone,
),
NotificationChannel(
channelKey: 'message_channel',
channelName: 'Message notifications',
channelDescription: 'Notification channel for message notifications',
defaultColor: Colors.blue,
ledColor: Colors.white,
importance: NotificationImportance.High,
channelShowBadge: true,
),
],
);
await Firebase.initializeApp(
choices: DefaultFirebaseOptions.currentPlatform,
);
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
FirebaseMessaging.onMessage.pay attention((RemoteMessage message) async {
Logger().d('Distant Message ${message.notification}');
Logger().d('Distant Message ${message.knowledge.toString()}');
String? title = message.knowledge['sender_name'] ?? 'Yeni Mesaj';
String? physique = message.knowledge['body'];
String channelKey = message.knowledge['channel_key'] ?? 'default_channel';
String? id = message.knowledge['id'] ?? '0';
print(
'Message title: ${message.notification?.title}, physique: ${message.notification?.physique}, knowledge: ${message.knowledge}');
if (channelKey == 'message_channel') {
await AwesomeNotifications().createNotification(
content material: NotificationContent(
id: getUniqueNotificationId(),
channelKey: channelKey,
shade: Colours.blue,
title: title,
physique: physique,
class: NotificationCategory.Message,
backgroundColor: Colours.blue,
payload: {'message-api-id': id, 'username': title}),
actionButtons: [
NotificationActionButton(
key: 'READ',
label: 'Read Message',
color: Colors.green,
),
NotificationActionButton(
key: 'DISMISS',
label: 'Dismiss',
color: Colors.red,
),
],
localizations: {
// Azərbaycanca
'az': NotificationLocalization(buttonLabels: {
'READ': 'Mesajı oxu',
'DISMISS': 'İmtina et',
}),
// EN
'en': NotificationLocalization(
buttonLabels: {
'READ': 'Learn Message',
'DISMISS': 'Dismiss',
},
),
// Rus
'ru': NotificationLocalization(
buttonLabels: {
'READ': 'Прочитать сообщение',
'DISMISS': 'Отклонить',
},
),
},
);
} else if (channelKey == 'call_channel') {
print('--------------------1');
showCallkitIncoming(const Uuid().v4(), message);
}
});
runApp(const MyApp());
}
@pragma('vm:entry-point')
Future listenerEvent() async {
strive {
FlutterCallkitIncoming.onEvent.pay attention((occasion) async {
print('Occasion: $occasion');
change (occasion!.occasion) {
case Occasion.actionCallIncoming:
// TODO: acquired an incoming name
break;
case Occasion.actionCallStart:
// TODO: began an outgoing name
// TODO: present display screen calling in Flutter
break;
case Occasion.actionCallAccept:
print('uuid ${occasion.physique['uuid']}');
NavigationService.occasion
.pushNamedIfNotCurrent(AppRoute.callingPage, args: occasion);
break;
case Occasion.actionCallDecline:
// TODO: declined an incoming name
// print(
//'actionCallDecline actionCallDecline actionCallDecline actionCallDecline actionCallDecline ');
break;
case Occasion.actionCallEnded:
// TODO: ended an incoming/outgoing name
break;
case Occasion.actionCallTimeout:
// TODO: missed an incoming name
break;
case Occasion.actionCallCallback:
// TODO: solely Android - click on motion `Name again` from missed name notification
break;
case Occasion.actionCallToggleHold:
// TODO: solely iOS
break;
case Occasion.actionCallToggleMute:
// TODO: solely iOS
break;
case Occasion.actionCallToggleDmtf:
// TODO: solely iOS
break;
case Occasion.actionCallToggleGroup:
// TODO: solely iOS
break;
case Occasion.actionCallToggleAudioSession:
NavigationService.occasion
.pushNamedIfNotCurrent(AppRoute.callingPage, args: occasion);
break;
case Occasion.actionDidUpdateDevicePushTokenVoip:
// TODO: solely iOS
break;
case Occasion.actionCallCustom:
break;
}
});
} on Exception catch (e) {}
}
@pragma('vm:entry-point')
Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
var token = await FlutterCallkitIncoming.getDevicePushTokenVoIP();
Logger().d('Distant Message ${message.notification}');
Logger().d('Distant Message ${message.knowledge.toString()}');
String? title = message.knowledge['sender_name'] ?? 'Yeni Mesaj';
String? physique = message.knowledge['body'];
String channelKey = message.knowledge['channel_key'] ?? 'default_channel';
String? id = message.knowledge['id'] ?? '0';
print(
'Message title: ${message.notification?.title}, physique: ${message.notification?.physique}, knowledge: ${message.knowledge}');
if (channelKey == 'message_channel') {
await AwesomeNotifications().createNotification(
content material: NotificationContent(
id: getUniqueNotificationId(),
channelKey: channelKey,
shade: Colours.blue,
title: title,
physique: physique,
class: NotificationCategory.Message,
backgroundColor: Colours.blue,
payload: {'message-api-id': id, 'username': title}),
actionButtons: [
NotificationActionButton(
key: 'READ',
label: 'Read Message',
color: Colors.green,
),
NotificationActionButton(
key: 'DISMISS',
label: 'Dismiss',
color: Colors.red,
),
],
localizations: {
// Azərbaycanca
'az': NotificationLocalization(buttonLabels: {
'READ': 'Mesajı oxu',
'DISMISS': 'İmtina et',
}),
// EN
'en': NotificationLocalization(
buttonLabels: {
'READ': 'Learn Message',
'DISMISS': 'Dismiss',
},
),
// Rus
'ru': NotificationLocalization(
buttonLabels: {
'READ': 'Прочитать сообщение',
'DISMISS': 'Отклонить',
},
),
},
);
} else if (channelKey == 'call_channel') {
print('--------------------2');
showCallkitIncoming(const Uuid().v4(), message);
}
}
Future showCallkitIncoming(String uuid, RemoteMessage message) async {
strive {
print('uuid $uuid');
last params = CallKitParams(
id: uuid,
nameCaller: message.knowledge['sender_name'],
appName: 'Legalis',
avatar: 'https://i.pravatar.cc/100',
deal with: '0123456789',
kind: 0,
period: 30000,
textAccept: 'Settle for',
textDecline: 'Decline',
missedCallNotification: const NotificationParams(
showNotification: true,
isShowCallback: false,
subtitle: 'Missed name',
),
additional: message.knowledge,
android: const AndroidParams(
isCustomNotification: true,
isShowLogo: false,
ringtonePath: 'system_ringtone_default',
backgroundColor: '#000E2B',
isShowFullLockedScreen: true,
isCustomSmallExNotification: true,
actionColor: '#4CAF50',
textColor: '#ffffff',
),
ios: const IOSParams(
iconName: 'CallKitLogo',
handleType: '',
supportsVideo: false,
maximumCallGroups: 2,
maximumCallsPerCallGroup: 1,
audioSessionMode: 'default',
audioSessionActive: true,
audioSessionPreferredSampleRate: 44100.0,
audioSessionPreferredIOBufferDuration: 0.005,
supportsDTMF: false,
supportsHolding: true,
supportsGrouping: false,
supportsUngrouping: false,
configureAudioSession: false,
ringtonePath: 'system_ringtone_default',
),
);
await FlutterCallkitIncoming.showCallkitIncoming(params);
} catch (e) {
print('Error: $e');
}
}
// ignore: must_be_immutable
class MyApp extends StatefulWidget {
last Widget? web page;
const MyApp({tremendous.key, this.web page});
@override
State createState() => _MyAppState();
}
class _MyAppState extends State with WidgetsBindingObserver {
late last Uuid _uuid;
String? _currentUuid;
late last FirebaseMessaging _firebaseMessaging;
@override
void initState() {
tremendous.initState();
// Take away _listenerCallKit name since we already initialized it in primary()
listenerEvent();
_uuid = const Uuid();
initFirebase();
WidgetsBinding.occasion.addObserver(this);
//Verify name when open app from terminated
checkAndNavigationCallingPage();
AwesomeNotifications().setListeners(
onActionReceivedMethod: NotificationController.onActionReceivedMethod,
onNotificationCreatedMethod:
NotificationController.onNotificationCreatedMethod,
onNotificationDisplayedMethod:
NotificationController.onNotificationDisplayedMethod,
onDismissActionReceivedMethod:
NotificationController.onDismissActionReceivedMethod);
}
getCurrentCall() async {
//test present name from pushkit if potential
var calls = await FlutterCallkitIncoming.activeCalls();
if (calls is Record) {
if (calls.isNotEmpty) {
if (calls[0]['id'] == CallUtil.currentKitCallId) {
return null;
}
print('DATA: $calls');
_currentUuid = calls[0]['id'];
CallUtil.currentKitCallId = _currentUuid;
return calls[0];
} else {
_currentUuid = "";
return null;
}
}
}
checkAndNavigationCallingPage() async {
var currentCall = await getCurrentCall();
print('Present Name : $currentCall');
print(currentCall.runtimeType);
if (currentCall != null) {
NavigationService.occasion
.pushNamedIfNotCurrent(AppRoute.callingPage, args: currentCall);
}
}
@override
Future didChangeAppLifecycleState(AppLifecycleState state) async {
// print(state);
if (state == AppLifecycleState.resumed) {
//Verify name when open app from background
checkAndNavigationCallingPage();
}
}
@override
Widget construct(BuildContext context) {
// Fbs
return MultiProvider(
suppliers: [
ChangeNotifierProvider(create: (_) => MessageListProvider()),
ChangeNotifierProvider(create: (_) => ChatDetailProvider()),
ChangeNotifierProvider(create: (_) => LawyerReportProvider()),
ChangeNotifierProvider(create: (_) => UserServiceProvider()),
ChangeNotifierProvider(create: (_) => CallProvider()),
ChangeNotifierProvider(create: (_) => CallAcceptProvider()),
ChangeNotifierProvider(create: (_) => NavigationProvider()),
ChangeNotifierProvider(create: (_) => LocalizationProvider()),
ChangeNotifierProvider(create: (_) => CommunicationProvider()),
ChangeNotifierProvider(create: (_) => AuthProvider()),
ChangeNotifierProvider(create: (_) => CategoryProvider()),
ChangeNotifierProvider(create: (_) => LanguageProvider()),
ChangeNotifierProvider(create: (_) => UserProvider()),
],
baby: Client(
builder: (context, supplier, baby) {
return MaterialApp(
onGenerateRoute: AppRoute.generateRoute,
initialRoute: AppRoute.homePage,
title: 'Legalis',
navigatorKey: NavigationService.occasion.navigationKey,
navigatorObservers: [
NavigationService.instance.routeObserver
],
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
locale: supplier.locale,
supportedLocales: L10n.help,
theme: ThemeData(
bottomSheetTheme: const BottomSheetThemeData(
backgroundColor: Coloration(0xFF000E2B)),
pageTransitionsTheme: const PageTransitionsTheme(
builders: {
TargetPlatform.android: CupertinoPageTransitionsBuilder(),
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
},
),
fontFamily: 'SF-Professional-Show',
primarySwatch: Colours.inexperienced,
appBarTheme: const AppBarTheme(
backgroundColor: Coloration(0xFF000E2B),
elevation: 0,
iconTheme: IconThemeData(shade: Colours.white),
titleTextStyle: TextStyle(
shade: Colours.white,
fontSize: 24,
fontWeight: FontWeight.w700,
),
),
scaffoldBackgroundColor: Colours.white,
),
house: widget.web page ?? const SplashPage(),
);
},
),
);
}
@override
void dispose() {
WidgetsBinding.occasion.removeObserver(this);
tremendous.dispose();
}
Future getDevicePushTokenVoIP() async {
var devicePushTokenVoIP =
await FlutterCallkitIncoming.getDevicePushTokenVoIP();
// print(devicePushTokenVoIP);
}
Future initFirebase() async {
await Firebase.initializeApp(
choices: DefaultFirebaseOptions.currentPlatform,
);
_firebaseMessaging = FirebaseMessaging.occasion;
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
await Future.delayed(const Period(seconds: 1));
NotificationSettings settings = await _firebaseMessaging.requestPermission(
alert: true,
badge: true,
sound: true,
);
await _firebaseMessaging.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
if (settings.authorizationStatus == AuthorizationStatus.licensed) {
strive {
String? apnsToken = await _firebaseMessaging.getAPNSToken();
Logger().t('APNS Token: $apnsToken');
if (apnsToken != null) {
print('APNS Token: $apnsToken');
}
} catch (e) {
print('Error: $e');
}
}
strive {
String? token = await _firebaseMessaging.getToken();
AppUtil.fcmToken = token ?? null;
print('FCM Token: ${AppUtil.fcmToken}');
} catch (e) {
print('Error: $e');
}
AwesomeNotifications().setListeners(
onActionReceivedMethod: (ReceivedAction receivedAction) async {
AppUtil.init();
AppUtil.setNotification(receivedAction.id);
NotificationController.onActionReceivedMethod(receivedAction);
},
onNotificationCreatedMethod:
(ReceivedNotification receivedNotification) async {
AppUtil.setNotification(receivedNotification.id);
NotificationController.onNotificationCreatedMethod(
receivedNotification);
},
onNotificationDisplayedMethod:
(ReceivedNotification receivedNotification) async {
NotificationController.onNotificationDisplayedMethod(
receivedNotification);
},
onDismissActionReceivedMethod: (ReceivedAction receivedAction) async {
NotificationController.onDismissActionReceivedMethod(receivedAction);
},
);
}
}
class CallUtil {
static String? currentKitCallId = '';
}