push_notification_kit 0.4.0 copy "push_notification_kit: ^0.4.0" to clipboard
push_notification_kit: ^0.4.0 copied to clipboard

Auth-agnostic Flutter push notifications: FCM token lifecycle, rich notifications (images & action buttons), deep links, and pluggable backend registration. Bring your own auth via a TokenProvider.

push_notification_kit #

Auth-agnostic Flutter push notifications: FCM token lifecycle, rich notifications (images & action buttons), deep links, and pluggable backend registration. Bring your own auth via a TokenProvider; bring your own backend via a DeviceRegistrar. The package never owns credentials and never assumes an identity provider, so it works with Keycloak, Firebase Auth, an API key, or anything else.

Extracted from a production app; designed to pair with the platform Push Notification service but usable against any backend.

Install #

dependencies:
  push_notification_kit: ^0.1.0

You must set up Firebase in the host app (firebase_core, firebase_options.dart, platform config files) — the package does not bundle Firebase project config.

Quick start #

final push = PushKit(
  // 1. Where device tokens are registered. HttpDeviceRegistrar authenticates
  //    with a bearer token from your TokenProvider.
  registrar: HttpDeviceRegistrar(
    registerUrl: Uri.parse('https://api.example.com/users/add-push-token'),
    tokenProvider: CallbackTokenProvider(
      getToken: () => auth.accessToken(),
      refreshToken: () => auth.forceRefresh(),
    ),
    // Legacy backend expecting {"pushToken": "..."}; omit for the full device JSON.
    tokenBodyKey: 'pushToken',
  ),

  // 2. Register only while signed in; clear on sign-out.
  authState: authCubit.stream.map((s) => s is Authenticated),

  // 3. Android channel (iOS categories are configured natively — see below).
  channel: const NotificationChannelConfig(
    channelId: 'default',
    channelName: 'Notifications',
    androidIcon: '@drawable/ic_stat_notify',
  ),

  // 4. Routing.
  onTap: (m) => router.go(m.deepLink ?? '/'),
  onAction: (actionId, m) => handleAction(actionId, m),

  // Engagement: report opens/clicks so the backend can compute open/click rates.
  reporter: HttpEngagementReporter(
    eventUrl: Uri.parse('https://api.example.com/users/notification-event'),
    tokenProvider: myTokenProvider,
  ),

  // 5. iOS action buttons: register categories whose identifiers match the
  //    `category` you send. Android ignores this (buttons come from `actions`).
  iosCategories: const [
    DarwinNotificationCategory('participation_invite', actions: [
      DarwinNotificationAction.plain('accept', 'Accept',
          options: {DarwinNotificationActionOption.foreground}),
      DarwinNotificationAction.plain('decline', 'Decline',
          options: {DarwinNotificationActionOption.foreground}),
    ]),
  ],
);

await push.init();

// After runApp (router/DI ready): dispatch a cold-start tap/action that launched
// the app from a terminated state. Fires onAction for an action-button cold start,
// onTap otherwise. No-op if the app wasn't launched from a notification.
await push.processPendingLaunch();

Background / terminated messages #

@pragma('vm:entry-point')
Future<void> _bgHandler(RemoteMessage message) async {
  await Firebase.initializeApp();
  await renderBackgroundMessage(
    message,
    channel: const NotificationChannelConfig(
      channelId: 'default', channelName: 'Notifications',
    ),
  );
}

void main() {
  FirebaseMessaging.onBackgroundMessage(_bgHandler);
  // ...
}

What the package handles #

  • Permission request; iOS APNS-token wait+retry before fetching the FCM token.
  • onTokenRefresh re-registration; register-only-when-authenticated; re-check on app resume.
  • Foreground policy: render a local notification, call back to your app, or ignore.
  • Rich rendering: downloads imageUrl and shows it as an Android BigPicture; renders actions as Android action buttons; passes category to iOS.
  • Deep-link taps (onTap) and action-button taps (onAction); cold-start via getInitialMessage.

What stays in your app #

  • Firebase project config and initialization.

  • Navigation inside onTap / business actions inside onAction.

  • iOS images require a Notification Service Extension (a native target the package cannot ship). Add one and mutate the payload to attach the image:

    // NotificationService.swift
    import UserNotifications
    class NotificationService: UNNotificationServiceExtension {
      override func didReceive(_ request: UNNotificationRequest,
          withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
        guard let content = (request.content.mutableCopy() as? UNMutableNotificationContent),
              let urlString = request.content.userInfo["fcm_options"] as? [String: Any],
              let imageURL = urlString["image"] as? String,
              let url = URL(string: imageURL) else { return contentHandler(request.content) }
        URLSession.shared.downloadTask(with: url) { tmp, _, _ in
          if let tmp = tmp,
             let attachment = try? UNNotificationAttachment(identifier: "image", url: tmp) {
            content.attachments = [attachment]
          }
          contentHandler(content)
        }.resume()
      }
    }
    

    iOS action buttons are registered via the iosCategories parameter (above) — no AppDelegate changes needed; the identifiers must match the category you send.

Testing #

flutter test        # model parsing + HttpDeviceRegistrar (auth/retry) with MockClient

PushKit itself is exercised via the example app against a real Firebase project.

0
likes
130
points
167
downloads

Documentation

API reference

Publisher

verified publisherconapps.net

Weekly Downloads

Auth-agnostic Flutter push notifications: FCM token lifecycle, rich notifications (images & action buttons), deep links, and pluggable backend registration. Bring your own auth via a TokenProvider.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

firebase_messaging, flutter, flutter_local_notifications, http, path_provider

More

Packages that depend on push_notification_kit