pkb_fcm_notification
Push notifications (Firebase Cloud Messaging) for Flutter apps in three calls —
system permission prompt, device registration with your backend, foreground display,
tap handling and an optional inbox page. Built for cooperative (credit union) apps
whose backend runs the Laravel package soat/laravel-soat-fcm-notification,
but any backend that implements the API below works.
🇹🇭 คู่มือภาษาไทย: doc/README.th.md
await PkbFcm.init(baseUrl: 'https://coop.example.com', authToken: () => storage.read(key: 'token'));
await PkbFcm.login(); // after the member signs in
await PkbFcm.logout(); // before clearing the app's login token
Features
| Permission | only the system dialog — no extra screens |
| Registration | POST /api/push/device with the app's existing login token (the member id is resolved on the server, never sent by the app) |
| Broadcasts | subscribes to topic all_members |
| Foreground | Android: shown by the plugin's native code · iOS: system presentation |
| Tap | PkbFcm.onOpened stream + marks the message as read (de-duplicated on cold start) |
| Next launch / token refresh | re-registers silently (no repeated permission prompt) |
| Logout | removes the device from the server and deletes the FCM token |
| Inbox (optional) | ready-made PkbFcmInboxPage() |
- Android & iOS, Flutter 3.22+ (Dart 3.4+)
- Only 3 dependencies:
firebase_core,firebase_messaging,http— native code is bundled (noflutter_local_notifications) - The main methods never throw; results are returned as
PkbFcmResult
Requirements
| Minimum | Notes | |
|---|---|---|
| Flutter / Dart | 3.22 / 3.4 | |
| Android | minSdk 23, Kotlin 1.9 | Firebase requirements (Flutter 3.22-era apps default to Kotlin 1.7 — bump it in android/settings.gradle) |
| iOS | 15.0 | Firebase requirement; push needs a real device |
| Backend | see Backend API |
Setup (once per app)
- Add the package
flutter pub add pkb_fcm_notification - Connect the app to its Firebase project
dart pub global activate flutterfire_cli flutterfire configure --project=<firebase-project-id> - Android —
minSdk≥ 23, Kotlin ≥ 1.9. Optional white notification icon inAndroidManifest.xml(inside<application>):<meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/ic_notification" /> - iOS — in Xcode → Runner → Signing & Capabilities add Push Notifications and Background Modes → Remote notifications;
set
platform :ios, '15.0'inios/Podfile; upload an APNs authentication key (.p8) in Firebase → Project settings → Cloud Messaging (one key works for every app in your Apple team).
Usage
import 'package:pkb_fcm_notification/pkb_fcm_notification.dart';
import 'firebase_options.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await PkbFcm.init(
baseUrl: 'https://coop.example.com',
authToken: () => storage.read(key: 'token'), // the token your app already sends as Bearer
firebaseOptions: DefaultFirebaseOptions.currentPlatform,
);
runApp(const MyApp());
}
// after sign-in — shows the system permission dialog and registers the device
final result = await PkbFcm.login();
// sign-out — call BEFORE deleting the login token (it is needed for DELETE /device)
await PkbFcm.logout();
Navigate when a notification is tapped
PkbFcm.onOpened.listen((message) {
switch (message.screen) { // value of data['screen'] sent by the backend
case 'deposit': navigatorKey.currentState?.pushNamed('/deposit'); break;
default: navigatorKey.currentState?.pushNamed('/notifications');
}
});
A tap that launched the app is buffered and delivered as soon as you start listening.
Inbox (optional)
Navigator.push(context, MaterialPageRoute(builder: (_) => const PkbFcmInboxPage()));
final unread = await PkbFcm.unreadCount(); // for a badge
Or call the API directly: PkbFcm.api.inbox(page: 1), markRead(id), markAllRead().
Result of login()
result.status |
Meaning |
|---|---|
registered |
registered, notifications allowed |
permissionDenied |
registered, but the user denied notifications |
notLoggedIn |
authToken returned null/empty |
notInitialized |
init() not called or Firebase not configured (see result.error) |
noToken |
no FCM token — iOS: real device + Push capability + APNs key; Android: Google Play services |
failed |
backend call failed (see result.error, e.g. 401 = wrong auth guard on the server) |
Options (PkbFcmOptions)
| Option | Default | |
|---|---|---|
apiPrefix |
api/push |
backend route prefix |
topic |
all_members |
broadcast topic |
androidChannelId / androidChannelName |
coop_default / การแจ้งเตือน |
must match the channel the backend sends to |
showForegroundNotification |
true |
show notifications while the app is open |
markReadOnOpen |
true |
mark as read when tapped |
registerOnStart |
true |
re-register on launch if previously logged in |
timeout |
15 s | API timeout |
debug |
false |
print [pkb_fcm] ... logs |
Backend API
All endpoints are under {baseUrl}/{apiPrefix} (default /api/push), authenticated with
Authorization: Bearer <authToken>, JSON in/out. The server resolves the member from the token.
| Method | Path | Body / query | Response |
|---|---|---|---|
| POST | /device |
fcm_token, platform (android/ios), app_version, device_model, permission |
{"ok":true,"registered":true} |
| GET | /device?fcm_token= |
{"ok":true,"registered":bool,"device_count":int} |
|
| DELETE | /device |
fcm_token |
{"ok":true} |
| GET | /inbox?page= |
{"ok":true,"data":[{id,type,category,title,body,data,created_at,read}],"page","last_page","total"} |
|
| GET | /inbox/unread-count |
{"ok":true,"unread":int} |
|
| POST | /inbox/{id}/read |
{"ok":true} |
|
| POST | /inbox/read-all |
{"ok":true} |
Push payloads should include data.push_message_id (for read tracking) and optionally data.screen / data.category,
and use Android channel coop_default.
Troubleshooting
| Symptom | Fix |
|---|---|
Android build: Module was compiled with an incompatible version of Kotlin |
set Kotlin ≥ 1.9 |
iOS build: requires a higher minimum iOS deployment version |
platform :ios, '15.0' in ios/Podfile |
login() → noToken on iOS |
simulator, missing Push capability, or no APNs key in Firebase |
login() → failed 401 |
backend auth middleware does not match the app's guard |
| Android works, iOS does not | APNs key not uploaded, or Remote notifications not enabled |
Libraries
- pkb_fcm_notification
- Push Notification (FCM) สำหรับแอปสหกรณ์ — ใช้คู่กับ Laravel package
soat/laravel-soat-fcm-notification