Skip to content
Pushix Pushix
Features Developers Guides Pricing Questions
Sign in Start free
Menu
Features Developers Guides Pricing Questions
Sign in Create a free account
  1. Home
  2. Guides
  3. Flutter

Flutter push notifications

Add push notifications to a Flutter app on Android and iPhone with Firebase Cloud Messaging (FCM) and Pushix: setup, taps, tags, in-app messages and testing.

Updated 31 October 2026 9 min read Mobile apps

Mobile apps

  • Flutter
  • Android
  • iOS
  • React Native

Websites

  • Web push
  • WordPress

Your backend

  • REST API
  • PHP, Node.js, Python

Switching

  • Switching to Pushix
  • All guides
On this page 16 sections
  1. Requirements
  2. 1. Add Firebase to the app
  3. 2. Add the package
  4. 3. Android setup
  5. 4. iOS setup
  6. 5. Initialize
  7. API overview
  8. Handling taps
  9. While the app is open
  10. In-app messages
  11. Tags
  12. Data-only messages
  13. If your app already has a background handler
  14. How notifications are drawn
  15. Testing
  16. Troubleshooting

The pushix_flutter package registers Android phones and iPhones with Pushix, shows notifications, tells your app when one is tapped, and shows in-app messages.

Requirements#

  • Flutter 3.32 or newer (Dart 3.8+)
  • firebase_core 5 and firebase_messaging 17 (earlier versions hide foreground notifications on iOS). If your app uses other FlutterFire plugins, move them to their matching versions too.
  • Android minSdk 24 and compileSdk 36
  • iOS 15.0 or newer, Xcode 16, and a paid Apple Developer account (push needs an APNs key)
  • One Firebase project with your Android app and your iOS app in it.

1. Add Firebase to the app#

The quickest way is the FlutterFire CLI:

Shell
dart pub global activate flutterfire_cli
flutterfire configure

This adds google-services.json (Android) and GoogleService-Info.plist (iOS). Use the Firebase project whose service account you upload to Pushix. A different project makes every send fail with SENDER_ID_MISMATCH.

2. Add the package#

Copy the sdks/flutter folder from the Pushix download next to your app as pushix_flutter (or put it in a private Git repository) and add it to pubspec.yaml:

YAML
dependencies:
  firebase_core: ">=5.0.0 <6.0.0"
  pushix_flutter:
    path: ../pushix_flutter
  # or from Git:
  # pushix_flutter:
  #   git:
  #     url: https://github.com/you/pushix_flutter.git

If the app already uses another push SDK, remove it: two push SDKs in one app fight over Firebase messaging.

3. Android setup#

Desugaring — required by flutter_local_notifications. In android/app/build.gradle.kts:

Kotlin
android {
    compileOptions {
        isCoreLibraryDesugaringEnabled = true
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }
}

dependencies {
    coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
}

With the older build.gradle (Groovy): coreLibraryDesugaringEnabled true and coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.4'.

AndroidManifest.xml — inside <application>:

XML
<!-- Lets notification buttons work -->
<receiver android:exported="false"
    android:name="com.dexterous.flutterlocalnotifications.ActionBroadcastReceiver" />

<!-- Default channel and icon for notifications Android shows while the app is closed -->
<meta-data android:name="com.google.firebase.messaging.default_notification_channel_id"
    android:value="pushix_default" />
<meta-data android:name="com.google.firebase.messaging.default_notification_icon"
    android:resource="@drawable/ic_stat_notification" />

Notification icon — Android draws the small icon as a white silhouette, so a full-colour launcher icon shows up as a white square. Create a white-on-transparent icon (Android Studio → New → Image Asset → Notification Icons), name it ic_stat_notification, and pass defaultSmallIcon: '@drawable/ic_stat_notification' to initialize. You can also set it once for all sends in the dashboard (App settings → Android defaults).

Keep icons and sounds in release builds — icons and sounds named only in messages (small_icon, android_sound) look unused to the build and get removed by resource shrinking. Create android/app/src/main/res/raw/keep.xml:

XML
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:keep="@drawable/ic_stat_*,@raw/*" />

If a message names an icon or sound that doesn't exist, the SDK falls back to the default icon and sound instead of dropping the notification.

POST_NOTIFICATIONS (Android 13+) is already declared by the Firebase plugin.

4. iOS setup#

iPhone notifications travel from Pushix through Firebase to Apple (APNs). Four one-time steps:

APNs key in Firebase. In the Apple Developer site create a key with Apple Push Notifications service (APNs) and download the .p8 file. In Firebase → Project settings → Cloud Messaging → Apple app configuration, upload it with its Key ID and your Team ID. One key works for all your apps.

Capabilities. Open ios/Runner.xcworkspace in Xcode, select the Runner target → Signing & Capabilities, and add Push Notifications and Background Modes with Remote notifications ticked.

Deployment target. In ios/Podfile set platform :ios, '15.0', and the same under Runner → General → Minimum Deployments.

AppDelegate. In ios/Runner/AppDelegate.swift, make the app the notification delegate so notifications the SDK shows while the app is open appear:

Swift
import Flutter
import UIKit

@main
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

Pictures and buttons on iPhone (optional)#

iOS only shows a picture (big_picture or ios_attachments) and buttons when the app has a Notification Service Extension. Pushix has one ready:

  1. Open ios/Runner.xcworkspace in Xcode: File → New → Target → Notification Service Extension, name it NotificationService and click Finish (choose "Cancel" if Xcode offers to activate the scheme).
  2. Add Pushix's extension library to it in ios/Podfile, then run cd ios && pod install:
Ruby
target 'NotificationService' do
  use_frameworks!
  pod 'Pushix/Extension', :path => '../../pushix/sdks/ios'   # the sdks/ios folder from your Pushix download
end
  1. Replace the generated NotificationService.swift with:
Swift
import Pushix

class NotificationService: PushixNotificationServiceExtension {}

The extension downloads the picture and adds the message's buttons (it registers a category for them); it needs no Firebase code. Deployment target 13.0 or later.

Your own button categories (optional)#

With the extension above, buttons need nothing else. If your app registers its own categories, you can still send one by name. Register it once in AppDelegate:

Swift
let claim = UNNotificationAction(identifier: "claim", title: "Claim", options: [.foreground])
let later = UNNotificationAction(identifier: "later", title: "Later", options: [])
UNUserNotificationCenter.current().setNotificationCategories([
  UNNotificationCategory(identifier: "OFFER_ACTIONS", actions: [claim, later], intentIdentifiers: [])
])

and send "ios_category": "OFFER_ACTIONS" (composer → iOS options → Category). The tapped button's identifier arrives as event.result.actionId.

Badges#

Send ios_badgeType Increase or SetTo with ios_badgeCount. Pushix keeps each iPhone's count and resets it to zero on the server whenever the app opens. To clear the number on the icon itself when the app opens, use a badge plugin such as app_badge_plus.

5. Initialize#

Dart
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:pushix_flutter/pushix_flutter.dart';

final navigatorKey = GlobalKey<NavigatorState>();

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  await Pushix.initialize(
    'YOUR_APP_ID',
    serverUrl: 'https://pushix.in',
    defaultSmallIcon: '@drawable/ic_stat_notification', // Android
    navigatorKey: navigatorKey,                         // optional, for in-app messages
  );

  Pushix.Notifications.addClickListener((event) {
    final data = event.notification.additionalData ?? {};
    // route to a screen, e.g. data['screen'] == 'orders'
  });

  runApp(MaterialApp(navigatorKey: navigatorKey, home: const HomePage()));
}

Then ask for permission at a sensible moment (iPhone and Android 13+ show the system dialog):

Dart
await Pushix.Notifications.requestPermission(true);

When the user logs in to your app, link the device to your user id so your backend can target them:

Dart
Pushix.login(user.id.toString());   // on login
Pushix.logout();                    // on logout (also clears tags)

That's it. Open the app once and the device appears in the dashboard under Subscribers, marked Android or iOS.

API overview#

CallWhat it does
Pushix.initialize(appId, serverUrl: ...)Starts the SDK. Options: defaultSmallIcon, navigatorKey, autoOpenWebUrls, inAppMessages, registerBackgroundHandler
Pushix.Debug.setLogLevel(PushixLogLevel.verbose)How much the SDK logs (none … verbose)
Pushix.login(id) / Pushix.logout()Links the device to your user id / unlinks it (also clears tags)
Pushix.consentRequired(true) / Pushix.consentGiven(true)Registers only after the user agrees
Pushix.Notifications.requestPermission(true) / .permission / addPermissionObserverNotification permission
Pushix.Notifications.addClickListener / addForegroundWillDisplayListener / addSilentDataListener (and remove…)Taps, notifications arriving while the app is open, data-only messages
Pushix.Notifications.clearAll()Removes the app's notifications from the shade
Pushix.User.addTag(k, v) (or addTagWithKey) / addTags(map) / removeTag / removeTags / getTags()Tags
Pushix.User.setLanguage('hi') / getExternalId() / getPlayerId()Message language, your user id, this device's player id
Pushix.User.pushSubscription.id / .token / .optedIn / optIn() / optOut() / addObserverPlayer id, FCM token and opt-in state of this device
Pushix.InAppMessages.addTrigger(k, v) / addTriggers / removeTrigger / removeTriggers / clearTriggersIn-app message triggers
Pushix.InAppMessages.paused(true) / arePaused()Holds in-app messages back
Pushix.InAppMessages.addClickListener / addWillDisplayListener / addDidDisplayListener / addWillDismissListener / addDidDismissListenerIn-app message events
Pushix.handleBackgroundMessage(message)For apps with their own Firebase background handler

The main types are PushixNotification, PushixNotificationClickEvent, PushixNotificationWillDisplayEvent and PushixInAppMessageClickEvent.

Handling taps#

Dart
Pushix.Notifications.addClickListener((event) {
  final n = event.notification;
  print(n.notificationId);      // id from the REST API
  print(n.additionalData);      // your "data" object
  print(n.launchUrl);           // your "url"
  print(event.result.actionId); // button id, or null for the notification itself
});
  • Taps that open the app from closed are delivered as soon as you add the first click listener, so add it early in main().
  • http:// and https:// URLs open in the browser automatically. Turn that off with autoOpenWebUrls: false in initialize.
  • Deep links (myshop://orders) are not opened automatically; read event.result.url and navigate yourself.
  • Each tap is reported to the server and shows up as “Clicked” in the dashboard.

While the app is open#

Notifications are shown even when the app is in the foreground, on Android and iPhone. To hide or delay one:

Dart
Pushix.Notifications.addForegroundWillDisplayListener((event) {
  if (currentScreen == 'orders') {
    event.preventDefault();            // don't show it
    // event.notification.display();   // or show it later
  }
});

On iOS the SDK turns off Firebase's own foreground banners so a message isn't shown twice. That also applies to pushes from other senders in your app, which aren't shown while it is open.

In-app messages#

Create them in the dashboard under In-app messages: a pop-up, card, top or bottom banner, or full screen, with your colours, picture and up to three buttons. The SDK downloads the ones meant for this device each time the app opens and shows them by their trigger:

TriggerShows
When the app opensOn each new session (launch, or back after 30+ seconds)
From the Nth sessionWhen the app opens and the device has had at least N sessions
After some time in the appN seconds into the session
When my code sets a triggerWhen your code calls addTrigger with a matching key and value
Dart
Pushix.InAppMessages.addTrigger('cart_value', 1200);
Pushix.InAppMessages.paused(true);   // e.g. during a game round
Pushix.InAppMessages.paused(false);

Pushix.InAppMessages.addClickListener((event) {
  // event.result.actionId is the button id; event.result.url its link
  if (event.result.action == 'deep_link' && event.result.url == 'myshop://account') {
    navigatorKey.currentState?.pushNamed('/kyc');
  }
});

Frequency rules (show at most N times, wait between showings) are kept on the device. "Open a link" buttons open the browser; "Deep link" buttons are handed to your click listener (or opened with the system if you have none). Seen, clicked and closed counts appear in the dashboard and in Analytics.

In-app messages need a MaterialApp (or WidgetsApp) with a Navigator. The SDK finds it automatically; pass navigatorKey to initialize if your app has more than one Navigator, and inAppMessages: false to turn the feature off.

Tags#

Tags are key–value strings used for segments, filters and {{ personalisation }}.

Dart
Pushix.User.addTags({'level': 7, 'plan': 'gold', 'last_bonus': DateTime.now().millisecondsSinceEpoch ~/ 1000});
Pushix.User.removeTag('plan');
final tags = await Pushix.User.getTags();

Your backend can also set tags by user id without the app: PUT /api/v1/apps/{app_id}/users/{external_user_id}.

Changes made while offline are saved on the phone and sent the next time the app opens.

Data-only messages#

Messages sent with content_available: true and no contents don't show anything. While the app is open you get them here:

Dart
Pushix.Notifications.addSilentDataListener((n) {
  if (n.additionalData?['refresh'] == 'balance') reloadBalance();
});

When the app is in the background they arrive in Firebase's background handler (see below). iOS limits how often background messages wake an app.

If your app already has a background handler#

initialize registers a Firebase background handler that draws Android notifications with buttons or a large icon while the app is closed. Firebase allows only one. If you already call FirebaseMessaging.onBackgroundMessage, pass registerBackgroundHandler: false and call Pushix from your handler:

Dart
@pragma('vm:entry-point')
Future<void> myBackgroundHandler(RemoteMessage message) async {
  await Firebase.initializeApp();
  await Pushix.handleBackgroundMessage(message);
  // ...your own code
}

How notifications are drawn#

  • Plain messages (title, text, picture) are sent as normal notifications. Android and iOS show them even if the app was swiped away.
  • On Android, messages with buttons, a large icon or android_group are sent as data messages and drawn by the SDK. These need the app process to start, which some phone makers (Xiaomi, Oppo, Vivo, Realme) block when battery saving is aggressive. Prefer plain messages for anything important.

Testing#

  1. Run the app on your phone and allow notifications. On iPhone use a real device; the simulator only receives pushes on recent Xcode with Apple silicon.
  2. In the dashboard, open Subscribers, click your device and tick Test device.
  3. New message → Send to test devices.

Troubleshooting#

ProblemFix
Device never appears in SubscribersCheck serverUrl (must be reachable from the phone over HTTPS) and the App ID. Turn on Pushix.Debug.setLogLevel(PushixLogLevel.verbose) and read the logs.
Every send fails with SENDER_ID_MISMATCHThe service account in the dashboard is from a different Firebase project than google-services.json / GoogleService-Info.plist.
iPhone never registers ("No APNs token yet" in the log)Add the Push Notifications capability, check the bundle id matches the iOS app in Firebase, and test on a real device.
iPhone registers but nothing arrivesUpload the APNs key in Firebase → Cloud Messaging. Firebase reports this as an authentication error in the message's error.
iPhone: nothing shows while the app is openAdd the UNUserNotificationCenter.current().delegate line to AppDelegate, and use firebase_messaging 17 or newer.
iPhone: no picture or no buttonsAdd the Pushix Notification Service Extension (see iOS setup).
Notification shows as a white square (Android)Use a white-on-transparent small icon (see Android setup).
No pop-up, only an icon in the status bar (Android)The channel isn't high importance. Use the default pushix_default channel, or create your own with Importance.high. If the user lowered it in system settings, only they can raise it again.
Works when open, nothing when closed (Xiaomi etc.)Ask users to allow Autostart / disable battery optimisation for the app, and send plain messages (no buttons or large icon).
Buttons don't respond (Android)Add the ActionBroadcastReceiver to the manifest.
Custom sound doesn't play (Android)On Android 8+ the channel decides the sound. Create a separate channel with that sound in your app and send its id as android_channel_id.
In-app message never showsCheck its status is Live, the platform is ticked, and its frequency hasn't been used up on this device. Messages are fetched when a session starts.
Build error about desugaringAdd the desugaring lines from Android setup.
Next guideAndroid push notifications

Send your first notification

Create an app in Pushix to get your App ID and REST API key, then follow this guide. The free plan needs no card.

Create a free account See pricing

On this page

  1. Requirements
  2. 1. Add Firebase to the app
  3. 2. Add the package
  4. 3. Android setup
  5. 4. iOS setup
  6. Pictures and buttons on iPhone (optional)
  7. Your own button categories (optional)
  8. Badges
  9. 5. Initialize
  10. API overview
  11. Handling taps
  12. While the app is open
  13. In-app messages
  14. Tags
  15. Data-only messages
  16. If your app already has a background handler
  17. How notifications are drawn
  18. Testing
  19. Troubleshooting
Back to top
Pushix Pushix

Push notification service for Android, iPhone and websites, from one dashboard or one API call. Built in West Bengal, India.

info@pushix.in

Product

Features Developers Pricing Questions Sign in Create an account

Guides

Flutter guide Android guide iOS guide React Native guide Web push guide WordPress guide REST API guide PHP, Node.js, Python guide Switching to Pushix guide All guides

Legal

Terms of Service Privacy Policy Cookie Policy Acceptable Use Policy Data Processing Addendum Refund & Cancellation Policy Service Delivery Policy

Company

Contact us All legal pages Support

© 2026 Pushix. All rights reserved.

Pushix · Powered by Pynix