I am using FLutterFlow to build an app and my coding level is beginner. I need to create a valid push data payload structure (JSON) at backend side to pass to app two parameters:
- app page (tab) name that should be opened when user taps on push (is called "initialPageName" in code below)
- push unique id assigned by backend (is called "pushGUID" in code below)
Below is push handler code that is under the hood of FlutterFlow web app builder, I can not change it. I know it works, because when I use FlutterFlow web push sender and fill in "initial page" and "push GUID" everything works fine. But when I send push from my backend using FCM HTTP API I get only notification itself, data payload with "push GUID" is not handled correctly.
import 'dart:async';
import 'dart:convert';
import 'serialization_util.dart';
import '../backend.dart';
import '../../flutter_flow/flutter_flow_theme.dart';
import '../../flutter_flow/flutter_flow_util.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import '../../index.dart';
import '../../main.dart';
final _handledMessageIds = <String?>{};
class PushNotificationsHandler extends StatefulWidget {
const PushNotificationsHandler({Key? key, required this.child})
: super(key: key);
final Widget child;
@override
_PushNotificationsHandlerState createState() =>
_PushNotificationsHandlerState();
}
class _PushNotificationsHandlerState extends State<PushNotificationsHandler> {
bool _loading = false;
Future handleOpenedPushNotification() async {
if (isWeb) {
return;
}
final notification = await FirebaseMessaging.instance.getInitialMessage();
if (notification != null) {
await _handlePushNotification(notification);
}
FirebaseMessaging.onMessageOpenedApp.listen(_handlePushNotification);
}
Future _handlePushNotification(RemoteMessage message) async {
if (_handledMessageIds.contains(message.messageId)) {
return;
}
_handledMessageIds.add(message.messageId);
if (mounted) {
setState(() => _loading = true);
}
try {
final initialPageName = message.data['initialPageName'] as String;
final initialParameterData = getInitialParameterData(message.data);
final parametersBuilder = parametersBuilderMap[initialPageName];
if (parametersBuilder != null) {
final parameterData = await parametersBuilder(initialParameterData);
context.pushNamed(
initialPageName,
params: parameterData.params,
extra: parameterData.extra,
);
}
} catch (e) {
print('Error: $e');
} finally {
if (mounted) {
setState(() => _loading = false);
}
}
}
@override
void initState() {
super.initState();
handleOpenedPushNotification();
}
@override
Widget build(BuildContext context) => _loading
? Container(
color: Colors.white,
child: Image.asset(
'assets/images/logo.png',
fit: BoxFit.contain,
),
)
: widget.child;
}
class ParameterData {
const ParameterData(
{this.requiredParams = const {}, this.allParams = const {}});
final Map<String, String?> requiredParams;
final Map<String, dynamic> allParams;
Map<String, String> get params => Map.fromEntries(
requiredParams.entries
.where((e) => e.value != null)
.map((e) => MapEntry(e.key, e.value!)),
);
Map<String, dynamic> get extra => Map.fromEntries(
allParams.entries.where((e) => e.value != null),
);
static Future<ParameterData> Function(Map<String, dynamic>) none() =>
(data) async => ParameterData();
}
final parametersBuilderMap =
<String, Future<ParameterData> Function(Map<String, dynamic>)>{
'PhoneAuth': ParameterData.none(),
'smsVerify': (data) async => ParameterData(
allParams: {
'phoneNumber': getParameter<String>(data, 'phoneNumber'),
},
),
'MainPage': (data) async => ParameterData(
allParams: {
'pushGUID': getParameter<String>(data, 'pushGUID'),
},
),
'Campaign': ParameterData.none(),
'LoyaltyPoints': ParameterData.none(),
'OnTheMap': ParameterData.none(),
'NewCollections': ParameterData.none(),
'Notifications': ParameterData.none(),
};
Map<String, dynamic> getInitialParameterData(Map<String, dynamic> data) {
try {
final parameterDataStr = data['parameterData'];
if (parameterDataStr == null ||
parameterDataStr is! String ||
parameterDataStr.isEmpty) {
return {};
}
return jsonDecode(parameterDataStr) as Map<String, dynamic>;
} catch (e) {
print('Error parsing parameter data: $e');
return {};
}
}
My guess looking at the code above is that I should use nested JSON like this:
{
"message":{
"notification":{
"body" : "my body",
"title" : "my title",
},
"data" : {
"initialPage":"mainPage",
"params":[
{"pushGUID":"1231231231"}
]
}
}
}
But as I mentioned my Futter code level is beginner, so please have a look at the handler code and correct my JSON structure, thanks for any advice.