From 3528a140b676da19686bc8c425363aeae65d84cf Mon Sep 17 00:00:00 2001 From: Gustl22 Date: Fri, 24 Nov 2023 13:28:55 +0100 Subject: [PATCH 01/10] docs: Fix typos from #1678 --- getting_started.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/getting_started.md b/getting_started.md index 36a00f61a..b3aeef8d2 100644 --- a/getting_started.md +++ b/getting_started.md @@ -3,7 +3,9 @@ This tutorial should help you get started with the audioplayers library, covering the basics but guiding you all the way through advanced features. You can also play around with our [official example app](https://bluefireteam.github.io/audioplayers/) and [explore the code](https://github.com/bluefireteam/audioplayers/tree/main/packages/audioplayers/example), that showcases every feature the library has to offer. -In order to install this package, add the [latest version](pub.dev/packages/audioplayers) of `audioplayers` to your `pubspec.yaml` file. This packages uses [the Federated Plugin](https://docs.flutter.dev/development/packages-and-plugins/developing-packages) guidelines to support multiple platforms, so it should just work on all supported platforms your app is built for without any extra configuration. You should not need to add the `audioplayers_*` packages directly. +In order to install this package, add the [latest version](pub.dev/packages/audioplayers) of `audioplayers` to your `pubspec.yaml` file. +This package uses [the Federated Plugin](https://docs.flutter.dev/development/packages-and-plugins/developing-packages) guidelines to support multiple platforms, so it should just work on all supported platforms your app is built for without any extra configuration. +You do not need to add the `audioplayers_*` packages directly. ## Setup Platforms @@ -182,7 +184,7 @@ You can pick one of 3 options: 1. `.error` (default): show only error messages 1. `.none`: show no messages at all (not recommended) -**Note**: before opening any issue, always try changing the log level to `.info` to gather any information that my assist you on solving the problem. +**Note**: before opening any issue, always try changing the log level to `.info` to gather any information that might assist you with solving the problem. **Note**: despite our best efforts, some native SDK implementations that we use spam a lot of log messages that we currently haven't figured out how to conform to this configuration (specially noticeable on Android). If you would like to contribute with a PR, they are more than welcome! From 0cf643aafbd9755d8369b127d1780bcaa5f9dd6a Mon Sep 17 00:00:00 2001 From: Gustl22 Date: Fri, 24 Nov 2023 13:56:47 +0100 Subject: [PATCH 02/10] docs: Update README of example --- packages/audioplayers/example/README.md | 20 ++++++++++++++----- .../example/lib/tabs/sources.dart | 2 +- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/packages/audioplayers/example/README.md b/packages/audioplayers/example/README.md index 3e4070f9b..2fa4baf8b 100644 --- a/packages/audioplayers/example/README.md +++ b/packages/audioplayers/example/README.md @@ -1,15 +1,25 @@ # AudioPlayer Example This is an example usage of audioplayers plugin. +Check out the live [example app](https://bluefireteam.github.io/audioplayers/) as demonstration. -It's a simple app with three tabs. - -- Remote Url: Plays audio from a remote url from the Internet. -- Local File: Downloads a file to your device in order to play it from your device. -- Local Asset: Play one of the assets bundled with this app. +It's a simple app with several tabs: +- **Src**: Manage audio sources. + - Url: Plays audio from a remote Url from the Internet. + - Asset: Play one of the assets bundled with this app. + - Device File: Play a file from your device from the specified path. + - Byte Array: Play from an array of bytes. +- **Ctrl**: Control playback, such as volume, balance and rate. +- **Stream**: Display of stream updates and properties. +- **Ctx**: Customize the audio context for mobile devices. +- **Log**: Display of logs. This example bundles a `PlayerWidget` that could be used as a very simple audio player interface. +## Setup + +In order to successfully run the example locally, you have to [set up](https://github.com/bluefireteam/audioplayers/blob/main/contributing.md#environment-setup) your environment with `melos`. + ## Dart Environment Variables Set the following variables as additional args `--dart-define MY_VAR=xyz`: diff --git a/packages/audioplayers/example/lib/tabs/sources.dart b/packages/audioplayers/example/lib/tabs/sources.dart index b3de1248e..62615c101 100644 --- a/packages/audioplayers/example/lib/tabs/sources.dart +++ b/packages/audioplayers/example/lib/tabs/sources.dart @@ -391,7 +391,7 @@ class _SourceDialogState extends State<_SourceDialog> { AssetSource: 'Asset', DeviceFileSource: 'Device File', UrlSource: 'Url', - BytesSource: 'Byte array', + BytesSource: 'Byte Array', }, selected: sourceType, onChange: (Type? value) { From d9f975f9e57b8ad0c451dc0c3baf3c5dd92e61b6 Mon Sep 17 00:00:00 2001 From: Gustl22 Date: Fri, 24 Nov 2023 14:01:41 +0100 Subject: [PATCH 03/10] docs: Change heading of CORS Policy in troubleshooting.md --- troubleshooting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/troubleshooting.md b/troubleshooting.md index 33d44dace..2e6a56e77 100644 --- a/troubleshooting.md +++ b/troubleshooting.md @@ -52,7 +52,7 @@ On Android, add `android:usesCleartextTraffic="true"` to your `AndroidManifest.x ``` -### [Web] CORS Policy +#### [Web] CORS Policy To be able to play your own resources on Web you need to make sure your server has CORS support enabled or [temporarily disable](https://stackoverflow.com/a/74783428/5164462) the security feature in your browser. From dd843aeb5a0cf29b10d44b96401ee3ec64e9777d Mon Sep 17 00:00:00 2001 From: Gustl22 Date: Fri, 24 Nov 2023 15:46:16 +0100 Subject: [PATCH 04/10] docs: Rework AudioCache and Local Assets docs --- getting_started.md | 31 +++++++++++++++++++++++++++++-- troubleshooting.md | 5 ++--- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/getting_started.md b/getting_started.md index b3aeef8d2..3988a20fc 100644 --- a/getting_started.md +++ b/getting_started.md @@ -31,8 +31,9 @@ Each AudioPlayer is created empty and has to be configured with an audio source The source (cf. packages/audioplayers/lib/src/source.dart) is basically what audio you are playing (a song, sound effect, radio stream, etc), and it can have one of 4 types: 1. **UrlSource**: get the audio from a remote URL from the Internet. This can be a direct link to a supported file to be downloaded, or a radio stream. -1. **DeviceFileSource**: access a file in the user's device, probably selected by a file picker -1. **AssetSource**: play an asset bundled with your app, normally within the `assets` directory +1. **DeviceFileSource**: access a file in the user's device, probably selected by a file picker. +1. **AssetSource**: play an asset bundled with your app, by default within the `assets` directory. + To customize the prefix, see [AudioCache](#audiocache). 1. **BytesSource** (only some platforms): pass in the bytes of your audio directly (read it from anywhere). In order to set the source on your player instance, call `setSource` with the appropriate source object: @@ -324,6 +325,32 @@ It works as a cache because it keeps track of the copied files so that you can r If desired, you can change the `AudioCache` per player via the `AudioPlayer().audioCache` property or for all players via `AudioCache.instance`. +#### Local Assets + +When playing local assets, by default every instance of AudioPlayers uses a [shared global instance of AudioCache](https://pub.dev/documentation/audioplayers/latest/audioplayers/AudioPlayer/audioCache.html), that will have a [default prefix "/assets"](https://pub.dev/documentation/audioplayers/latest/audioplayers/AudioCache/prefix.html) configured, as per Flutter conventions. +However, you can easily change that by specifying your own instance of AudioCache with any other (or no) prefix. + +Default behavior, presuming that your audio is stored in `/assets/audio/my-audio.wav`: +```dart +final player = AudioPlayer(); +await player.play(AssetSource('audio/my-audio.wav')); +``` + +Remove the asset prefix for all players: +```dart +AudioCache.instance = AudioCache(prefix: '') +final player = AudioPlayer(); +await player.play(AssetSource('assets/audio/my-audio.wav')); +``` + +Set a different prefix for only one player (e.g. when using assets from another package): +```dart +final player = AudioPlayer(); +player.audioCache = AudioCache(prefix: 'packages/OTHER_PACKAGE/assets/') +await player.play(AssetSource('other-package-audio.wav')); +``` + + ### playerId By default, each time you initialize a new instance of AudioPlayer, a unique playerId is generated and assigned to it using the [uuid package](https://pub.dev/packages/uuid). diff --git a/troubleshooting.md b/troubleshooting.md index 2e6a56e77..212e01ab6 100644 --- a/troubleshooting.md +++ b/troubleshooting.md @@ -69,10 +69,9 @@ If the issue persists, then open the issue, including the file so we can test. O ### Issues with local Assets and AudioCache -Flutter requires that assets are specified on your `pubspec.yaml` file, under `flutter > assets`; check [this](https://github.com/bluefireteam/audioplayers/blob/main/packages/audioplayers/example/pubspec.yaml#L29) for an example. +[Flutter requires](https://docs.flutter.dev/ui/assets/assets-and-images) that assets are specified on your `pubspec.yaml` file, under `flutter > assets`; check [this](https://github.com/bluefireteam/audioplayers/blob/main/packages/audioplayers/example/pubspec.yaml#L29) for an example. -**Note**: when playing local assets, by default every instance of AudioPlayers uses a [shared global instance of AudioCache](https://github.com/bluefireteam/audioplayers/blob/main/packages/audioplayers/lib/src/audioplayer.dart#L24), that will have a [default prefix "/assets"](https://github.com/bluefireteam/audioplayers/blob/main/packages/audioplayers/lib/src/audio_cache.dart#L41) configured, as per Flutter conventions. -However you can easily change that by specifying your own instance of AudioCache with any other (or no) prefix. +**Note**: Make sure you have set the path to your asset correctly, see the [AudioCache](https://github.com/bluefireteam/audioplayers/blob/main/getting_started.md#audiocache) concept. ### [iOS] Background Audio From ee21af603388f291d51ad9d2dc86120bcea3d5dd Mon Sep 17 00:00:00 2001 From: Gustl22 Date: Fri, 24 Nov 2023 16:50:20 +0100 Subject: [PATCH 05/10] Create example.dart --- packages/audioplayers/example/example.dart | 236 ++++++++++++++++++ .../example/lib/components/player_widget.dart | 7 +- 2 files changed, 240 insertions(+), 3 deletions(-) create mode 100644 packages/audioplayers/example/example.dart diff --git a/packages/audioplayers/example/example.dart b/packages/audioplayers/example/example.dart new file mode 100644 index 000000000..083279aab --- /dev/null +++ b/packages/audioplayers/example/example.dart @@ -0,0 +1,236 @@ +import 'dart:async'; + +import 'package:audioplayers/audioplayers.dart'; +import 'package:flutter/material.dart'; + +void main() { + runApp(const MaterialApp(home: _SimpleExampleApp())); +} + +class _SimpleExampleApp extends StatefulWidget { + const _SimpleExampleApp(); + + @override + _SimpleExampleAppState createState() => _SimpleExampleAppState(); +} + +class _SimpleExampleAppState extends State<_SimpleExampleApp> { + late AudioPlayer player = AudioPlayer(); + + @override + void initState() { + super.initState(); + + // Create the audio player. + player = AudioPlayer(); + + // Set the release mode to keep the source after playback has completed. + player.setReleaseMode(ReleaseMode.stop); + + // Start the player as soon as the app is displayed. + WidgetsBinding.instance.addPostFrameCallback((_) async { + await player.setSource(AssetSource('ambient_c_motion.mp3')); + await player.resume(); + }); + } + + @override + void dispose() { + // Release all sources and dispose the player. + player.dispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Simple Player'), + ), + body: PlayerWidget(player: player), + ); + } +} + +// This code is a copy of /lib/components/player_widget.dart. +//#region PlayerWidget + +class PlayerWidget extends StatefulWidget { + final AudioPlayer player; + + const PlayerWidget({ + required this.player, + super.key, + }); + + @override + State createState() { + return _PlayerWidgetState(); + } +} + +class _PlayerWidgetState extends State { + PlayerState? _playerState; + Duration? _duration; + Duration? _position; + + StreamSubscription? _durationSubscription; + StreamSubscription? _positionSubscription; + StreamSubscription? _playerCompleteSubscription; + StreamSubscription? _playerStateChangeSubscription; + + bool get _isPlaying => _playerState == PlayerState.playing; + + bool get _isPaused => _playerState == PlayerState.paused; + + String get _durationText => _duration?.toString().split('.').first ?? ''; + + String get _positionText => _position?.toString().split('.').first ?? ''; + + AudioPlayer get player => widget.player; + + @override + void initState() { + super.initState(); + // Use initial values from player + _playerState = player.state; + player.getDuration().then( + (value) => setState(() { + _duration = value; + }), + ); + player.getCurrentPosition().then( + (value) => setState(() { + _position = value; + }), + ); + _initStreams(); + } + + @override + void setState(VoidCallback fn) { + // Subscriptions only can be closed asynchronously, + // therefore events can occur after widget has been disposed. + if (mounted) { + super.setState(fn); + } + } + + @override + void dispose() { + _durationSubscription?.cancel(); + _positionSubscription?.cancel(); + _playerCompleteSubscription?.cancel(); + _playerStateChangeSubscription?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final color = Theme.of(context).primaryColor; + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + key: const Key('play_button'), + onPressed: _isPlaying ? null : _play, + iconSize: 48.0, + icon: const Icon(Icons.play_arrow), + color: color, + ), + IconButton( + key: const Key('pause_button'), + onPressed: _isPlaying ? _pause : null, + iconSize: 48.0, + icon: const Icon(Icons.pause), + color: color, + ), + IconButton( + key: const Key('stop_button'), + onPressed: _isPlaying || _isPaused ? _stop : null, + iconSize: 48.0, + icon: const Icon(Icons.stop), + color: color, + ), + ], + ), + Slider( + onChanged: (value) { + final duration = _duration; + if (duration == null) { + return; + } + final position = value * duration.inMilliseconds; + player.seek(Duration(milliseconds: position.round())); + }, + value: (_position != null && + _duration != null && + _position!.inMilliseconds > 0 && + _position!.inMilliseconds < _duration!.inMilliseconds) + ? _position!.inMilliseconds / _duration!.inMilliseconds + : 0.0, + ), + Text( + _position != null + ? '$_positionText / $_durationText' + : _duration != null + ? _durationText + : '', + style: const TextStyle(fontSize: 16.0), + ), + ], + ); + } + + void _initStreams() { + _durationSubscription = player.onDurationChanged.listen((duration) { + setState(() => _duration = duration); + }); + + _positionSubscription = player.onPositionChanged.listen( + (p) => setState(() => _position = p), + ); + + _playerCompleteSubscription = player.onPlayerComplete.listen((event) { + setState(() { + _playerState = PlayerState.stopped; + _position = Duration.zero; + }); + }); + + _playerStateChangeSubscription = + player.onPlayerStateChanged.listen((state) { + setState(() { + _playerState = state; + }); + }); + } + + Future _play() async { + final position = _position; + if (position != null && position.inMilliseconds > 0) { + await player.seek(position); + } + await player.resume(); + setState(() => _playerState = PlayerState.playing); + } + + Future _pause() async { + await player.pause(); + setState(() => _playerState = PlayerState.paused); + } + + Future _stop() async { + await player.stop(); + setState(() { + _playerState = PlayerState.stopped; + _position = Duration.zero; + }); + } +} + +//#endregion diff --git a/packages/audioplayers/example/lib/components/player_widget.dart b/packages/audioplayers/example/lib/components/player_widget.dart index 70424d3ac..01b76429e 100644 --- a/packages/audioplayers/example/lib/components/player_widget.dart +++ b/packages/audioplayers/example/lib/components/player_widget.dart @@ -3,6 +3,8 @@ import 'dart:async'; import 'package:audioplayers/audioplayers.dart'; import 'package:flutter/material.dart'; +// This code is also used in the example.md. Please keep it up to date. + class PlayerWidget extends StatefulWidget { final AudioPlayer player; @@ -106,12 +108,12 @@ class _PlayerWidgetState extends State { ], ), Slider( - onChanged: (v) { + onChanged: (value) { final duration = _duration; if (duration == null) { return; } - final position = v * duration.inMilliseconds; + final position = value * duration.inMilliseconds; player.seek(Duration(milliseconds: position.round())); }, value: (_position != null && @@ -129,7 +131,6 @@ class _PlayerWidgetState extends State { : '', style: const TextStyle(fontSize: 16.0), ), - Text('State: ${_playerState ?? '-'}'), ], ); } From b88245721fb625dedd6546b99f92400392966840 Mon Sep 17 00:00:00 2001 From: Gustl22 Date: Fri, 24 Nov 2023 17:05:07 +0100 Subject: [PATCH 06/10] Add example.md --- .../audioplayers/example/{example.dart => example.md} | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) rename packages/audioplayers/example/{example.dart => example.md} (93%) diff --git a/packages/audioplayers/example/example.dart b/packages/audioplayers/example/example.md similarity index 93% rename from packages/audioplayers/example/example.dart rename to packages/audioplayers/example/example.md index 083279aab..81996d6b9 100644 --- a/packages/audioplayers/example/example.dart +++ b/packages/audioplayers/example/example.md @@ -1,3 +1,9 @@ +Below example demonstrates a simple player app. + +A complete example showcasing all _audioplayers_ features can be found in our [repository](https://github.com/bluefireteam/audioplayers/tree/main/packages/audioplayers/example). +Also check out our live [web app](https://bluefireteam.github.io/audioplayers/). + +```dart import 'dart:async'; import 'package:audioplayers/audioplayers.dart'; @@ -53,7 +59,7 @@ class _SimpleExampleAppState extends State<_SimpleExampleApp> { } } -// This code is a copy of /lib/components/player_widget.dart. +// The PlayerWidget is a copy of "/lib/components/player_widget.dart". //#region PlayerWidget class PlayerWidget extends StatefulWidget { @@ -234,3 +240,4 @@ class _PlayerWidgetState extends State { } //#endregion +``` From 9052072c921271dd6a865486bfd79a1a657c39a0 Mon Sep 17 00:00:00 2001 From: Gustl22 Date: Sat, 25 Nov 2023 00:48:57 +0100 Subject: [PATCH 07/10] Clarify Low latency --- getting_started.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/getting_started.md b/getting_started.md index 3988a20fc..1c3e86577 100644 --- a/getting_started.md +++ b/getting_started.md @@ -79,7 +79,7 @@ Changes the current position (note: this does not affect the "playing" status). Stops the playback but keeps the current position. ```dart - await player.pause(); + await player.pause(); ``` ### stop @@ -167,7 +167,11 @@ The Player Mode represents what kind of native SDK is used to playback audio, wh 1. `.mediaPlayer` (default): for long media files or streams. 1. `.lowLatency`: for short audio files, since it reduces the impacts on visuals or UI performance. -**Note**: on low latency mode, the player won't fire any duration or position updates. Also, it is not possible to use the seek method to set the audio a specific position. +**Note**: on low latency mode, these features are NOT available: +- get duration & duration event +- get position & position event +- playback completion event (this means you are responsible for stopping the player, as it will not stop by itself) +- seeking & seek completion event Normally you want to use `.mediaPlayer` unless you care about performance and your audios are short (i.e. for sound effects in games). From af55d319740adf2efd2171d6472dfc7548a32af8 Mon Sep 17 00:00:00 2001 From: Gustl22 Date: Sat, 25 Nov 2023 01:04:36 +0100 Subject: [PATCH 08/10] Clarify Low latency --- getting_started.md | 2 +- .../lib/src/api/player_mode.dart | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/getting_started.md b/getting_started.md index 1c3e86577..5f60ca4c2 100644 --- a/getting_started.md +++ b/getting_started.md @@ -170,7 +170,7 @@ The Player Mode represents what kind of native SDK is used to playback audio, wh **Note**: on low latency mode, these features are NOT available: - get duration & duration event - get position & position event -- playback completion event (this means you are responsible for stopping the player, as it will not stop by itself) +- playback completion event (this means you are responsible for stopping the player) - seeking & seek completion event Normally you want to use `.mediaPlayer` unless you care about performance and your audios are short (i.e. for sound effects in games). diff --git a/packages/audioplayers_platform_interface/lib/src/api/player_mode.dart b/packages/audioplayers_platform_interface/lib/src/api/player_mode.dart index 8f70499ea..36d751ab9 100644 --- a/packages/audioplayers_platform_interface/lib/src/api/player_mode.dart +++ b/packages/audioplayers_platform_interface/lib/src/api/player_mode.dart @@ -8,8 +8,9 @@ enum PlayerMode { /// Ideal for short audio files, since it reduces the impacts on visuals or /// UI performance. /// - /// In this mode the backend won't fire any duration or position updates. - /// Also, it is not possible to use the seek method to set the audio a + /// In this mode the backend won't fire any duration, position or playback + /// completion events. This means you are responsible for stopping the player. + /// Also, it is not possible to use the seek method to set the audio to a /// specific position. lowLatency, } From dd4dd245312f3b225dd333fb2768281ceba2e8f5 Mon Sep 17 00:00:00 2001 From: Gustl22 Date: Sat, 25 Nov 2023 01:22:41 +0100 Subject: [PATCH 09/10] Troubleshooting: Analyze audio file format --- troubleshooting.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/troubleshooting.md b/troubleshooting.md index 212e01ab6..9b6d3c685 100644 --- a/troubleshooting.md +++ b/troubleshooting.md @@ -9,7 +9,10 @@ For that check out our [Contributing Guide](https://github.com/bluefireteam/audi ### Supported Formats / Encodings -Not all formats are supported by all platforms. Essentially `audioplayers` is just centralized interface that communicate with native audio players on each platform. We are not parsing the bytes of your song. Each platform has its own native support. Please do not open issues regarding encoding/file format compatibility unless it is an AudioPlayers specific issue. +Not all formats are supported by all platforms. +Essentially `audioplayers` is just centralized interface that communicate with native audio players on each platform. +We are not parsing the bytes of your song. Each platform has its own native support. +**Please do not open issues regarding encoding / audio format compatibility unless it is an AudioPlayers specific issue.** You can check a list of supported formats below: @@ -20,11 +23,15 @@ You can check a list of supported formats below: - [Windows](https://learn.microsoft.com/en-us/windows/win32/medfound/supported-media-formats-in-media-foundation) - Linux: List of defined [audio types](https://gstreamer.freedesktop.org/documentation/plugin-development/advanced/media-types.html?gi-language=c#table-of-audio-types) and their according [Plugins](https://gstreamer.freedesktop.org/documentation/plugins_doc.html?gi-language=c) +Also, there is no guarantee that the file extension matches the audio format. +A file encoded as Opus (`.ogg`) can easily be renamed to `.mp3`, but that doesn't mean it can be played by the platform's audio player. +Please verify that the real encoding / audio format is supported by analyzing the audio file (e.g. with [Aconvert](https://www.aconvert.com/analyze.html)). + ### Issues with remote URLs #### Unsafe HTTP -It is very common for mobile platforms to forbid non-HTTPS traffic due to it's lack of encryption and severe security deficiency. However, there are ways to bypass this protection. +It is very common for mobile platforms to forbid non-HTTPS traffic due to its lack of encryption and severe security deficiency. However, there are ways to bypass this protection. On iOS and macOS, edit your `.plist` and add: From 96d106b8d0989159183b1e6a8763bcdd46d4d514 Mon Sep 17 00:00:00 2001 From: Gustl22 Date: Sat, 25 Nov 2023 11:13:27 +0100 Subject: [PATCH 10/10] Apply suggestions from code review Co-authored-by: Lukas Klingsbo --- getting_started.md | 1 - packages/audioplayers/example/example.md | 2 +- packages/audioplayers/example/lib/components/player_widget.dart | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/getting_started.md b/getting_started.md index 5f60ca4c2..f4006ec23 100644 --- a/getting_started.md +++ b/getting_started.md @@ -354,7 +354,6 @@ player.audioCache = AudioCache(prefix: 'packages/OTHER_PACKAGE/assets/') await player.play(AssetSource('other-package-audio.wav')); ``` - ### playerId By default, each time you initialize a new instance of AudioPlayer, a unique playerId is generated and assigned to it using the [uuid package](https://pub.dev/packages/uuid). diff --git a/packages/audioplayers/example/example.md b/packages/audioplayers/example/example.md index 81996d6b9..21c206fbb 100644 --- a/packages/audioplayers/example/example.md +++ b/packages/audioplayers/example/example.md @@ -1,4 +1,4 @@ -Below example demonstrates a simple player app. +# Simple audio player app example A complete example showcasing all _audioplayers_ features can be found in our [repository](https://github.com/bluefireteam/audioplayers/tree/main/packages/audioplayers/example). Also check out our live [web app](https://bluefireteam.github.io/audioplayers/). diff --git a/packages/audioplayers/example/lib/components/player_widget.dart b/packages/audioplayers/example/lib/components/player_widget.dart index 01b76429e..ddc051a1d 100644 --- a/packages/audioplayers/example/lib/components/player_widget.dart +++ b/packages/audioplayers/example/lib/components/player_widget.dart @@ -4,7 +4,6 @@ import 'package:audioplayers/audioplayers.dart'; import 'package:flutter/material.dart'; // This code is also used in the example.md. Please keep it up to date. - class PlayerWidget extends StatefulWidget { final AudioPlayer player;