2

I've been looking everywhere about how to call a function in background (from main isolated process / foreground), but there's no result so far.

This is roughly the process I'm talking about:

import 'package:audioplayers/audioplayers.dart';

Future<void> main() async {
    WidgetsFlutterBinding.ensureInitialized();
    
    await Firebase.initializeApp();
    FirebaseMessaging.onBackgroundMessage(myBackgroundMessageHandler);

    runApp(MyApp());
}

Future<void> myBackgroundMessageHandler(RemoteMessage message) async {
    flutterLocalNotificationsPlugin.show(
        0,
        "Title",
        "Body",
        NotificationDetails(
          android: AndroidNotificationDetails(
            "channelid",
            "none",
            "none",
            priority: Priority.high,
          ),
        )
    );
    audioPlayer = await audioPlayerCache.loop("ring.mp3", isNotification: true);
}

void stopRing() async {
    await audioPlayer.stop();
}

When the audioPlayer is called in background, calling stopRing() function from foreground will NOT affect anything because - I think - they are in different isolated process.

Here is the flow: app receive firebase notification (in background) -> show notification (using flutter_local_notifications plugin) and play sound -> user tap the notification -> wakeup the app -> call stopRing() (not working, the app is already in foreground).

So, my question is, how to call stopRing() in background from foreground process, or probably just simply terminate the background process, I guess that will stop the ring, too.

Thanks in advance...

(Note: everything else is working well, except the problem mentioned in this post)

Pitu Werno
  • 93
  • 5

1 Answers1

0

I was also implementing the exact feature you mentioned, and facing the same challenge. I managed to solve it using IsolateNameServer to communicate with isolated process.


In your background process, setup listener to stop the audio player as below:

import 'dart:isolate';
import 'dart:ui';

Future<void> myBackgroundMessageHandler(RemoteMessage message) async {
    AudioPlayer audioPlayer = new AudioPlayer();

    flutterLocalNotificationsPlugin.show(
        0,
        "Title",
        "Body",
        NotificationDetails(
          android: AndroidNotificationDetails(
            "channelid",
            "none",
            "none",
            priority: Priority.high,
          ),
        )
    );


    final SendPort? sendStopBeep = IsolateNameServer.lookupPortByName('stop_beep_send_port');
    if (sendStopBeep == null) {
        ReceivePort portStopBeep = ReceivePort();
        IsolateNameServer.registerPortWithName(portStopBeep.sendPort, 'stop_beep_send_port');
        
        portStopBeep.listen((dynamic data) {
            // You should stop the audio player here
            await audioPlayer.stop();
            IsolateNameServer.removePortNameMapping('stop_beep_send_port');
        });

        // You should start the audio player here
        audioPlayer = await audioPlayerCache.loop("ring.mp3", isNotification: true);
    }
}

When your app state resumes (or wakes your app), trigger the stop audio player event to the above port listener:

class _HomeState extends State<Home> with WidgetsBindingObserver {
    @override
    void initState() {
        super.initState();
        WidgetsBinding.instance.addObserver(this);
    }

    @override
    void dispose() {
        WidgetsBinding.instance.removeObserver(this);
        super.dispose();
    }

    @override
    Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
        if (state == AppLifecycleState.resumed) {
            final SendPort? send = IsolateNameServer.lookupPortByName('stop_beep_send_port');
            if (send != null) {
                send.send(['test', 1]); // You can send any object over, ['test', 1] is just an example
            }
        }
    } 
}

Hope this helps!

Boon
  • 687
  • 4
  • 12
  • 25