Compare commits
6 Commits
0cdb9a6c27
...
1ff98b7bbf
Author | SHA1 | Date | |
---|---|---|---|
1ff98b7bbf | |||
545d9a951f | |||
b11eb6063c | |||
aa3f743eee | |||
30aa018bed | |||
41feac8ef5 |
@ -3,12 +3,19 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:audioplayers/audioplayers.dart';
|
import 'package:chewie_audio/chewie_audio.dart';
|
||||||
import 'package:fluent_ui/fluent_ui.dart' as fluent;
|
import 'package:fluent_ui/fluent_ui.dart' as fluent;
|
||||||
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:music_web_player/api.dart';
|
import 'package:music_web_player/api.dart';
|
||||||
import 'package:music_web_player/ui/cover_image.dart';
|
import 'package:music_web_player/ui/cover_image.dart';
|
||||||
|
import 'package:video_player/video_player.dart';
|
||||||
|
|
||||||
|
extension DurationExt on Duration {
|
||||||
|
String get formatted {
|
||||||
|
return "$inMinutes:${(inSeconds % 60).toString().padLeft(2, '0')}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class MusicPlayer extends StatefulWidget {
|
class MusicPlayer extends StatefulWidget {
|
||||||
final MusicsList musicsList;
|
final MusicsList musicsList;
|
||||||
@ -22,8 +29,14 @@ class MusicPlayer extends StatefulWidget {
|
|||||||
class _MusicPlayerState extends State<MusicPlayer> {
|
class _MusicPlayerState extends State<MusicPlayer> {
|
||||||
final rng = Random();
|
final rng = Random();
|
||||||
|
|
||||||
final audioPlayer = AudioPlayer(playerId: "player");
|
VideoPlayerController? _videoPlayerController;
|
||||||
var _playerState = PlayerState.STOPPED;
|
ChewieAudioController? _chewieAudioController;
|
||||||
|
|
||||||
|
bool get _isPlaying => _videoPlayerController?.value.isPlaying ?? false;
|
||||||
|
|
||||||
|
Duration? get _duration => _videoPlayerController?.value.duration;
|
||||||
|
|
||||||
|
Duration? get _position => _videoPlayerController?.value.position;
|
||||||
|
|
||||||
final List<MusicEntry> stack = [];
|
final List<MusicEntry> stack = [];
|
||||||
int currMusicPos = 0;
|
int currMusicPos = 0;
|
||||||
@ -40,37 +53,40 @@ class _MusicPlayerState extends State<MusicPlayer> {
|
|||||||
return stack[currMusicPos];
|
return stack[currMusicPos];
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
|
|
||||||
audioPlayer.onPlayerError.listen((event) {
|
|
||||||
print("Player error!");
|
|
||||||
print(event);
|
|
||||||
_playNext();
|
|
||||||
});
|
|
||||||
|
|
||||||
audioPlayer.onPlayerStateChanged.listen((s) => setState(() {
|
|
||||||
_playerState = s;
|
|
||||||
|
|
||||||
if (_playerState == PlayerState.COMPLETED) _playNext();
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _play() async {
|
Future<void> _play() async {
|
||||||
if (_playerState == PlayerState.PAUSED) {
|
if (_chewieAudioController != null) {
|
||||||
await audioPlayer.resume();
|
await _chewieAudioController!.play();
|
||||||
} else {
|
} else {
|
||||||
await audioPlayer.play(currMusic.musicURL);
|
_videoPlayerController =
|
||||||
|
VideoPlayerController.network(currMusic.musicURL);
|
||||||
|
await _videoPlayerController!.initialize();
|
||||||
|
_chewieAudioController = ChewieAudioController(
|
||||||
|
videoPlayerController: _videoPlayerController!,
|
||||||
|
autoPlay: true,
|
||||||
|
showControls: false);
|
||||||
|
|
||||||
|
_videoPlayerController!.addListener(() => setState(() {
|
||||||
|
// Automatically play next music if required
|
||||||
|
if (_videoPlayerController != null && _duration == _position) {
|
||||||
|
if (_duration?.inSeconds == _position?.inSeconds &&
|
||||||
|
(_duration?.inSeconds ?? 0) > 0) {
|
||||||
|
_playNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _stop() async {
|
Future<void> _stop() async {
|
||||||
await audioPlayer.stop();
|
_chewieAudioController?.dispose();
|
||||||
|
_videoPlayerController?.dispose();
|
||||||
|
|
||||||
|
_chewieAudioController = null;
|
||||||
|
_videoPlayerController = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _pause() async {
|
void _pause() async {
|
||||||
await audioPlayer.pause();
|
await _chewieAudioController?.pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _playPrevious() async {
|
void _playPrevious() async {
|
||||||
@ -85,6 +101,14 @@ class _MusicPlayerState extends State<MusicPlayer> {
|
|||||||
await _play();
|
await _play();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _updatePosition(Duration d) => _videoPlayerController?.seekTo(d);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
_stop();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return LayoutBuilder(
|
return LayoutBuilder(
|
||||||
@ -149,6 +173,24 @@ class _MusicPlayerState extends State<MusicPlayer> {
|
|||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
Text(currMusic.artist, textAlign: TextAlign.center),
|
Text(currMusic.artist, textAlign: TextAlign.center),
|
||||||
const fluent.SizedBox(height: 40),
|
const fluent.SizedBox(height: 40),
|
||||||
|
fluent.Row(
|
||||||
|
children: [
|
||||||
|
DurationText(_position),
|
||||||
|
const SizedBox(width: 15),
|
||||||
|
Flexible(
|
||||||
|
child: fluent.Slider(
|
||||||
|
max: _duration?.inSeconds as double? ?? 0,
|
||||||
|
value: _position?.inSeconds as double? ?? 0,
|
||||||
|
onChanged: (d) =>
|
||||||
|
_updatePosition(Duration(seconds: d.toInt())),
|
||||||
|
label: _position?.formatted,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 15),
|
||||||
|
DurationText(_duration),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const fluent.SizedBox(height: 40),
|
||||||
fluent.Row(
|
fluent.Row(
|
||||||
children: [
|
children: [
|
||||||
IconButton(
|
IconButton(
|
||||||
@ -157,11 +199,10 @@ class _MusicPlayerState extends State<MusicPlayer> {
|
|||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: PlayerIcon(_playerState == PlayerState.PLAYING
|
icon: PlayerIcon(_isPlaying
|
||||||
? fluent.FluentIcons.pause
|
? fluent.FluentIcons.pause
|
||||||
: fluent.FluentIcons.play),
|
: fluent.FluentIcons.play),
|
||||||
onPressed:
|
onPressed: _isPlaying ? _pause : _play,
|
||||||
_playerState == PlayerState.PLAYING ? _pause : _play,
|
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
IconButton(
|
IconButton(
|
||||||
@ -185,3 +226,17 @@ class PlayerIcon extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => Icon(icon, size: 35);
|
Widget build(BuildContext context) => Icon(icon, size: 35);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DurationText extends StatelessWidget {
|
||||||
|
final Duration? duration;
|
||||||
|
|
||||||
|
const DurationText(this.duration, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Text(
|
||||||
|
duration?.formatted ?? "0:00",
|
||||||
|
style: const TextStyle(fontSize: 10),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
63
pubspec.lock
63
pubspec.lock
@ -8,13 +8,6 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.8.2"
|
version: "2.8.2"
|
||||||
audioplayers:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: audioplayers
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "0.20.1"
|
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -57,6 +50,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.1"
|
version: "1.3.1"
|
||||||
|
chewie_audio:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: chewie_audio
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.0"
|
||||||
clock:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -78,6 +78,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.0.1"
|
||||||
|
csslib:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: csslib
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.17.1"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -163,6 +170,13 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
html:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: html
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.15.0"
|
||||||
http:
|
http:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -420,6 +434,41 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.1"
|
||||||
|
video_player:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: video_player
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.0"
|
||||||
|
video_player_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: video_player_android
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.1"
|
||||||
|
video_player_avfoundation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: video_player_avfoundation
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.1"
|
||||||
|
video_player_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: video_player_platform_interface
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "5.1.1"
|
||||||
|
video_player_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: video_player_web
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.7"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -47,7 +47,8 @@ dependencies:
|
|||||||
cached_network_image_platform_interface: ^1.0.0
|
cached_network_image_platform_interface: ^1.0.0
|
||||||
|
|
||||||
# Audio player
|
# Audio player
|
||||||
audioplayers: ^0.20.1
|
video_player: ^2.3.0
|
||||||
|
chewie_audio: ^1.3.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Reference in New Issue
Block a user