9.4 推送通知

推送通知是 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

👉 下一节继续阅读后续章节

相关推荐
神奇的程序员5 小时前
开发了一个管理本地开发环境的软件
前端·flutter
xmdy58666 小时前
Flutter+开源鸿蒙实战|智联邻里Day9 系统权限适配+应用全局分享+缓存深度优化+版本更新弹窗
flutter·开源·harmonyos
maaath10 小时前
【maaath】Flutter for OpenHarmony 乐器学习应用开发实战
flutter·华为·harmonyos
maaath15 小时前
【maaath】 Flutter for OpenHarmony 实战:电池优化应用开发指南
flutter·华为·harmonyos
勤劳打代码16 小时前
Flutter 架构日记 —— 可演进的 Flutter Dialog 组件
flutter·架构
Eric_HYD19 小时前
Flutter 字体字生效原理解析
flutter
maaath20 小时前
【无标题】Flutter for OpenHarmony 的文具手账应用开发实践
flutter·华为·harmonyos
里欧跑得慢20 小时前
Flutter 主题管理:构建一致的用户界面
前端·css·flutter·web
liulian09161 天前
Flutter for OpenHarmony 跨平台开发:单位转换功能实战指南
flutter
千码君20161 天前
Trae:一些关于flutter和 go前后端开发构建的分享
android·flutter·gradle·android-studio·trae·vibe code