UnifiedPush library
Library to subscribe and receive push notifications with UnifiedPush.
To receive notifications with UnifiedPush, users must have a dedicated application, a distributor, installed on their system.
Initialize the receiver
When you initialize your application, register the different functions that will handle the incoming events with UnifiedPush.initialize:
UnifiedPush.initialize(
onNewEndpoint: onNewEndpoint,
onRegistrationFailed: onRegistrationFailed,
onUnregistered: onUnregistered,
onMessage: onMessage,
).then((registered) => { if (registered) UnifiedPush.register(instance) });
void onNewEndpoint(PushEndpoint endpoint, String instance) {
// You should send the endpoint to your application server
// and sync for missing notifications.
}
void onRegistrationFailed(FailedReason reason, String instance) {}
void onUnregistered(String instance) {}
void onMessage(PushMessage message, String instance) {}
Register for push messages
When you try to register for the first time, you will probably want to use the user default distributor:
UnifiedPush.tryUseCurrentOrDefaultDistributor().then((success) {
debugPrint("Current or Default found=$success");
if (success) {
UnifiedPush.registerApp(
instance, // Optionnal String, to get multiple endpoints (one per instance)
features = [] // Optionnal String Array with required features, if a platform needs it
vapid = vapid // Optionnal String with the server public VAPID key
);
} else {
getUserChoice(); // You UI function to has the distributor to use
}
});
If using the current distrbutor doesn't succeed, or when you want to let the user chose a non-default distrbutor, you can implement your own logic:
void getUserChoice() {
// Get a list of distributors that are available
final distributors = await UnifiedPush.getDistributors(
[] // Optionnal String Array with required features
);
// select one or show a dialog or whatever
final distributor = myPickerFunc(distributors);
// save the distributor
UnifiedPush.saveDistributor(distributor);
// register your app to the distributor
UnifiedPush.registerApp(
instance, // Optionnal String, to get multiple endpoints (one per instance)
features = [] // Optionnal String Array with required features, if a platform needs it
vapid = vapid // Optionnal String with the server public VAPID key
);
}
If you want, unifiedpush_ui provides a dialog to pick the user choice.
Unregister
A registration can be canceled with UnifiedPush.unregister
.
Embed a distributor
On Android, this is possible to embed a distributor that will register to the Google play services directly. You will need to update the Android side of your flutter project. For more information refer to unifiedpush.org/kdoc/embedded_fcm_distributor/.
Send push messages
You can then send web push messages to your applications. Web push is defined by 3 RFC: RFC8030 defines the content of the http request used to push a message, RFC8291 defines the (required) encryption of the push messages, and RFC8292 defines the authorization used to control the sender of push messages, this authoization is known as VAPID and is optional with most distributors, required by others.
When the application receives a new endpoint, it comes with information used by the server to encrypt notifications too: PushEndpoint.pubKeySet.
The application automatically decrypt incoming notifications. When onNewMessage is called, PushMessage.content contains the decrypted content of the push notification. If it wasn't possible to correctly decrypt it, PushMessage.decrypted is false, and PushMessage.content contains the encrypted content of push notifications.
Example
An example app can be found on the repository.
Troubleshooting
The build fails because of duplicate classes
An error is thrown during build about duplicate classes, related to tink:
> A failure occurred while executing com.android.build.gradle.internal.tasks.CheckDuplicatesRunnable
> Duplicate class com.google.crypto.tink.AccessesPartialKey found in modules tink-1.16.0.jar -> jetified-tink-1.16.0 (com.google.crypto.tink:tink:1.16.0) and tink-android-1.9.0.jar -> jetified-tink-android-1.9.0 (com.google.crypto.tink:tink-android:1.9.0)
Duplicate class com.google.crypto.tink.Aead found in modules tink-1.16.0.jar -> jetified-tink-1.16.0 (com.google.crypto.tink:tink:1.16.0) and tink-android-1.9.0.jar -> jetified-tink-android-1.9.0 (com.google.crypto.tink:tink-android:1.9.0)
Duplicate class com.google.crypto.tink.BinaryKeysetReader found in modules tink-1.16.0.jar -> jetified-tink-1.16.0 (com.google.crypto.tink:tink:1.16.0) and tink-android-1.9.0.jar -> jetified-tink-android-1.9.0 (com.google.crypto.tink:tink-android:1.9.0)
[...]
This is due to another library using another version of tink.
To resolve the issue, edit android/app/build.gradle.kts, and add, after the plugins
section:
configurations.all {
// Use the latest version published: https://central.sonatype.com/artifact/com.google.crypto.tink/tink-android
val tink = "com.google.crypto.tink:tink-android:1.17.0"
// You can also use the library declaration catalog
// val tink = libs.google.tink
resolutionStrategy {
force(tink)
dependencySubstitution {
substitute(module("com.google.crypto.tink:tink")).using(module(tink))
}
}
}