Getting to know Flutter: Simple audioplayer

theotherdevs

TheOtherDev/s

Posted on June 9, 2021

Getting to know Flutter: Simple audioplayer

Nowadays many apps have audio files inside to listen to, whether they are fitness, music or mental health apps. Let's see a basic approach to build an app with an audio player.

In this article we will focus on the audioplayers plugin but there are many other plugins to handle audios like audio_service.

So without further ado let's dive into coding! The first thing to do is to add the following packages to your pubspec.yamland then runflutter pub get.

dependencies:
  audioplayers: ^0.19.0
  http: ^0.13.3
  path_provider: ^2.0.2
Enter fullscreen mode Exit fullscreen mode

Notice that http and path_provider are required only if you need to download your audio files.

Platform Specific Setup

To allow HTTP connections to any site you will need few lines of code:

iOS

Edit the Info.plist file with:

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>
Enter fullscreen mode Exit fullscreen mode

Android

Edit AndroidManifest.xml file located in android/app/src/main/AndroidManifest.xml

<manifest ...>
    <uses-permission android:name="android.permission.INTERNET" />
    <application
        ...
        android:usesCleartextTraffic="true"
        ...>
        ...
    </application>
</manifest>
Enter fullscreen mode Exit fullscreen mode

Creating the widget

Now, let's start to create our Widget that will handle the audio; First of all, let's import the package in our widget:

import 'package:audioplayers/audioplayers.dart';
Enter fullscreen mode Exit fullscreen mode

then create a Stateful Widget that will accept a url as parameter and an isAsset that will be true only if the audio to be played will be inside assets:

class AudioPlayerWidget extends StatefulWidget {
  final String url;
  final bool isAsset;

  const AudioPlayerWidget({
    Key? key,
    required this.url,
    this.isAsset = false,
  }) : super(key: key);

  @override
  _AudioPlayerWidgetState createState() => _AudioPlayerWidgetState();
}
Enter fullscreen mode Exit fullscreen mode

In our _AudioPlayerWidgetState let's setup some properties:

  • the AudioPlayer itself
  • the AudioCache*, mandatory* if you need to reproduce an asset audio
  • our PlayerState that will store the current state of the AudioPlayer
  • a commodity computed variableisPlaying that we'll use to see wether the player is playing or not
  • another computed variable_isLocal that we'll use to see whether the url to play is local or not
late AudioPlayer _audioPlayer;
late AudioCache _audioCache;

PlayerState _playerState = PlayerState.STOPPED;

bool get _isPlaying => _playerState == PlayerState.PLAYING;
bool get _isLocal => !widget.url.contains('https');
Enter fullscreen mode Exit fullscreen mode

Since we've declared the _audioPlayer and _audioCache as late variables we are going to init them inside the initState method:

@override
  void initState() {
    _audioPlayer = AudioPlayer(mode: PlayerMode.MEDIA_PLAYER);
    _audioCache = AudioCache(fixedPlayer: _audioPlayer);
    AudioPlayer.logEnabled = true;

    _audioPlayer.onPlayerError.listen((msg) {
      print('audioPlayer error : $msg');
      setState(() {
        _playerState = PlayerState.STOPPED;
      });
    });
    super.initState();
  }

  @override
  void dispose() {
    _audioPlayer.dispose();
    super.dispose();
  }
Enter fullscreen mode Exit fullscreen mode

Again, this is a starter guide so I won't handle here duration, seek or background; just remember that if you are going to choose the PlayerMode.LOW_LATENCYyou won't be able to handle seek or duration updates since this mode is crafted for very short audio files.

The only stream that we're going to listen to will be onPlayerError, so, if an error is thrown for any reason, we will stop immediately the audio.

Please note: always remember to invoke the dispose() method to close all the streams!!

Play / Pause / Stop

Play/pause actions are managed by the _playPause() method which checks the player's status and invokes an action accordingly. We won't show the Stop method for the sake of brevity but the logic is exactly the same as for the method below.

_playPause() async {
    if (_playerState == PlayerState.PLAYING) {
      final playerResult = await _audioPlayer.pause();
      if (playerResult == 1) {
        setState(() {
          _playerState = PlayerState.PAUSED;
        });
      }
    } else if (_playerState == PlayerState.PAUSED) {
      final playerResult = await _audioPlayer.resume();
      if (playerResult == 1) {
        setState(() {
          _playerState = PlayerState.PLAYING;
        });
      }
    } else {
      if (widget.isAsset) {
        _audioPlayer = await _audioCache.play(widget.url);
        setState(() {
          _playerState = PlayerState.PLAYING;
        });
      } else {
        final playerResult = await _audioPlayer.play(widget.url, isLocal: _isLocal);
        if (playerResult == 1) {
          setState(() {
            _playerState = PlayerState.PLAYING;
          });
        }
      }
    }
  }
Enter fullscreen mode Exit fullscreen mode

Usage

The usage is pretty straightforward, just implement theAudioPlayerWidget inside you widget specifying for:

Assets

  • specify the filename + extension (e.g. my_audio.mp3) of the asset to reproduce
  • set isAsset variable to true

Remote

Set the audio widget's url property with the url of the audio.

Local

Download the audio from the url, save it and set the file path as the audio widget's url property.

Bonus 🥳

You don't how how to fetch and save an audio from a provided url? don't worry we are here for this Just set _loadFilePath() as the future of a FutureBuilder.

Future<String> _loadFilePath() async {
    final dir = await getApplicationDocumentsDirectory();
    final file = File('${dir.path}/the_name.mp3');

    if (await _assetAlreadyPresent(file.path)) {
      return file.path;
    }

    final bytes = await readBytes(Uri.parse(_remoteUrl));

    await file.writeAsBytes(bytes);

    return file.path;
  }

  Future<bool> _assetAlreadyPresent(String filePath) async {
    final File file = File(filePath);
    return file.exists();
  }
Enter fullscreen mode Exit fullscreen mode

Conclusions

As you may have noticed a simple implementation of an Audio Player is pretty straightforward and you won't have any problem to implement it in you app.

You can find the whole code inside this GitHub repo!

Where to go now?

You can dig deeper inside audioplayers plugin and do some experiments with seek and background handling or... just wait my next chapter of this Audio Player series see you soon!

Article written by the majestic Alessandro Viviani.

💖 💪 🙅 🚩
theotherdevs
TheOtherDev/s

Posted on June 9, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related