0

Im trying to use plugin Android Alarm Manager for timer and code execution in background, but cant really get it working right. If i set something like "print("v")" as a callback - everything works fine, but when Im trying to do something extra, it just doesn't work.

import 'package:flutter/material.dart';
import 'dart:async';
import 'package:android_alarm_manager/android_alarm_manager.dart';
import 'dart:isolate';

class MyApp extends StatefulWidget {
  _MyAppState createState() => _MyAppState();
}



startTimer(sendport) async {
  await AndroidAlarmManager.oneShot(
      Duration(seconds: 60), 0, timerCallback(sendport),
      wakeup: true, exact: true);
}

timerCallback(sendport) {
  sendport.send("DONE");
}

class _MyAppState extends State<MyApp> {
  ReceivePort receivePort = ReceivePort();
  SendPort sendport;

  @override
  void initState() {
    super.initState();
    AndroidAlarmManager.initialize();
    receivePort.listen((v) {
      print(v);
    });
  }

  @override
  Widget build(BuildContext context) {
    RaisedButton(
      onPressed: startTimer(sendport),
      child: Text("Start"),
    );
  }
}

I expect that code to send message after 1 minute, instead im getting message right after execution and get Error "/flutter (11424): [ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: 'dart:ui/plugins.dart': Failed assertion: line 62: '': 'callback' must not be null."

Ruslan
  • 433
  • 1
  • 4
  • 7

1 Answers1

0

In the question code, startTimer() calls timerCallup (sendPort) when setting up the oneShot, and so it's happening immediately.

The timerCallback function required by the oneShot call does not take any parameters. When the timerCallback runs at the alarm time, it runs in the context of an "isolate", which means it is isolated from the rest of your application. Constants seem to be accessible but variable values are not. The timerCallback function can talk to your app via the ports to exchange information and can use limited APIs to talk the the system, but that's about it. Check the docs on isolates for details.

Isolates can find one another by registering and looking up names with IsolateNameServer

You need a name for the port which the main app and the callback isolate can both see, a global constant seems to work ok for that.

The timer callback passed to AlarmManager.oneShot doesn't have a parameter, so it needs to look up the port name to find the SendPort to use when it runs.

Register the port name for the main isolate's SendPort when setting up - you still need the ReceivePort in MyAppState, but you don't need the SendPort.

The code below registers the receive port with a name, the timerCallback looks that name up, and then sends the message to the send port belonging to the receive port. It does feel very clunky.

import 'package:flutter/material.dart';
import 'dart:async';
import 'package:android_alarm_manager/android_alarm_manager.dart';
import 'dart:isolate';
import 'dart:ui';

const String portName = "MyAppPort";

class MyApp extends StatefulWidget {
    _MyAppState createState() => _MyAppState();
}



startTimer() async {
    await AndroidAlarmManager.oneShot(
        Duration(seconds: 60), 0, timerCallback,
        wakeup: true, exact: true);
}

timerCallback() {
    SendPort sendPort = IsolateNameServer.lookupPortByName(portName);
    if (sendPort != null) {
        sendport.send("DONE");
    }
}

class _MyAppState extends State<MyApp> {
    ReceivePort receivePort = ReceivePort();

    @override
    void initState() {
        super.initState();
        IsolateNameServer.registerPortName(receivePort.sendPort, portName)
        AndroidAlarmManager.initialize();
        receivePort.listen((v) {
            print(v);
        });
    }

    @override
    Widget build(BuildContext context) {
        RaisedButton(
            onPressed: startTimer(),
            child: Text("Start"),
        );
  }
Bill99
  • 395
  • 2
  • 8
  • I have same problem, I have this Callback function that has a parameter in it and when I put it on the AndroidAlarmManager.oneShot(....myfunction(parameter)), it will cause an error – Aquiko Mar 02 '21 at 15:24
  • @Ruslan just want to execute the function with parameter, I think that causes the problem. – Aquiko Mar 02 '21 at 15:26