Flutter, Google’s UI toolkit, allows developers to build natively compiled applications for mobile, web, and desktop from a single codebase. When it comes to handling audio and video playback, the just_audio
package provides a powerful and extensible solution. In this blog post, we’ll dive deep into how to play audio and video using just_audio
in Flutter.
What is just_audio
?
just_audio
is a feature-rich Flutter package that allows you to play audio and video from various sources such as local files, network streams, assets, and more. It provides APIs to control playback, handle buffering, manage playlists, and respond to playback events. This package is highly extensible and suitable for both simple and complex audio/video playback requirements.
Why Use just_audio
?
- Versatile: Supports multiple audio formats and sources.
- Extensible: Easily customizable and extendable to fit various needs.
- Feature-rich: Offers extensive control over audio playback.
- Platform-independent: Works seamlessly on Android, iOS, web, and desktop platforms.
How to Play Audio and Video with just_audio
in Flutter
To get started with just_audio
, follow these steps:
Step 1: Add Dependency
First, add the just_audio
package to your pubspec.yaml
file:
dependencies:
flutter:
sdk: flutter
just_audio: ^0.9.36 # Use the latest version
After adding the dependency, run flutter pub get
to install the package.
Step 2: Basic Audio Playback
Let’s start with a simple example of playing an audio file from a URL.
import 'package:flutter/material.dart';
import 'package:just_audio/just_audio.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final _player = AudioPlayer();
@override
void initState() {
super.initState();
_init();
}
Future<void> _init() async {
// Load a URL
await _player.setUrl(
'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3');
}
@override
void dispose() {
_player.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Just Audio Example')),
body: Center(
child: ElevatedButton(
onPressed: () {
if (_player.playing) {
_player.pause();
} else {
_player.play();
}
},
child: Text(_player.playing ? 'Pause' : 'Play'),
),
),
),
);
}
}
Explanation:
- Import Statements: Includes necessary packages, such as
flutter/material.dart
andjust_audio/just_audio.dart
. - AudioPlayer Initialization: Creates an instance of
AudioPlayer
named_player
. - Initialization (
_init
): In theinitState
method, initializes theAudioPlayer
to load the audio file from the provided URL. - Playback Control: A button toggles between playing and pausing the audio based on the current state of the player.
- Disposal: The
dispose
method ensures resources are released when the widget is removed.
Step 3: Handling Playback State
To better manage and display the playback state (playing, paused, loading, stopped), you can listen to the player’s state changes.
import 'package:flutter/material.dart';
import 'package:just_audio/just_audio.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final _player = AudioPlayer();
bool _isLoading = true;
@override
void initState() {
super.initState();
_init();
}
Future<void> _init() async {
_player.playbackEventStream.listen((event) {},
onError: (Object e, StackTrace stackTrace) {
print('A stream error occurred: $e');
});
try {
await _player.setUrl(
'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3');
} catch (e) {
print("Error loading audio source: \$e");
} finally {
setState(() {
_isLoading = false;
});
}
}
@override
void dispose() {
_player.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Just Audio Example')),
body: Center(
child: _isLoading
? CircularProgressIndicator()
: ElevatedButton(
onPressed: () {
if (_player.playing) {
_player.pause();
} else {
_player.play();
}
},
child: Text(_player.playing ? 'Pause' : 'Play'),
),
),
),
);
}
}
Enhancements:
- Loading Indicator: Displays a
CircularProgressIndicator
while the audio is loading. - Error Handling: Implements basic error handling for loading the audio source.
- Playback State Management: Monitors and updates the UI based on the loading state.
Step 4: Playing Audio from Local Files
To play audio from a local file, you’ll need to first ensure that the file is accessible to your Flutter app. You can use the path_provider
package to get the device’s local storage directory, and then use just_audio
to load the file.
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:just_audio/just_audio.dart';
import 'package:path_provider/path_provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final _player = AudioPlayer();
bool _isLoading = true;
@override
void initState() {
super.initState();
_init();
}
Future<void> _init() async {
try {
final directory = await getApplicationDocumentsDirectory();
final file = File('${directory.path}/audio.mp3'); // Replace with your file
// For demonstration, create a dummy file if it doesn't exist
if (!file.existsSync()) {
await file.writeAsBytes([0, 0, 0]);
}
await _player.setFilePath(file.path);
} catch (e) {
print("Error loading audio source: \$e");
} finally {
setState(() {
_isLoading = false;
});
}
}
@override
void dispose() {
_player.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Just Audio Example')),
body: Center(
child: _isLoading
? CircularProgressIndicator()
: ElevatedButton(
onPressed: () {
if (_player.playing) {
_player.pause();
} else {
_player.play();
}
},
child: Text(_player.playing ? 'Pause' : 'Play'),
),
),
),
);
}
}
Key additions:
path_provider
Package: Used to get the application documents directory.- File Handling: Checks for the existence of the audio file and creates a dummy file if it doesn’t exist (for demonstration purposes).
setFilePath
: Loads the audio file from the specified file path.
Step 5: Playing Video
While just_audio
is primarily an audio player, it can be combined with other packages like video_player
for video playback. This combination can be used for playing video files with advanced audio controls provided by just_audio
.
First, add the video_player
package:
dependencies:
flutter:
sdk: flutter
just_audio: ^0.9.36
video_player: ^2.8.2 # Use the latest version
Here is a basic video playback implementation with just_audio
managing audio aspects:
import 'package:flutter/material.dart';
import 'package:just_audio/just_audio.dart';
import 'package:video_player/video_player.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late VideoPlayerController _videoController;
final _audioPlayer = AudioPlayer();
@override
void initState() {
super.initState();
_init();
}
Future<void> _init() async {
// Initialize Video Player
_videoController = VideoPlayerController.network(
'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4')
..initialize().then((_) {
setState(() {});
});
// Load audio (if video doesn't have audio track)
try {
await _audioPlayer.setUrl(
'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3');
} catch (e) {
print("Error loading audio source: \$e");
}
}
@override
void dispose() {
_videoController.dispose();
_audioPlayer.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Video and Audio Example')),
body: Center(
child: _videoController.value.isInitialized
? AspectRatio(
aspectRatio: _videoController.value.aspectRatio,
child: VideoPlayer(_videoController),
)
: CircularProgressIndicator(),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
if (_videoController.value.isPlaying) {
_videoController.pause();
_audioPlayer.pause();
} else {
_videoController.play();
_audioPlayer.play();
}
});
},
child: Icon(
_videoController.value.isPlaying ? Icons.pause : Icons.play_arrow,
),
),
),
);
}
}
Details:
- Video and Audio Controllers: Uses both
VideoPlayerController
(fromvideo_player
) andAudioPlayer
(fromjust_audio
). - Initialization: Initializes both the video and audio players.
- Synchronization: Playback is controlled by starting and stopping both the video and audio together.
Advanced Features of just_audio
- Playlists: You can create and manage playlists using
AudioPlayer
to play multiple audio files sequentially. - Playback Speed Control: Control the playback speed of audio using the
setSpeed
method. - Audio Effects: Apply audio effects like pitch and tempo adjustments.
- Buffering Management: Monitor and handle buffering events to provide a smooth playback experience.
// Example of using playlists
final _player = AudioPlayer();
final playlist = ConcatenatingAudioSource(children: [
AudioSource.uri(Uri.parse('https://example.com/audio1.mp3')),
AudioSource.uri(Uri.parse('https://example.com/audio2.mp3')),
]);
Future<void> setupPlaylist() async {
await _player.setAudioSource(playlist);
}
// Example of setting playback speed
Future<void> setPlaybackSpeed(double speed) async {
await _player.setSpeed(speed);
}
Conclusion
The just_audio
package offers a robust and flexible way to play audio and video in Flutter applications. Whether you’re building a simple music player or a complex multimedia app, just_audio
provides the necessary tools to deliver a great audio playback experience. By combining it with other packages like video_player
, you can handle various media playback scenarios effectively. Its extensive feature set, ease of use, and cross-platform compatibility make it an excellent choice for Flutter developers working with audio and video.