Flutter'da Push Notifications: FCM ve Local Notifications
Push bildirimler iki kaynaktan gelir: sunucudan gelen FCM (Firebase Cloud Messaging) bildirimleri ve cihazın kendi gönderdiği yerel bildirimler. Bu yazıda her ikisini de Flutter'da nasıl kuracağınızı, arka plan mesajlarını nasıl işleyeceğinizi ve bildirime tıklandığında nasıl yönlendirme yapacağınızı ele alacağız.
FCM Kurulumu
// pubspec.yaml
// firebase_core: ^3.6.0
// firebase_messaging: ^15.1.3
// flutter_local_notifications: ^17.2.2
// main.dart — Firebase başlatma
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
// Arka plan mesaj handler'ı — top-level fonksiyon olmalı
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
// iOS için izin iste
final messaging = FirebaseMessaging.instance;
await messaging.requestPermission(
alert: true,
badge: true,
sound: true,
provisional: false,
);
runApp(const MyApp());
}
// Top-level — sınıf üyesi olamaz
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
debugPrint('Arka plan mesajı: ${message.messageId}');
// Bildirim kaydı, badge güncelleme vb.
}
FCM Servis Sınıfı
class NotificationService {
final FirebaseMessaging _messaging = FirebaseMessaging.instance;
final FlutterLocalNotificationsPlugin _localNotifications =
FlutterLocalNotificationsPlugin();
Future<void> initialize() async {
// Local notifications başlat
await _localNotifications.initialize(
const InitializationSettings(
android: AndroidInitializationSettings('@mipmap/ic_launcher'),
iOS: DarwinInitializationSettings(
requestAlertPermission: false,
requestBadgePermission: false,
requestSoundPermission: false,
),
),
onDidReceiveNotificationResponse: _onNotificationTap,
);
// Android bildirim kanalı
await _localNotifications
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(const AndroidNotificationChannel(
'high_importance_channel',
'Önemli Bildirimler',
description: 'Kritik uygulama bildirimleri',
importance: Importance.high,
));
// Ön planda gelen FCM mesajını local notification olarak göster
FirebaseMessaging.onMessage.listen(_handleForegroundMessage);
// Uygulama kapalıyken bildirime tıklama
final initial = await _messaging.getInitialMessage();
if (initial != null) _handleNotificationNavigation(initial.data);
// Arka planda bildirime tıklama
FirebaseMessaging.onMessageOpenedApp.listen(
(message) => _handleNotificationNavigation(message.data),
);
}
Future<String?> getToken() => _messaging.getToken();
void _handleForegroundMessage(RemoteMessage message) {
final notification = message.notification;
if (notification == null) return;
_localNotifications.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
'high_importance_channel',
'Önemli Bildirimler',
icon: '@mipmap/ic_launcher',
importance: Importance.high,
priority: Priority.high,
styleInformation: BigTextStyleInformation(notification.body ?? ''),
),
iOS: const DarwinNotificationDetails(
presentAlert: true,
presentBadge: true,
presentSound: true,
),
),
payload: jsonEncode(message.data),
);
}
void _onNotificationTap(NotificationResponse response) {
if (response.payload == null) return;
final data = jsonDecode(response.payload!) as Map<String, dynamic>;
_handleNotificationNavigation(data);
}
void _handleNotificationNavigation(Map<String, dynamic> data) {
final type = data['type'] as String?;
final id = data['id'] as String?;
if (type == null || id == null) return;
switch (type) {
case 'product':
AppRouter.pushNamed('/products/$id');
case 'order':
AppRouter.pushNamed('/orders/$id');
case 'chat':
AppRouter.pushNamed('/messages/$id');
}
}
// Topic'e abone ol
Future<void> subscribeToTopic(String topic) =>
_messaging.subscribeToTopic(topic);
Future<void> unsubscribeFromTopic(String topic) =>
_messaging.unsubscribeFromTopic(topic);
}
Local Notifications: Zamanlanmış Bildirimler
class ScheduledNotificationService {
final FlutterLocalNotificationsPlugin _plugin =
FlutterLocalNotificationsPlugin();
// Belirli bir saatte bildirim gönder
Future<void> scheduleReminder({
required int id,
required String title,
required String body,
required DateTime scheduledTime,
String? payload,
}) async {
final tz = tz.TZDateTime.from(scheduledTime, tz.local);
await _plugin.zonedSchedule(
id,
title,
body,
tz,
NotificationDetails(
android: AndroidNotificationDetails(
'reminders',
'Hatırlatıcılar',
importance: Importance.high,
priority: Priority.high,
),
iOS: const DarwinNotificationDetails(),
),
payload: payload,
androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime,
);
}
// Her gün aynı saatte bildirim
Future<void> scheduleDailyAt({
required int id,
required String title,
required String body,
required Time time,
}) async {
await _plugin.periodicallyShowWithDuration(
id, title, body,
const Duration(days: 1),
NotificationDetails(
android: AndroidNotificationDetails('daily', 'Günlük'),
),
);
}
// Bildirimi iptal et
Future<void> cancel(int id) => _plugin.cancel(id);
Future<void> cancelAll() => _plugin.cancelAll();
// Bekleyen bildirimleri listele
Future<List<PendingNotificationRequest>> getPending() =>
_plugin.pendingNotificationRequests();
}
Android Manifest ve iOS Info.plist
<!-- android/app/src/main/AndroidManifest.xml -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<application>
<!-- FCM default notification icon -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_notification" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/notification_color" />
</application>
<!-- ios/Runner/Info.plist -->
<!-- Bu anahtarları ekleyin -->
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>remote-notification</string>
</array>
Push bildirim sistemi, arka plan handler'ının top-level fonksiyon olma zorunluluğu ve iOS izin akışı nedeniyle kurulumu en karmaşık Flutter özelliklerinden biridir. Testler için Firebase console'un test mesajı gönderme aracını ve cihaz FCM token'ını kullanın.