Flutter how to pass messages to the isolate created by Android AlarmManager

Issue

I am using Android AlarmManger to set a oneShotAt alarm at a particular date and time. I am playing a looping sound with the help of FlutterRingtonePlayer as soon as the alarm is activated.

class _MyHomePageState extends State<MyHomePage> {
  TimeOfDay _time = TimeOfDay.now();
  DateTime _date = DateTime.now();
  final int helloAlarmID = 0;

  static playLocalAsset() {
    final DateTime now = DateTime.now();
    final int isolateId = Isolate.current.hashCode;
    print("[$now] Hello, world! isolate=${isolateId}");
    FlutterRingtonePlayer.playAlarm(looping: true);
  }

  static stopLocalAsset() {
    print("TEST");
    FlutterRingtonePlayer.stop();
  }
  void stopMusic() async {
    AndroidAlarmManager.oneShot(Duration(seconds: 0), helloAlarmID, stopLocalAsset);
  }
  //Date and Time Picking
  Future<Null> selectTime(BuildContext context) async {
    await selectDate(context);
    _time = await showTimePicker(context: context, initialTime: _time);
    _date = DateTime(_date.year, _date.month, _date.day, _time.hour, _time.minute);
    print(_date);
    await AndroidAlarmManager.oneShotAt(_date, helloAlarmID, playLocalAsset); //ALARM SET HERE
  }

  Future<Null> selectDate(BuildContext context) async {
    _date = await showDatePicker(context: context, initialDate: _date, firstDate: _date, lastDate: DateTime(2222));
  }

So alarm is set perfectly and it rings on exact time set. However, when I want to stop the music, I am unable to stop it. So I had to use another oneShot of AlarmManager in order to access the isolate and stop the music as seen in the code above. This stops the music, but it takes more than 3 seconds just to stop the music.

My question is, how do I make the AlarmManger stop the music IMMEDIATELY as soon as the stop button is pressed.

Thank You,
Jack

EDIT: Removed a line that was kept for testing.

Solution

So after searching through the web I finnally found a way to stop the player immediately after you press.

As AlarmManger creates an isolate, you must create a recieve port inside callback function of the AlarmManger.In my case it is static playLocalAsset()

static playLocalAsset() {
    ReceivePort rcPort = new ReceivePort();
    IsolateNameServer.registerPortWithName(rcPort.sendPort, portName);
    rcPort.listen((v) {
      FlutterRingtonePlayer.stop();
    });
    final DateTime now = DateTime.now();
    final int isolateId = Isolate.current.hashCode;
    print("[$now] Hello, world! isolate=${isolateId}");
    FlutterRingtonePlayer.playAlarm(looping: true);
  }

create a portname outside all functions (a global variable per say) like

const String portName = "ConnectingIsolate";

Now in your stop button function create a SendPort which will send to the portname that you have declared globally. In my case, the final stopLocalAsset() will look like

void stopMusic() {
    SendPort sendPort = IsolateNameServer.lookupPortByName(portName);
    sendPort.send("Abcd");
  }

So, basically, when you send something through SendPort, the receiever port (which is in isolate) will listen and perform whatever operation that you have provided in rcport.listen function, which in my case is to stop the player.

Hope this helps.

Answered By – Jack621311

Answer Checked By – Cary Denson (FlutterFixes Admin)

Leave a Reply

Your email address will not be published.