iFrong
iFrong

Reputation: 5

Flutter android_alarm_manager plugin can't pass a parameter to a callback function

I try to pass the parameter in this way, but it doesn't work

alarm() async {
  int i = 1;
  await AndroidAlarmManager.oneShot(
    Duration(seconds: 3),
    2,
    () => callback(i),
    wakeup: true,
    ).then((val) => print('set up:' + val.toString()));
}

and

alarm() async {
int i = 1;
await AndroidAlarmManager.oneShot(
  Duration(seconds: 3),
  2,
  () {
    callback(i);
  },
  wakeup: true,
).then((val) => print('set up:' + val.toString()));
}

in console log print like this :

I/flutter ( 9306): set up:false

callback function :

static Future<void> callback(int i) async {
print('Alarm fired!' + '$i');
uiSendPort ??= IsolateNameServer.lookupPortByName(isolateName);
uiSendPort?.send(null);
}

What Im doing wrong?

Upvotes: 0

Views: 838

Answers (1)

chunhunghan
chunhunghan

Reputation: 54367

Reason
from source code

/// `id` will passed to `callback` if it is of type `Function(int)`

https://github.com/flutter/plugins/blob/d72cebd235220000cc7b438c7b48c9a27e07699b/packages/android_alarm_manager/lib/android_alarm_manager.dart#L115

In your case, with correct use of callback, and id is 2, it will always print Alarm fired!2

alarm() async {
    int i = 1;
    await AndroidAlarmManager.oneShot(
      Duration(seconds: 3),
      2,
      callback,
      wakeup: true,
    ).then((val) => print('set up:' + val.toString()));
  }

Solution

/// The `callback` will run whether or not the main application is running or
/// in the foreground. It will run in the Isolate owned by the
/// AndroidAlarmManager service.

To allow callback get parameter, you can follow official example use SharedPreferences
code snippet

static Future<void> callback(int i) async {
    print('Alarm fired!' + '$i');

    // Get the previous cached count and increment it.
    final prefs = await SharedPreferences.getInstance();
    int currentCount = prefs.getInt(countKey) ?? 0;
    await prefs.setInt(countKey, currentCount + 1);

    // This will be null if we're running in the background.
    uiSendPort ??= IsolateNameServer.lookupPortByName(isolateName);
    uiSendPort?.send(null);
  }

full test code

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

import 'package:android_alarm_manager/android_alarm_manager.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter/material.dart';

/// The [SharedPreferences] key to access the alarm fire count.
const String countKey = 'count';

/// The name associated with the UI isolate's [SendPort].
const String isolateName = 'isolate';

/// A port used to communicate from a background isolate to the UI isolate.
final ReceivePort port = ReceivePort();

/// Global [SharedPreferences] object.
SharedPreferences prefs;

Future<void> main() async {
  // TODO(bkonyi): uncomment
  WidgetsFlutterBinding.ensureInitialized();

  // Register the UI isolate's SendPort to allow for communication from the
  // background isolate.
  IsolateNameServer.registerPortWithName(
    port.sendPort,
    isolateName,
  );
  prefs = await SharedPreferences.getInstance();
  if (!prefs.containsKey(countKey)) {
    await prefs.setInt(countKey, 0);
  }
  runApp(AlarmManagerExampleApp());
}

/// Example app for Espresso plugin.
class AlarmManagerExampleApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: _AlarmHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class _AlarmHomePage extends StatefulWidget {
  _AlarmHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _AlarmHomePageState createState() => _AlarmHomePageState();
}

class _AlarmHomePageState extends State<_AlarmHomePage> {
  int _counter = 0;

  @override
  void initState() {
    super.initState();
    AndroidAlarmManager.initialize();

    // Register for events from the background isolate. These messages will
    // always coincide with an alarm firing.
    port.listen((_) async => await _incrementCounter());
  }

  alarm() async {
    int i = 1;
    await AndroidAlarmManager.oneShot(
      Duration(seconds: 3),
      2,
      callback,
      wakeup: true,
    ).then((val) => print('set up:' + val.toString()));
  }

  Future<void> _incrementCounter() async {
    print('Increment counter!');

    // Ensure we've loaded the updated count from the background isolate.
    await prefs.reload();

    setState(() {
      _counter++;
    });
  }

  // The background
  static SendPort uiSendPort;

  // The callback for our alarm
  static Future<void> callback(int i) async {
    print('Alarm fired!' + '$i');

    // Get the previous cached count and increment it.
    final prefs = await SharedPreferences.getInstance();
    int currentCount = prefs.getInt(countKey) ?? 0;
    await prefs.setInt(countKey, currentCount + 1);

    // This will be null if we're running in the background.
    uiSendPort ??= IsolateNameServer.lookupPortByName(isolateName);
    uiSendPort?.send(null);
  }

  @override
  Widget build(BuildContext context) {
    final textStyle = Theme.of(context).textTheme.headline4;
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ElevatedButton(
                onPressed: () {
                  alarm();
                },
                child: Text('alarm')),
            Text(
              'Alarm fired $_counter times',
              style: textStyle,
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(
                  'Total alarms fired: ',
                  style: textStyle,
                ),
                Text(
                  prefs.getInt(countKey).toString(),
                  key: ValueKey('BackgroundCountText'),
                  style: textStyle,
                ),
              ],
            ),
            ElevatedButton(
              child: Text(
                'Schedule OneShot Alarm',
              ),
              key: ValueKey('RegisterOneShotAlarm'),
              onPressed: () async {
                await AndroidAlarmManager.oneShot(
                  const Duration(seconds: 5),
                  // Ensure we have a unique alarm ID.
                  Random().nextInt(pow(2, 31).toInt()),
                  callback,
                  exact: true,
                  wakeup: true,
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

Upvotes: 3

Related Questions