Firebase Push Notifications in Flutter – Complete 2026 Guide
Bilal Fali··2 min read

Firebase Cloud Messaging (FCM) is the standard push notification service for Flutter apps. This guide covers every notification state with production-ready code.
Setup
# pubspec.yaml
dependencies:
firebase_core: ^3.3.0
firebase_messaging: ^15.1.0
flutter_local_notifications: ^17.2.1
Android Configuration
<!-- AndroidManifest.xml inside <application> -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="high_importance_channel" />
The Notification Service
@pragma('vm:entry-point')
Future<void> _bgHandler(RemoteMessage message) async {
await Firebase.initializeApp();
await NotificationService.instance.showLocal(message);
}
class NotificationService {
NotificationService._();
static final instance = NotificationService._();
final _fcm = FirebaseMessaging.instance;
final _local = FlutterLocalNotificationsPlugin();
Future<void> init() async {
FirebaseMessaging.onBackgroundMessage(_bgHandler);
await _setupLocalNotifications();
await _requestPermission();
await _setupHandlers();
}
Future<void> _requestPermission() async {
await _fcm.requestPermission(alert: true, badge: true, sound: true);
await _fcm.setForegroundNotificationPresentationOptions(
alert: true, badge: true, sound: true,
);
}
Future<void> _setupLocalNotifications() async {
const android = AndroidInitializationSettings('@drawable/ic_notification');
const ios = DarwinInitializationSettings(
requestAlertPermission: false,
requestBadgePermission: false,
requestSoundPermission: false,
);
await _local.initialize(
const InitializationSettings(android: android, iOS: ios),
onDidReceiveNotificationResponse: _onTap,
);
const channel = AndroidNotificationChannel(
'high_importance_channel',
'High Importance Notifications',
importance: Importance.high,
);
await _local
.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
}
Future<void> _setupHandlers() async {
FirebaseMessaging.onMessage.listen(showLocal);
FirebaseMessaging.onMessageOpenedApp.listen(_navigate);
final initial = await _fcm.getInitialMessage();
if (initial != null) _navigate(initial);
}
Future<void> showLocal(RemoteMessage msg) async {
final n = msg.notification;
if (n == null) return;
await _local.show(
n.hashCode, n.title, n.body,
const NotificationDetails(
android: AndroidNotificationDetails(
'high_importance_channel', 'High Importance Notifications',
importance: Importance.high, priority: Priority.high,
),
iOS: DarwinNotificationDetails(presentAlert: true, presentSound: true),
),
payload: msg.data['route'],
);
}
void _onTap(NotificationResponse r) {
if (r.payload != null) _goTo(r.payload!);
}
void _navigate(RemoteMessage msg) {
final route = msg.data['route'];
if (route != null) _goTo(route);
}
void _goTo(String route) {
debugPrint('Navigate to $route');
}
Future<String?> getToken() => _fcm.getToken();
Future<void> subscribe(String topic) => _fcm.subscribeToTopic(topic);
}
Initialize in main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
await NotificationService.instance.init();
runApp(const MyApp());
}
FCM Payload Structure
{
"message": {
"token": "device_token",
"notification": { "title": "New Order", "body": "Order #1234 placed" },
"data": { "route": "/orders/1234", "orderId": "1234" },
"android": {
"notification": { "channel_id": "high_importance_channel" }
}
}
}
Topics (Broadcast to Groups)
await NotificationService.instance.subscribe('all_users');
await NotificationService.instance.subscribe('flutter_devs');
Common Issues
No notification on Android — Check channel ID matches manifest. No foreground on iOS — Implement UNUserNotificationCenterDelegate. Android 13+ blocked — Request POST_NOTIFICATIONS permission. Token null — Wait for Firebase init before calling getToken().