Flutter推送通知的声音会重复播放

移动开发 2026-07-08

我正在为我的推送通知实现自定义声音。问题在于,当推送通知发送时,声音会重复播放,直到我点击通知图标才会停止。

我只想让声音只播放一次,但我仍然不知道为什么会重复。

我在不同设备上进行了测试,但情况仍然一样。我不知道自己哪里做错了。

class NotificationController {
  static Future<void> initializeNotification() async {
    await AwesomeNotifications().removeChannel(moneyInChannelId);
    await AwesomeNotifications().removeChannel(normalTransactionChannelId);
    await AwesomeNotifications().removeChannel(actionNotificationId);

    await AwesomeNotifications().initialize(
      'resource://drawable/res_logo_white_bg',
      [


        NotificationChannel(
          channelKey: moneyInChannelId,
          channelName: 'Money Received',
          channelDescription: 'Alerts when money enters your account',

          playSound: true,
          soundSource: 'resource://raw/notification',

          enableVibration: true,
          vibrationPattern: Int64List.fromList([0, 300, 200, 300, 200, 600]),

          importance: NotificationImportance.Max,

          defaultPrivacy: NotificationPrivacy.Private,
          defaultColor: AppColor.kColor700,
          ledColor: AppColor.kColor700,

          onlyAlertOnce: false,
          groupAlertBehavior: GroupAlertBehavior.Children,
        ),

        NotificationChannel(
          channelKey: normalTransactionChannelId,
          channelName: transactionNotifyChannelName,
          channelDescription: channelDescription,

          playSound: true,
          soundSource: 'resource://raw/notification',

          importance: NotificationImportance.High,

          defaultPrivacy: NotificationPrivacy.Private,
          defaultColor: AppColor.kColor700,
          ledColor: AppColor.kColor700,

          onlyAlertOnce: false,
          groupAlertBehavior: GroupAlertBehavior.Children,
        ),

        NotificationChannel(
          channelKey: actionNotificationId,
          channelName: notificationWithActionChannelName,
          channelDescription: channelDescription,

          playSound: true,
          soundSource: 'resource://raw/notification',

          importance: NotificationImportance.High,

          defaultPrivacy: NotificationPrivacy.Private,
          defaultColor: AppColor.kColor700,
          ledColor: AppColor.kColor700,

          onlyAlertOnce: false,
          groupAlertBehavior: GroupAlertBehavior.Children,
        ),
      ],
      debug: true,
    );
  }

  static Future<void> requestPermissionToSendNotification() async {
    await AwesomeNotifications().requestPermissionToSendNotifications();
  }

  static Future<bool> _areNotificationsAllowed() async {
    return await AwesomeNotifications().isNotificationAllowed();
  }

  static Future<void> createTransactionNotification({
    required bool isMoneyIn,
    String? title,
    String? body,
  }) async {
    final isAllowed = await _areNotificationsAllowed();
    if (!isAllowed) return;

    await AwesomeNotifications().createNotification(
      content: NotificationContent(
        id: DateTime.now().millisecondsSinceEpoch.remainder(100000),
        channelKey: isMoneyIn ? moneyInChannelId : normalTransactionChannelId,
        title: title,
        body: body,
        summary: transactionNotifySummary,
        notificationLayout: NotificationLayout.BigText,

        fullScreenIntent: isMoneyIn,
        wakeUpScreen: isMoneyIn,
        category: isMoneyIn ? NotificationCategory.Alarm : null,
      ),
    );
  }

  static Future<void> createNewNotification({
    String? title,
    String? body,
  }) async {
    await createTransactionNotification(
      isMoneyIn: false,
      title: title,
      body: body,
    );
  }

  static Future<void> createNotificationWithAction({
    String? title,
    String? body,
    String? bigPicture,
    String? largeIcon,
  }) async {
    final isAllowed = await _areNotificationsAllowed();
    if (!isAllowed) return;

    await AwesomeNotifications().createNotification(
      content: NotificationContent(
        id: DateTime.now().millisecondsSinceEpoch.remainder(100000),
        channelKey: actionNotificationId,
        title: title,
        body: body,
        summary: actionNotifySummary,
        notificationLayout: NotificationLayout.BigText,
        fullScreenIntent: true,
        largeIcon: largeIcon,
        bigPicture: bigPicture,
      ),
      actionButtons: [
        NotificationActionButton(
          key: actionNotificationId,
          label: 'View',
          color: AppColor.kColor700,
        ),
      ],
    );
  }
}
 @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver;
    //Listen to foreground notification
    FirebaseMessaging.onMessage.listen(_firebaseMessagingForegroundHandler);
    //Registering a background function.
    FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
    //set a notification listener
    AwesomeNotifications().setListeners(
      onActionReceivedMethod: onActionReceivedMethod,
    );
  }

  @override
  Widget build(BuildContext context) {
    return ScreenUtilInit(
      designSize: const Size(720, 1280),
      builder: (context, child) {
        return MaterialApp(
          navigatorKey: PageCatholicPay.navigatorKey,
          navigatorObservers: [routeObserver],
          debugShowCheckedModeBanner: env == Environment.live ? false : true,
          theme: AppTheme.light(),
          initialRoute: AppRoutes.splashPath,
          // routes: {
          //   AppRoutes.notificationPath: (context) => const PageNotification(),
          //   AppRoutes.transactionsPath: (context) =>
          //       const PageTransactionsHistory(),
          // },
          onGenerateRoute: onGenerateRoute,
        );
      },
    );
  }

  Route<dynamic> onGenerateRoute(RouteSettings settings) {
    //routes to history
    switch (settings.name) {
      case AppRoutes.notificationPath:
        return CustomRoute(
          child: const PageNotification(
            poppable: true,
          ),
          animationStyle: PageAnimationStyle.fade,
        );
      default:
        return CustomRoute(
          child: const PageSplash(),
          animationStyle: PageAnimationStyle.fade,
        );
    }
  }

  Future<void> _firebaseMessagingForegroundHandler(
      RemoteMessage message,) async {
    pp('foreground Notification was triggered');
    pp(message.data.toString());
    final data = message.data;
    final String? type = data['type'];

    final bool isMoneyIn = (type == 'Inflow') ||
        (type == 'Funding');

    NotificationController.createTransactionNotification(
      isMoneyIn: isMoneyIn,
      title: message.notification?.title ?? data['title'] ?? '',
      body: message.notification?.body ?? data['body'] ?? '',
    );

    pp(type);
    pp(isMoneyIn);

    // Refresh account
    sl<HomeRepository>().getAccount(const GetAccountParams());
  }
}

//triggered in the background
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  pp('background Notification was triggered');

  final data = message.data;
  final String? type = data['type'];

  final bool isMoneyIn = (type == 'Transaction') ||
      (type == 'Funding');

  NotificationController.createTransactionNotification(
    isMoneyIn: isMoneyIn,
    title: data['title'] ?? '',
    body: data['body'] ?? '',
  );

  pp(type);
  pp(isMoneyIn);

  sl<HomeRepository>().getAccount(const GetAccountParams());
}

// /// Use this method to detect when the user taps on a notification or action button
@pragma("vm:entry-point")
Future<void> onActionReceivedMethod(ReceivedAction receivedAction) async {
  if (receivedAction.channelKey == actionNotificationId) {
    pp('---------------Am been called int e onAction method');
    // Navigate to notifications
    PageCatholicPay.navigatorKey.currentState?.push(
      CustomRoute(
        child: const PageNotification(),
        animationStyle: PageAnimationStyle.fade,
      ),
    );
  }
}

解决方案

你使用了 NotificationCategory.Alarm 搭配 fullScreenIntent: true

在Android上,将 Alarm 分类分配给推送通知,会指示移动操作系统将传入的推送通知像晨间闹钟或来电一样处理,从而使系统无限循环自定义音频资源,直到用户进行交互为止。

将分类从 NotificationCategory.Alarm 更改为诸如 NotificationCategory.StatusNotificationCategory.Notification 之类的标准分类类型(或完全省略它)。

看看是否有效。

在你的 NotificationController 中更新你的 createTransactionNotification 方法:

static Future<void> createTransactionNotification({
    required bool isMoneyIn,
    String? title,
    String? body,
  }) async {
    final isAllowed = await _areNotificationsAllowed();
    if (!isAllowed) return;

    await AwesomeNotifications().createNotification(
      content: NotificationContent(
        id: DateTime.now().millisecondsSinceEpoch.remainder(100000),
        channelKey: isMoneyIn ? moneyInChannelId : normalTransactionChannelId,
        title: title,
        body: body,
        summary: transactionNotifySummary,
        notificationLayout: NotificationLayout.BigText,

        // 1. If you don't need a full screen takeover, turn this off
        fullScreenIntent: false, 
        wakeUpScreen: isMoneyIn,

        // 2. Change the category here to stop the looping behavior
        category: isMoneyIn ? NotificationCategory.Status : null, 
      ),
    );
  }
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章