Mobilni razvoj
FlutterPush notifikacijeFCMAPNsFirebaseMobilnoDeep linkingPouzdanostVodič

Flutter push notifikacije u produkciji: FCM + APNs, deep linkovi i pouzdanost (vodič za 2026.)

AO
Adrijan Omićević
·14 min čitanja

# Što ćete izgraditi (i zašto je produkcija drugačija)#

Ovaj vodič pokriva Flutter push notifikacije FCM APNs end-to-end, s produkcijskim načinom razmišljanja: ne samo “prikaže se banner”, nego isporuku, routing, životni ciklus tokena i observability.

U produkciji push sustavi padaju iz dosadnih razloga: istekli tokeni, krivo konfigurirani APNs ključevi, pogrešno okruženje, nedostajući background handleri ili krhko routanje deep linkova. Razlika između “push featurea” i push platforme je pouzdanost.

Implementirat ćete:

  • FCM + APNs postavljanje za Android i iOS
  • strategiju životnog ciklusa tokena koja preživi reinstalacije aplikacije, odjave i promjene uređaja
  • obrasce topicova i segmentacije koji skaliraju dalje od “pošalji svima”
  • deep linking iz notifikacija kroz foreground, background i terminated stanja
  • checkliste za troubleshooting i savjete za isporučivost koje možete predati timu

Ovo možete upariti s našim vodičem za Firebase temelje: Flutter Firebase tutorial.

# Preduvjeti#

ZahtjevVerzijaNapomene
Flutter3.19+Koristite najnoviji stable ako je moguće
Dart3+Dolazi uz Flutter
Firebase projektFCM uključen
iOSXcode 15+Apple Developer račun potreban za APNs
AndroidminSdk 21+Preporučeno za moderno ponašanje
PaketiNajnovijefirebase_core, firebase_messaging, opcionalno flutter_local_notifications

ℹ️ Napomena: Na iOS-u notifikacije u konačnici isporučuje APNs. FCM djeluje kao vaš provider i sloj mapiranja, ali vaša iOS aplikacija mora biti ispravno entitleana i potpisana, inače će isporuka tiho failati.

# Arhitektura: FCM, APNs i vaš backend#

Pouzdana produkcijska postava obično izgleda ovako:

KomponentaOdgovornostProdukcijski rizik
Flutter appTraži dopuštenje, upravlja tokenima, obrađuje tapove, routa deep linkoveUpravljanje stanjima, refresh tokena, UX
FCMRegistracija uređaja, topic messaging, delivery bridge prema APNs-uPostavljanje APNs ključa, format poruke
APNsiOS isporukaEntitlementi, okruženje, headeri, push type
BackendCiljanje korisnika, segmenti, idempotency, audit logoviSigurnost, retryji, analitika

Preporuka: Šaljite s backenda, ne iz aplikacije#

Slanje push notifikacija direktno iz aplikacije je teško osigurati i praktički nemoguće kvalitetno auditirati. U produkciji tretirajte push kao backend mogućnost uz:

  • autentificirane send API-je
  • rate limite
  • predloške poruka
  • metrike isporuke po kampanji

Za security hardening, uskladite push endpointove sa širom checklistom: Web application security checklist.

# Korak 1: Konfigurirajte Firebase i FCM#

Android: Dodajte Firebase i registrirajte aplikaciju#

  1. 1
    Kreirajte Android app u Firebaseu s točnim applicationId.
  2. 2
    Preuzmite google-services.json u android/app.
  3. 3
    Primijenite Google services plugin i provjerite da ovisnosti odgovaraju vašoj Gradle verziji.

Na Androidu 13 i novijem morate tražiti dopuštenje za notifikacije u runtimeu (Flutter plugin pomaže, ali i dalje ga morate eksplicitno pozvati).

iOS: Dodajte Firebase i APNs vjerodajnice#

  1. 1
    Kreirajte iOS app u Firebaseu s točnim bundle ID-jem.
  2. 2
    Preuzmite GoogleService-Info.plist u ios/Runner.
  3. 3
    U Apple Developer:
    • uključite capability Push Notifications za App ID
    • generirajte APNs Auth Key (preporučeno)
  4. 4
    U Firebase konzoli:
    • uploadajte APNs Auth Key .p8
    • postavite Key ID i Team ID

Koristite APNs Auth Key za dugoročnu pouzdanost. Certifikati istječu i uzrokuju iznenadne ispade.

⚠️ Upozorenje: Jedan od najčešćih produkcijskih failova je korištenje pogrešnog bundle ID-ja ili provisioning profila. Ako signing identity ne odgovara entitlementima, APNs registracija može uspjeti lokalno, ali isporuka će failati u produkciji.

# Korak 2: Dodajte pakete i inicijalizirajte messaging#

Dodajte pakete:

Bash
flutter pub add firebase_core firebase_messaging

Inicijalizirajte Firebase i konfigurirajte background obradu.

Dart
// main.dart
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
 
@pragma('vm:entry-point')
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  await Firebase.initializeApp();
  // Keep this minimal: log, enqueue work, update local storage.
}
 
Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
 
  FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
 
  runApp(const MyApp());
}

Zatražite dopuštenje i konfigurirajte foreground prikaz (iOS)#

Dart
Future<void> setupPushPermissions() async {
  final messaging = FirebaseMessaging.instance;
 
  final settings = await messaging.requestPermission(
    alert: true,
    badge: true,
    sound: true,
    provisional: false,
  );
 
  // iOS: show notifications while app is in foreground
  await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
    alert: true,
    badge: true,
    sound: true,
  );
 
  // You can log settings.authorizationStatus for analytics.
}

Produkcijska napomena: stope prihvaćanja dopuštenja jako variraju po kategoriji. Mnoge consumer aplikacije imaju opt-in oko 50 do 80 posto uz dobar value prompt, dok “cold prompt” može biti značajno niži. Koristite pre-permission ekran ako o notifikacijama ovisi ključni dio workflowa.

# Korak 3: Životni ciklus tokena koji ne razbije kampanje#

Upravljanje tokenima je mjesto gdje se većina sustava s vremenom degradira.

Što može promijeniti token#

FCM registration tokeni nisu stabilni identifikatori. Mogu se rotirati zbog:

  • reinstalacije aplikacije ili brisanja podataka
  • OS restorea ili promjene uređaja
  • updatea aplikacije i interne rotacije
  • uključivanja/isključivanja postavki notifikacija od strane korisnika
  • promjena iOS APNs tokena koje dovode do novog mapiranja FCM tokena

Tokene morate tretirati kao promjenjive i držati ih sinkronizirane s backendom.

Minimalna produkcijska strategija za tokene#

DogađajAkcija u aplikacijiAkcija na backendu
Start aplikacije nakon login-adohvat tokenaupsert token pod user ID
Refresh tokenapošalji novi tokenoznači stari token neaktivnim
Logoutobriši token lokalnoukloni vezu s korisnikom
Uninstallnema callbackadetektiraj kroz send failove i očisti

Implementirajte:

  • getToken() pri startu
  • listener onTokenRefresh
  • server-side upsert ključan po userId + deviceId
Dart
Future<void> syncTokenWithBackend(String userId) async {
  final messaging = FirebaseMessaging.instance;
 
  final token = await messaging.getToken();
  if (token != null) {
    await sendTokenToBackend(userId: userId, fcmToken: token);
  }
 
  FirebaseMessaging.instance.onTokenRefresh.listen((newToken) async {
    await sendTokenToBackend(userId: userId, fcmToken: newToken);
  });
}

Backend endpoint neka bude idempotentan. Ako mobilna aplikacija pošalje isti token više puta, to ne smije stvarati duplikate.

💡 Savjet: Spremite tokene s metapodacima: platforma, verzija aplikacije, locale, vremenska zona i last-seen timestamp. To omogućuje cleanup jobove i pametniju segmentaciju bez dodatnih poziva s klijenta.

Backend model podataka za tokene#

PoljeTipZašto je bitno
idUUIDInterna referenca
user_idstringCiljanje i privatnost
device_idstringPodrška za više uređaja
fcm_tokenstringAdresa za isporuku
platformenumiOS i Android se razlikuju
app_versionstringDebugging rollouta
enabledbooleanOpt-out obrada
last_seen_attimestampCleanup i “svježina”

Čišćenje i pruning tokena#

FCM send odgovori će vam reći kada je token nevažeći. Napravite nightly job koji:

  • onemogućava tokene koji hard-failaju
  • uklanja tokene koji nisu viđeni 60 do 180 dana (po vašoj politici)
  • čuva audit logove radi compliancea

Ako ne pruneate, platit ćete kroz lošiju isporučivost i “šumnije” metrike.

# Korak 4: Topics i segmenti koji skaliraju#

Strategija samo s topicsima rijetko preživi prvi marketing campaign. Strategija samo sa segmentima može preopteretiti backend fanoutom.

Koristite hibridni pristup:

Kada topics imaju smisla#

Topics su najbolji za:

  • široke interesne skupine poput sports, promotions, product_updates
  • globalne operativne push poruke poput status_incidents
  • ekstremno jednostavne pretplate koje kontrolirate u aplikaciji

Pretplate na topics u aplikaciji:

Dart
Future<void> subscribeToTopics() async {
  await FirebaseMessaging.instance.subscribeToTopic('product_updates');
  await FirebaseMessaging.instance.subscribeToTopic('promotions');
}

Kada je backend segmentacija bolja#

Koristite backend segmentaciju kada trebate:

  • ciljanja po više atributa poput država + plan + prozor aktivnosti
  • frequency capove poput max 2 push poruke dnevno
  • A B testove i holdout grupe
  • compliance pravila, npr. marketinški consent po regiji
StrategijaSloženostPreciznost ciljanjaOperativni rizik
Samo topicsNiskaSrednjaSrednji, topics se lako “zapetljaju”
Samo backend segmentiVisokaVisokaVisok, fanout troškovi i bugovi
HibridSrednjaVisokaNizak do srednji uz disciplinu

Praktični primjeri segmenata#

  • “Aktivni u zadnjih 7 dana i visok rizik od churn-a”
  • “Plaćeni korisnici na verziji manjoj od 3.4.0”
  • “Croatia locale i opt-in na marketing”

U produkciji je najveća dobit obično frequency capping. Više smanjuje opt-out i povećava povjerenje nego bilo kakva promjena copyja.

# Korak 5: Deep linkovi iz notifikacija (bez slomljenog routanja)#

Notifikacija vrijedi samo ako dovede korisnika na pravi ekran.

Dizajn payloada: Data prvo#

Preferirajte data payload polja koja kontrolirate, pa ih mapirajte u rute:

KeyPrimjerSvrha
typeorder_updateRouting i analitika
deep_linkmyapp://orders/123Navigacijska meta
id123Lookup entiteta
campaign_idspring_2026_01Atribucija
dedupe_iduser123-ord123-v2Idempotency

Izbjegavajte enkodiranje kompleksnih JSON stringova. Payload neka bude mali i predvidiv.

Obrada u foreground, background i terminated stanju#

Morate pokriti tri toka:

  1. 1
    Foreground: poruka stiže dok je aplikacija otvorena
  2. 2
    Background: korisnik tapne notifikaciju
  3. 3
    Terminated: aplikacija se pokreće nakon tap-a

Osnovni listeneri:

Dart
void setupMessageHandlers() {
  FirebaseMessaging.onMessage.listen((message) {
    // Foreground: you may show an in-app banner or local notification.
    handleMessage(message, openedFromTap: false);
  });
 
  FirebaseMessaging.onMessageOpenedApp.listen((message) {
    // Background: user tapped notification.
    handleMessage(message, openedFromTap: true);
  });
}
 
Future<void> handleInitialMessage() async {
  final initial = await FirebaseMessaging.instance.getInitialMessage();
  if (initial != null) {
    handleMessage(initial, openedFromTap: true);
  }
}

Sigurno routanje nakon starta aplikacije#

Navigacija deep linkom često faila jer aplikacija pokušava navigirati prije nego što su:

  • učitana korisnička sesija
  • router spreman
  • potrebni podaci “hidratizirani”

Koristite queue: spremite deep link, pa routajte tek kad je aplikacija spremna.

Dart
String? pendingDeepLink;
 
void handleMessage(RemoteMessage message, {required bool openedFromTap}) {
  final deepLink = message.data['deep_link'];
  if (deepLink is String && deepLink.isNotEmpty) {
    pendingDeepLink = deepLink;
  }
}
 
void onAppReadyNavigate() {
  final link = pendingDeepLink;
  pendingDeepLink = null;
  if (link == null) return;
 
  // Parse and route using your navigation solution.
  // Example: go_router or Navigator.
}

🎯 Ključna poruka: Tretirajte navigaciju iz notifikacija kao i bilo koji drugi deep link: validirajte inpute, odgodite routanje dok aplikacija nije spremna i učinite ga idempotentnim.

Sigurnost deep linkova#

Nikad nemojte slijepo vjerovati notification data payloadu. U nekim kontekstima napadači mogu replayati ili spoofati intentove. Validirajte:

  • dopuštene putanje
  • obavezne parametre
  • autorizaciju korisnika za pristup entitetu

Ovo je isti princip kao na webu: validacija na serveru i na klijentu. Primijenite mindset iz sigurnosne checkliste: Web application security checklist.

# Korak 6: Background obrada i obrasci pouzdanosti#

Ograničenja background handlera#

Background handler nije za težak posao. Zadržite ga na:

  • logiranju
  • pisanju u local storage
  • scheduleanju lokalnog taska
  • sinkanju malog payloada

Ako trebate garantirano izvršavanje u pozadini, dizajnirajte eventual consistency: ažurirajte stanje na serveru i pustite aplikaciju da fetch-a updateove pri sljedećem otvaranju.

Koristite local notifications za konzistentan UX u foregroundu#

Na Androidu i iOS-u foreground push poruke možda neće prikazati sistemski UI osim ako ga konfigurirate. Mnogi timovi koriste flutter_local_notifications kako bi prikazali konzistentan banner kad se okine onMessage.

Ako to radite, držite se dva pravila:

  • nikad ne “double-notify”: pokažite local notification samo kada se remote notifikacija ionako ne prikazuje
  • koristite iste payload ključeve kako bi tap handling ostao konzistentan

Idempotency i deduplikacija#

Mobilne mreže su nepouzdane. Korisnici mogu tapnuti dvaput. Isporuka se može retryati. Vaša aplikacija treba tretirati akcije pokrenute notifikacijom kao idempotentne.

Koristite dedupe_id i spremite nedavno obrađene ID-jeve na 24 sata:

  • ako je već obrađeno, ignorirajte
  • ako nije, obradite i spremite

To smanjuje duplikate navigacije i ponovljene dijaloge.

# Isporučivost i pouzdanost: što stvarno pravi razliku#

Pouzdanost je većinom operativna disciplina, ne kod.

Praktična checklist za isporučivost#

PodručjeŠto napravitiZašto pomaže
Svježina tokenapruneajte zastarjele tokene, pratite last seensmanjuje invalid sendove i šum
Permission UXodgodite permission dok vrijednost nije jasnapovećava opt-in
Quiet hourspoštujte lokalno vrijeme korisnikasmanjuje opt-out
Frequency capsprovedite per user i per campaignsmanjuje zamor
Veličina payloadadržite data malismanjuje failove i parsing bugove
Monitoringalertajte na send failove i pad isporukebrzo hvata outage

Mjerite ono što je bitno#

Minimum koji trebate pratiti:

  • attempted sends
  • accepted by FCM
  • stopa invalid tokena
  • otvaranja iz push-a
  • opt-out stopa
  • vrijeme do otvaranja

“Dobra” stopa invalid tokena ovisi o churnu i profilu reinstalacija, ali ako kontinuirano raste, životni ciklus tokena vam je slomljen.

Za performanse kada se aplikacija otvara iz push-a, pazite da target ekran ostane brz. Ako deep link sleti na “tešku” stranicu, korisnici odustaju. Osvježite osnove performansi ovdje: Flutter performance optimization for 60 FPS.

# Troubleshooting checkliste (copy-paste za runbook)#

iOS checklist: Push nije isporučen#

  1. 1
    Potvrdite da je Push Notifications capability uključen za App ID.
  2. 2
    Potvrdite da se za build koristi ispravan provisioning profile.
  3. 3
    Potvrdite da je APNs Auth Key uploadan u Firebase i da Team ID odgovara.
  4. 4
    Potvrdite da uređaj ima dopuštenje u iOS Settings.
  5. 5
    Potvrdite da je setForegroundNotificationPresentationOptions postavljen za prikaz u foregroundu.
  6. 6
    Potvrdite da bundle ID točno odgovara Firebase app registraciji.
  7. 7
    Potvrdite da ne testirate na simulatoru za stvarno APNs ponašanje.
  8. 8
    Potvrdite da poruka uključuje ispravna polja za vaš use case:
    • koristite data payload za routing
    • uključite notification payload ako želite sistemski UI bez local notifications

Android checklist: Notifikacija se ne prikazuje#

  1. 1
    Android 13 i noviji: potvrdite da je POST_NOTIFICATIONS dopuštenje odobreno.
  2. 2
    Potvrdite da aplikacija ima default notification channel i da je importance high.
  3. 3
    Potvrdite da ne suppressate notifikacije u foregroundu bez local notifications.
  4. 4
    Potvrdite da battery optimization ne ograničava aplikaciju previše.
  5. 5
    Potvrdite da payload uključuje notification polja ako se oslanjate na sistemski UI.

Token i backend checklist: “Radi nekim korisnicima”#

  1. 1
    Provjerite da backend sprema više tokena po korisniku za multi-device.
  2. 2
    Provjerite da logout uklanja asocijaciju tokena.
  3. 3
    Provjerite da token refresh ažurira backend.
  4. 4
    Provjerite da ne prepisujete tokene između korisnika.
  5. 5
    Provjerite da se invalid tokeni pruneaju nakon FCM odgovora.
  6. 6
    Provjerite da su segmentation filteri točni i auditirani.
  1. 1
    Provjerite da se getInitialMessage() obrađuje za terminated stanje.
  2. 2
    Provjerite da se onMessageOpenedApp obrađuje za background stanje.
  3. 3
    Provjerite da routing čeka dok sesija i router nisu spremni.
  4. 4
    Provjerite da su deep linkovi validirani i mapirani na postojeće rute.
  5. 5
    Provjerite da je tap handling idempotentan i dedupliciran.

# Produkcijski formati poruka (primjeri)#

Primjer: Samo data za custom obradu#

JSON
{
  "message": {
    "token": "{{fcm_token}}",
    "data": {
      "type": "order_update",
      "deep_link": "myapp://orders/123",
      "campaign_id": "ops_2026_04",
      "dedupe_id": "user42-order123-v1"
    }
  }
}

Primjer: Notification + Data za sistemski UI plus routing#

JSON
{
  "message": {
    "token": "{{fcm_token}}",
    "notification": {
      "title": "Order shipped",
      "body": "Tracking is available now."
    },
    "data": {
      "type": "order_update",
      "deep_link": "myapp://orders/123"
    }
  }
}

Držite server-side template varijable poput {{fcm_token}} eksplicitnima i validirajte sve podatke prije slanja.

# Ključne lekcije#

  • Implementirajte stvarni životni ciklus tokena: upsert pri login-u, slušajte refresh, disocirajte pri logoutu i server-side pruneajte invalid tokene.
  • Koristite hibridnu strategiju ciljanja: topics za široke interese, backend segmenti za precizno targetiranje, frequency capove i compliance.
  • Pokrijte sva stanja aplikacije: onMessage za foreground, onMessageOpenedApp za background tapove i getInitialMessage() za pokretanje iz terminated stanja.
  • Dizajnirajte payload za routing: mali, stabilni ključevi poput type, deep_link, campaign_id i dedupe_id za idempotency i analitiku.
  • Tretirajte pouzdanost kao operacije: pratite stopu invalid tokena, padove isporuke i latenciju otvaranja iz push-a te održavajte runbook s iOS i Android checklistama.

# Zaključak#

Flutter push notifikacije je lako demonstrirati, a iznenađujuće lako slomiti u produkciji. Ako izgradite disciplinu oko životnog ciklusa tokena, predvidive payloade, deep-link routanje kroz sva stanja i osnovni monitoring isporučivosti, dobit ćete sustav koji raste s bazom korisnika umjesto da se s vremenom degradira.

Ako želite da Samioda pregleda vašu trenutnu postavu ili implementira produkcijski push pipeline sa segmentacijom i deep linkovima, kontaktirajte nas i pomoći ćemo vam isporučiti notifikacije kojima korisnici stvarno vjeruju i koje stvarno otvaraju.

FAQ

Share
A
Adrijan OmićevićSamioda Team
All articles →

Više iz kategorije Mobilni razvoj

Sve

Trebate pomoć s projektom?

Gradimo prilagođena rješenja koristeći tehnologije iz ovog članka. Senior tim, fiksne cijene.