Hey ๐๐ป
If youa re using custom auth (not Firebase), you probably ran into issues trying to get push notification to work for your FlutterFlow app.
Well, I've made some progress and I am posting here to see if there are others who can help in moving this forward with me (since I am not a developer and mostly relying on online resources and ChatGPT!).
Please note:
At this stage, I am mainly focused on getting push to work on iOS (which I think is harder than Android). Therefore, I've tested on iOS so far.
I will not be sharing the steps related to configuring Firebase Messaging and adding your Apple Auth Key there to be able to fetch APNs
This is a work in progress and while the code is working, it still includes debugging reporting to the console which we should remove once we reach a good/final version as a community
Notes About the current code v1:
It can fetch an APNs for iOS devices (with retries)
Using the APNs it can try to fetch an FCM Token
It stores both tokens into app states which you should create (strings): fcmToken, apnsToken
Push notifications require a physical device, I am still testing on iOS
The Code
1) Create a custom Action with the following code
Name: initializeMessaging
// Automatic FlutterFlow imports
import '/backend/schema/structs/index.dart';
import '/actions/actions.dart' as action_blocks;
import '/flutter_flow/flutter_flow_theme.dart';
import '/flutter_flow/flutter_flow_util.dart';
import '/custom_code/actions/index.dart'; // Imports other custom actions
import '/flutter_flow/custom_functions.dart'; // Imports custom functions
import 'package:flutter/material.dart';
// Begin custom action code
// DO NOT REMOVE OR MODIFY THE CODE ABOVE!
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'dart:io'; // Required for Platform check
Future initializeMessaging() async {
// Add your function code here!
await Firebase.initializeApp();
try {
// Request notification permissions
FirebaseMessaging.instance.requestPermission().then((settings) async {
if (settings.authorizationStatus == AuthorizationStatus.authorized) {
try {
String? token = await FirebaseMessaging.instance.getToken();
FFAppState().fcmToken = token ?? 'No token';
// Comment the below out if not needed for debug
print('FCM token: $token');
} catch (error) {
FFAppState().fcmToken = 'Token retrieval failed';
print('Error getting FCM token: $error');
}
} else {
FFAppState().fcmToken = 'Push permission not approved';
print('Push notifications permission not granted.');
}
}).catchError((error) {
FFAppState().fcmToken = 'Permission request failed';
print('Error requesting push notification permissions: $error');
});
// Fetch APNs token for iOS with retry mechanism
if (Platform.isIOS) {
String? apnsToken = await getAPNSTokenWithRetry();
if (apnsToken != null) {
print('APNs token: $apnsToken');
FFAppState().apnsToken = apnsToken;
FirebaseMessaging.instance.onTokenRefresh.listen((fcmToken) {
print("FCM Token refreshed: $fcmToken");
FFAppState().fcmToken = fcmToken ?? 'No token';
});
} else {
print('APNs token not available after retry.');
}
}
// Listen for foreground messages
FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
print('Received message: ${message.notification?.title}');
});
} catch (error) {
print('Error in initializing messaging: $error');
FFAppState().fcmToken = 'Initialization failed';
}
}
// Function to retry getting APNs token
Future<String?> getAPNSTokenWithRetry(
{int retries = 3, Duration delay = const Duration(seconds: 3)}) async {
for (int i = 0; i < retries; i++) {
try {
String? apnsToken = await FirebaseMessaging.instance.getAPNSToken();
if (apnsToken != null) {
return apnsToken;
}
} catch (error) {
print('Error getting APNs token on attempt ${i + 1}: $error');
}
await Future<void>.delayed(delay);
}
return null;
}
2) Add the custom action to the "main.dart" through FlutterFlow, in the Final Actions section.
3) Testing the Code (By Accessing the Project in XCode)
You will not be able to test/use this code using a local mode even if using a physical device. To test it, you need to open the XCode project (for iOS on Mac, I have to use Runner.xcworkspace which doesn't open by default from FlutterFlow. You should open the project folder and then open the ios folder)
After opening the correct workspace make sure you are adding the following 2 items (Push Notifications & Background Modes) under "Signing & Capabilities" after selecting "Runner" from Targets on the left. Note the settings enabled for Background Modes.
Click the play button to compile and test on your device.
Assuming you did the configuration correctly on Firebase Messaging & Apple's side, this should allow you to store APNs and FCM Token to the app state, and it will be shown in the console output on XCode. You can take FCM Token and send a message from Firebase Messaging. Please note that the current code for the custom action provided below works when the app is in the background/not open. Once I am able to make this work, I plan to explore making it work in foreground.
Pending Items/Areas for Improvements:
Not yet sure if there is a need to modify files outside of FlutterFlow to enable Push capabilities or not for iOS. I'd rather not do this and I hope this can be done. However, the code will not work on the physical device if those 2 capabilities are not added for the target device from within XCode.
Code is yet to be tested in production.
Final version to strip out console logging and reduce any non-necessary debugging code/comments.