推送通知是 App 召回用户、传递信息的重要手段。Flutter 中推送通知分为本地通知和远程推送两种,通常结合使用。
一、本地通知(flutter_local_notifications)
yaml
dependencies:
flutter_local_notifications: ^17.2.1
1.1 初始化
dart
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
class LocalNotificationService {
static final _plugin = FlutterLocalNotificationsPlugin();
static Future<void> initialize() async {
const androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher');
const iosSettings = DarwinInitializationSettings(
requestAlertPermission: true,
requestBadgePermission: true,
requestSoundPermission: true,
);
const settings = InitializationSettings(
android: androidSettings,
iOS: iosSettings,
);
await _plugin.initialize(
settings,
onDidReceiveNotificationResponse: _onNotificationTapped,
);
// 请求 Android 13+ 通知权限
await _plugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.requestNotificationsPermission();
}
// 用户点击通知时的回调
static void _onNotificationTapped(NotificationResponse response) {
final payload = response.payload;
if (payload != null) {
final data = jsonDecode(payload);
router.push('/order/${data['orderId']}'); // 跳转到对应页面
}
}
}
1.2 显示通知
dart
// 简单通知
static Future<void> showSimple({
required String title,
required String body,
String? payload,
}) async {
const androidDetails = AndroidNotificationDetails(
'default_channel',
'默认通知',
channelDescription: '应用默认通知渠道',
importance: Importance.high,
priority: Priority.high,
icon: '@mipmap/ic_launcher',
largeIcon: DrawableResourceAndroidBitmap('@mipmap/ic_launcher'),
);
const iosDetails = DarwinNotificationDetails(
presentAlert: true,
presentBadge: true,
presentSound: true,
);
const details = NotificationDetails(
android: androidDetails,
iOS: iosDetails,
);
await _plugin.show(
DateTime.now().millisecond, // 通知 ID
title,
body,
details,
payload: payload,
);
}
// 带进度条的通知(文件下载)
static Future<void> showProgress({
required int id,
required String title,
required int progress,
required int maxProgress,
}) async {
final androidDetails = AndroidNotificationDetails(
'download_channel',
'下载通知',
channelDescription: '文件下载进度',
importance: Importance.low,
showProgress: true,
maxProgress: maxProgress,
progress: progress,
ongoing: true, // 不可滑动删除
autoCancel: false,
);
await _plugin.show(id, title, '$progress%', NotificationDetails(android: androidDetails));
}
// 定时通知
static Future<void> scheduleNotification({
required String title,
required String body,
required DateTime scheduledTime,
}) async {
await _plugin.zonedSchedule(
0,
title,
body,
tz.TZDateTime.from(scheduledTime, tz.local),
const NotificationDetails(
android: AndroidNotificationDetails('reminder', '提醒通知'),
iOS: DarwinNotificationDetails(),
),
androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime,
);
}
二、Firebase Cloud Messaging(远程推送)
yaml
dependencies:
firebase_core: ^2.30.0
firebase_messaging: ^14.8.0
2.1 初始化 FCM
dart
import 'package:firebase_messaging/firebase_messaging.dart';
class PushNotificationService {
static final _messaging = FirebaseMessaging.instance;
static Future<void> initialize() async {
// 请求推送权限
final settings = await _messaging.requestPermission(
alert: true,
badge: true,
sound: true,
provisional: false,
);
debugPrint('Push permission: ${settings.authorizationStatus}');
// 获取 FCM Token(发送给后端保存)
final token = await _messaging.getToken();
debugPrint('FCM Token: $token');
await _sendTokenToServer(token);
// 监听 Token 刷新
_messaging.onTokenRefresh.listen(_sendTokenToServer);
// 前台消息处理
FirebaseMessaging.onMessage.listen(_handleForegroundMessage);
// 后台点击通知(App 从后台唤起)
FirebaseMessaging.onMessageOpenedApp.listen(_handleNotificationTap);
// 冷启动点击通知(App 完全关闭时)
final initialMessage = await _messaging.getInitialMessage();
if (initialMessage != null) {
_handleNotificationTap(initialMessage);
}
}
// 前台收到消息:显示本地通知
static void _handleForegroundMessage(RemoteMessage message) {
debugPrint('前台消息: ${message.notification?.title}');
if (message.notification != null) {
LocalNotificationService.showSimple(
title: message.notification!.title ?? '',
body: message.notification!.body ?? '',
payload: jsonEncode(message.data),
);
}
}
// 用户点击通知:跳转到对应页面
static void _handleNotificationTap(RemoteMessage message) {
final data = message.data;
switch (data['type']) {
case 'order':
router.push('/order/${data['orderId']}');
break;
case 'chat':
router.push('/chat/${data['roomId']}');
break;
case 'promotion':
router.push('/promotion/${data['promotionId']}');
break;
}
}
static Future<void> _sendTokenToServer(String? token) async {
if (token == null) return;
await ApiClient.post('/device/register', body: {'fcmToken': token});
}
// 主题订阅
static Future<void> subscribeToTopic(String topic) =>
_messaging.subscribeToTopic(topic);
static Future<void> unsubscribeFromTopic(String topic) =>
_messaging.unsubscribeFromTopic(topic);
}
2.2 后台消息处理
dart
// main.dart --- 后台消息处理器(必须是顶层函数)
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
debugPrint('后台消息: ${message.messageId}');
// 可以在此更新本地数据库、显示本地通知等
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
// 注册后台处理器
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
await PushNotificationService.initialize();
runApp(const MyApp());
}
三、通知渠道管理(Android)
dart
static Future<void> createNotificationChannels() async {
final androidPlugin = _plugin
.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>();
// 订单通知(高优先级,有声音)
await androidPlugin?.createNotificationChannel(
const AndroidNotificationChannel(
'order_channel',
'订单通知',
description: '订单状态变更通知',
importance: Importance.high,
sound: RawResourceAndroidNotificationSound('order_sound'),
),
);
// 活动通知(低优先级,静默)
await androidPlugin?.createNotificationChannel(
const AndroidNotificationChannel(
'promotion_channel',
'活动通知',
description: '促销活动推送',
importance: Importance.low,
),
);
}
四、角标管理
yaml
dependencies:
flutter_app_badger: ^1.5.0
dart
import 'package:flutter_app_badger/flutter_app_badger.dart';
// 设置角标
FlutterAppBadger.updateBadgeCount(unreadCount);
// 清除角标
FlutterAppBadger.removeBadge();
小结
| 功能 | 方案 |
|---|---|
| 本地通知 | flutter_local_notifications |
| 远程推送 | Firebase Cloud Messaging(FCM) |
| 前台通知 | 用本地通知展示远程消息 |
| 通知跳转 | 解析 payload/data → 路由跳转 |
| 后台处理 | onBackgroundMessage(顶层函数) |
| 角标 | flutter_app_badger |
👉 下一节继续阅读后续章节