just_audio_harmonyos 0.0.1
just_audio_harmonyos: ^0.0.1 copied to clipboard
HarmonyOS implementation of the just_audio plugin.
import 'package:audio_session/audio_session.dart';
import 'package:flutter/material.dart';
import 'package:just_audio/just_audio.dart';
import 'package:rxdart/rxdart.dart';
import 'common.dart';
void main() {
runApp(const App());
}
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: SafeArea(
child: const HomePage(),
),
),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
final _player = AudioPlayer();
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_init();
}
Future<void> _init() async {
// Inform the operating system of our app's audio attributes etc.
// We pick a reasonable default for an app that plays speech.
final session = await AudioSession.instance;
await session.configure(const AudioSessionConfiguration.speech());
// Listen to errors during playback.
_player.errorStream.listen((e) {
debugPrint('A stream error occurred: $e');
});
// Try to load audio from a source and catch any errors.
try {
// AAC example: https://dl.espressif.com/dl/audio/ff-16b-2c-44100hz.aac
await _player
.setAudioSource(AudioSource.uri(Uri.parse("https://s3.amazonaws.com/scifri-episodes/scifri20181123-episode.mp3")));
} on PlayerException catch (e) {
debugPrint("Error loading audio source: $e");
}
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
_player.dispose();
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.paused) {
_player.stop();
}
}
/// Collects the data useful for displaying in a seek bar, using a handy
/// feature of rx_dart to combine the 3 streams of interest into one.
Stream<PositionData> get _positionDataStream => Rx.combineLatest3<Duration, Duration, Duration?, PositionData>(
_player.positionStream,
_player.bufferedPositionStream,
_player.durationStream,
(position, bufferedPosition, duration) => PositionData(position, bufferedPosition, duration ?? Duration.zero));
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
ControlButtons(_player),
StreamBuilder<PositionData>(
stream: _positionDataStream,
builder: (context, snapshot) {
final positionData = snapshot.data;
return SeekBar(
duration: positionData?.duration ?? Duration.zero,
position: positionData?.position ?? Duration.zero,
bufferedPosition: positionData?.bufferedPosition ?? Duration.zero,
onChangeEnd: _player.seek,
);
},
),
],
);
}
}
/// Displays the play/pause button and volume/speed sliders.
class ControlButtons extends StatelessWidget {
final AudioPlayer player;
const ControlButtons(this.player, {super.key});
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(Icons.volume_up),
onPressed: () {
showSliderDialog(
context: context,
title: "Adjust volume",
divisions: 10,
min: 0.0,
max: 1.0,
value: player.volume,
stream: player.volumeStream,
onChanged: player.setVolume,
);
},
),
/// This StreamBuilder rebuilds whenever the player state changes, which
/// includes the playing/paused state and also the
/// loading/buffering/ready state. Depending on the state we show the
/// appropriate button or loading indicator.
StreamBuilder<PlayerState>(
stream: player.playerStateStream,
builder: (context, snapshot) {
final playerState = snapshot.data;
final processingState = playerState?.processingState;
final playing = playerState?.playing;
if (processingState == ProcessingState.loading || processingState == ProcessingState.buffering) {
return Container(
margin: const EdgeInsets.all(8.0),
width: 64.0,
height: 64.0,
child: const CircularProgressIndicator(),
);
} else if (playing != true) {
return IconButton(
icon: const Icon(Icons.play_arrow),
iconSize: 64.0,
onPressed: player.play,
);
} else if (processingState != ProcessingState.completed) {
return IconButton(
icon: const Icon(Icons.pause),
iconSize: 64.0,
onPressed: player.pause,
);
} else {
return IconButton(
icon: const Icon(Icons.replay),
iconSize: 64.0,
onPressed: () => player.seek(Duration.zero),
);
}
},
),
IconButton(
icon: const Icon(Icons.stop),
iconSize: 64.0,
onPressed: player.stop,
),
// Opens speed slider dialog
StreamBuilder<double>(
stream: player.speedStream,
builder: (context, snapshot) => IconButton(
icon: Text("${snapshot.data?.toStringAsFixed(1)}x", style: const TextStyle(fontWeight: FontWeight.bold)),
onPressed: () {
showSliderDialog(
context: context,
title: "Adjust speed",
divisions: 10,
min: 0.5,
max: 1.5,
value: player.speed,
stream: player.speedStream,
onChanged: player.setSpeed,
);
},
),
),
],
);
}
}