在后台播放通知声音的方法(Flutter)

huangapple go评论61阅读模式
英文:

How to play notification sound in background (Flutter)

问题

以下是您要翻译的内容:

"The alarm sound only plays once when received an FCM push notification in background.

Instead of using the default sound, I play the custom alarm sound using audioplayer.
I want the alarm keeps playing and stops only when the app is opened / user taps the notification if an FCM push message is received in background. If it is a foreground message, I want it plays once or the user can stop it immediately if they tap the notification.

Right now if it is a foreground message, the user is able to stop the notification sound by tapping the notification or it stops on completed. For a background message, I play the sound in background and it's supposed to loop if the user doesn't respond to it, however it only plays once and doesn't stop immediately when the app is brought to foreground.

I created a class for the audio player, and call the play() and stop() function in the FCM background/foreground message handler. The audio player object is created and initialised in main.dart initState.

AlarmPlayer.dart

import 'package:audioplayers/audioplayers.dart';

enum notiState { background, foreground, unknown }

class AlarmPlayer {
  late bool isplaying;
  late bool isResponded;
  late AudioPlayer player;
  late notiState state;

  AlarmPlayer() {
    isplaying = false;
    isResponded = true;
    player = AudioPlayer();
    state = notiState.unknown;
  }

  void init() {
    player.onPlayerStateChanged.listen((event) {
      switch (event) {
        case PlayerState.playing:
          print("Player event: playing");
          isplaying = true;
          break;
        case PlayerState.stopped:
          print("Player event: stopped");
          isplaying = false;
          break;
        case PlayerState.completed:
          print("Player event: completed");
          isplaying = false;
          if (!isResponded && state == notiState.background) {
            print("Play again");
            playAlarm();
          }
          break;
        default:
          break;
      }
    });
  }

  Future<void> playAlarm() async {
    if (!isplaying) {
      print("Alarm started");
      await player.play(AssetSource('audio/emergency.mp3'));
    }
  }

  Future<void> stopAlarm() async {
    print("Alarm stopped");
    await player.stop();
  }
}

FCM background message handler

Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  // If you're going to use other Firebase services in the background, such as Firestore,
  // make sure you call `initializeApp` before using other Firebase services.
  await Firebase.initializeApp();

  final String? msgTitle = message.notification!.title;
  final String? msgBody = message.notification!.body;
  if (msgTitle != null && msgBody != null) {
    print("Handling a background message: ${message.messageId}");
    print("onBackgroundMessage $message");

    alarmPlayer.state = notiState.background;
    alarmPlayer.isResponded = false;
    alarmPlayer.playAlarm();
  }
}

On app opened

    FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) async {
      print("*****Opened notification*****");
      alarmPlayer.isResponded = true;
      alarmPlayer.stopAlarm();
    });

It seems that the onPlayerStateChanged listener is never triggered in background. Any clue what I'm missing? Please let me know if my question or information is unclear. Thanks."

英文:

The alarm sound only plays once when received an FCM push notification in background.

Instead of using the default sound, I play the custom alarm sound using audioplayer.
I want the alarm keeps playing and stops only when the app is opened / user taps the notification if an FCM push message is received in background. If it is a foreground message, I want it plays once or the user can stop it immediately if they tap the notification.

Right now if it is a foreground message, the user is able to stop the notification sound by tapping the notification or it stops on completed. For a background message, I play the sound in background and it's supposed to loop if the user doesn't respond to it, however it only plays once and doesn't stop immediately when the app is brought to foreground.

I created a class for the audio player, and call the play() and stop() function in the FCM background/foreground message handler. The audio player object is created and initialised in main.dart initState.

AlarmPlayer.dart

import &#39;package:audioplayers/audioplayers.dart&#39;;

enum notiState { background, foreground, unknown }

class AlarmPlayer {
  late bool isplaying;
  late bool isResponded;
  late AudioPlayer player;
  late notiState state;

  AlarmPlayer() {
    isplaying = false;
    isResponded = true;
    player = AudioPlayer();
    state = notiState.unknown;
  }

  void init() {
    player.onPlayerStateChanged.listen((event) {
      switch (event) {
        case PlayerState.playing:
          print(&quot;Player event: playing&quot;);
          isplaying = true;
          break;
        case PlayerState.stopped:
          print(&quot;Player event: stopped&quot;);
          isplaying = false;
          break;
        case PlayerState.completed:
          print(&quot;Player event: completed&quot;);
          isplaying = false;
          if (!isResponded &amp;&amp; state == notiState.background) {
            print(&quot;Play again&quot;);
            playAlarm();
          }
          break;
        default:
          break;
      }
    });
  }

  Future&lt;void&gt; playAlarm() async {
    if (!isplaying) {
      print(&quot;Alarm started&quot;);
      await player.play(AssetSource(&#39;audio/emergency.mp3&#39;));
    }
  }

  Future&lt;void&gt; stopAlarm() async {
    print(&quot;Alarm stopped&quot;);
    await player.stop();
  }
}

FCM background message handler

Future&lt;void&gt; _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  // If you&#39;re going to use other Firebase services in the background, such as Firestore,
  // make sure you call `initializeApp` before using other Firebase services.
  await Firebase.initializeApp();

  final String? msgTitle = message.notification!.title;
  final String? msgBody = message.notification!.body;
  if (msgTitle != null &amp;&amp; msgBody != null) {
    print(&quot;Handling a background message: ${message.messageId}&quot;);
    print(&quot;onBackgroundMessage $message&quot;);

    alarmPlayer.state = notiState.background;
    alarmPlayer.isResponded = false;
    alarmPlayer.playAlarm();
  }
}

On app opened

    FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) async {
      print(&quot;*****Opened notification*****&quot;);
      alarmPlayer.isResponded = true;
      alarmPlayer.stopAlarm();
    });

It seems that the onPlayerStateChanged listener is never triggered in background. Any clue what I'm missing? Please let me know if my question or information is unclear. Thanks.

答案1

得分: 0

只找到一个简单的方法来做到这一点。我从FCM消息中删除了“notification”,因此Flutter应用程序不会将其视为通知,而是普通的远程消息,它不会触发任何弹出通知或声音。在后台消息处理程序中,只需实现具有唯一通道ID和自定义声音的Flutter本地通知功能。如果您希望它是一个持续的通知,请将附加标志设置为4。声音和振动会在您与通知互动的那一刻停止。

在下面了解有关Flutter本地通知的更多信息:
https://pub.dev/packages/flutter_local_notifications/example

英文:

Just found a simple way to do that. I remove the "notification" from the FCM message so the the flutter app doesn't consider it as a notification but a normal remote message, it won't trigger any popup notification or sound. In the background message handler, just simply implement the flutter local notification function with a unique channel id and custom sound. If you want it an insistent notification, set the additional flag to 4. The sound and vibration stop at the moment you interact with the notification.

Find more about flutter local notification below:
https://pub.dev/packages/flutter_local_notifications/example

huangapple
  • 本文由 发表于 2023年5月24日 21:04:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/76323868.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定