Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Keep music app alive in background, iOS and Android?

I am building a music playing app using Flutter. It helps me as a single developer to build an app once for both platforms. However there are a few hiccups I have hit on both platforms respectivly. Although I have overcome some of these road bumps, I just can't figure out a major bug/issue that just takes the purpose out of making a music app. Music doesn't change if the app is not in the foreground.

I am using the package: audioplayers (https://pub.dartlang.org/packages/audioplayers). My app is streaming songs, the mp3 files are hosted online and have individual links.

When the first song completes playback, AudioPlayerState.COMPLETED. I make a call to play, with the new songs url. This works fine if the app is in the foreground but it doesn't work if the app is in the background. This happens on the latest version of iOS and I have caught this on Android 5.0 (not on Android 8.0+). In the RUN tab of Android Studio when I test this, it does show me the call is made, but song doesn't play however when I open app it does show updated album art, which is located on a url also (not in the song metadata). If I call resume, after opening app back up from the background, song starts to play.

I don't know if this is an issue with the package or just how it is with iOS, I have opened an issue on the package Github. However I believe it is something with iOS as before it wasn't playing the song if the app was minimized until I check the background audio in Xcode.

import 'package:flutter/material.dart';
import 'package:audioplayers/audioplayers.dart';
import 'dart:math';

AudioPlayer audioPlayer = new AudioPlayer();

class MusicPlay {
  MusicPlay() {
    _initAudioPlayer();
  }

  play(String audioURL) async {
    int result = await audioPlayer.play(audioURL);
    if (result == 1) {
      // success
    }
  }

  nextSong() async {
    play(String nextAudioURL)
  }

  void _initAudioPlayer() {
    audioPlayer.audioPlayerStateChangeHandler = (AudioPlayerState state) {
      switch (state) {
        case AudioPlayerState.PLAYING:
          break;
        case AudioPlayerState.PAUSED:
          break;
        case AudioPlayerState.STOPPED:
          break;
        case AudioPlayerState.COMPLETED:
          nextSong();
          break;
      }
    };
  }
}
like image 741
punjabi4life Avatar asked Feb 12 '19 09:02

punjabi4life


2 Answers

Solution is a one liner on the iOS Side.

You need this code in either your first view controller's init or viewDidLoad method:

[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];

https://stackoverflow.com/a/9721032/6024667

Swift 3 UIApplication.sharedApplication().beginReceivingRemoteControlEvents()

Swift 4 UIApplication.shared.beginReceivingRemoteControlEvents()

Add this code to AppDelegate.m/AppDelegate.swift inside your iOS/Runner directory, just before the return.

like image 196
punjabi4life Avatar answered Nov 15 '22 09:11

punjabi4life


You just need to enable "Audio, Airplay and Picture in Picture" in Xcode as shown below. You need to add the Capability "Background Modes". You can find this in the "Signing & Capabilities" tab:

enter image description here

A detailed instruction you can find in the offical docs: https://developer.apple.com/documentation/avfoundation/media_assets_playback_and_editing/creating_a_basic_video_player_ios_and_tvos/enabling_background_audio

like image 45
SwiftiSwift Avatar answered Nov 15 '22 08:11

SwiftiSwift