2

I want to play 1 (or more) scheduled short audio bells in the executeTask callback from the workmanager package when the user puts in the app in the background.

executeTask callback always fires when it should. However -- the audio does not play consistently when run in this callback. It only sometimes plays. But usually it just doesn't play. There are no errors either. I therefore suspect there is an issue with using async methods in the callback.


AudioPlayer player = AudioPlayer();

@pragma('vm:entry-point')
void callbackDispatcher() {
  Workmanager().executeTask((task, inputData) async {
    print("Native called background task: $task");
    try {

/// CALLBACK RUNS BUT AUDIO DOES NOT PLAY HERE

      await player.setAsset('assets/sample.mp3');
      await player.play();
    } catch (e) {
      developer.log(e.toString());
      throw Exception(e);
    }
    return Future.value(true);
  });
}

void main() {
  WidgetsFlutterBinding.ensureInitialized();

  Workmanager().initialize(callbackDispatcher);

  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
  bool _taskRegistered = false;

  @override
  void initState() {
    WidgetsBinding.instance.addObserver(this);
    super.initState();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.resumed ||
        state == AppLifecycleState.inactive) {
      _registerTask();
    }
  }

  /// etc.

Kdon
  • 892
  • 6
  • 19

1 Answers1

0

I have the same problem, and I'm trying different libs to try to solve and get the best result for may app. So far I could make it works with the lib AssetsAudioPlayer but I had to put a sleep(...) after the player open method because the workmanager kills the process before the end of the audio. Besides the sleep I had to do a kind of awake, because the first open I had the error (MissingPluginException(No implementation found for method stop on channel assets_audio_player), but for the next attempts it works good, so I had to call it in the initState() with volume 0 just to avoid the first error. Check my example below:

import 'package:assets_audio_player/assets_audio_player.dart';

const String simpleTaskKey = "com.dhdsolucoes.dhdfitnessflutter.vibrationTask";
const String audioInitializerTask = "com.dhdsolucoes.dhdfitnessflutter.audioInitializerTask";
final AssetsAudioPlayer assetsAudioPlayer = AssetsAudioPlayer.newPlayer();
final Audio androidAudioBell = Audio('assets/audio/bell_notification_right_pitch.mp3');

@pragma('vm:entry-point') // Mandatory if the App is obfuscated or using Flutter 3.1+
void callbackDispatcher() {
  if (Platform.isAndroid) {
    try {
      Workmanager().executeTask((task, inputData) async {
        if (task == simpleTaskKey) {
          try {
            await assetsAudioPlayer.open(androidAudioBell,
                autoStart: true,
                volume: 0.5,
                forceOpen: true,
                respectSilentMode: false,
                playInBackground: PlayInBackground.enabled,
                audioFocusStrategy: const AudioFocusStrategy.request(
                    resumeAfterInterruption: true,
                    resumeOthersPlayersAfterDone: true));
          } catch (e) {
            print('ERROR >>> $e');
          }
          sleep(const Duration(milliseconds: 1200));
        } else if (task == audioInitializerTask) {
          try {
            assetsAudioPlayer.open(androidAudioBell,
                autoStart: true,
                volume: 0,
                forceOpen: true,
                playInBackground: PlayInBackground.enabled,
                respectSilentMode: false);
          } catch (e) {}
          sleep(const Duration(seconds: 2));
        }
        return Future.value(true);
      });
    } catch (e) {}
  }
}

...

  @override
  void initState() {
    super.initState();
    Workmanager().registerOneOffTask(
        audioInitializerTask, audioInitializerTask, 
        initialDelay: const Duration(seconds: 1),
        existingWorkPolicy: ExistingWorkPolicy.replace);
  }

Take care about the sleep() time, it should be a little more than your audio lenght.