// ignore_for_file: avoid_print import 'dart:math'; import 'dart:ui'; import 'package:audioplayers/audioplayers.dart'; import 'package:fluent_ui/fluent_ui.dart' as fluent; import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:flutter/material.dart'; import 'package:music_web_player/api.dart'; import 'package:music_web_player/ui/cover_image.dart'; class MusicPlayer extends StatefulWidget { final MusicsList musicsList; const MusicPlayer({Key? key, required this.musicsList}) : super(key: key); @override State createState() => _MusicPlayerState(); } class _MusicPlayerState extends State { final rng = Random(); final audioPlayer = AudioPlayer(playerId: "player"); var _playerState = PlayerState.STOPPED; final List stack = []; int currMusicPos = 0; MusicEntry get currMusic { if (currMusicPos < 0) currMusicPos = 0; // Automatically choose next music if required if (currMusicPos >= stack.length) { var nextId = rng.nextInt(widget.musicsList.length); stack.add(widget.musicsList[nextId]); } 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})); } Future _play() async { if (_playerState == PlayerState.PAUSED) { await audioPlayer.resume(); } else { await audioPlayer.play(currMusic.musicURL); } } Future _stop() async { await audioPlayer.stop(); } void _pause() async { await audioPlayer.pause(); } void _playPrevious() async { currMusicPos -= 1; await _stop(); await _play(); } void _playNext() async { currMusicPos += 1; await _stop(); await _play(); } @override Widget build(BuildContext context) { return LayoutBuilder( builder: (context, constraints) => Stack( children: [ // Background image CoverImage( music: currMusic, width: constraints.maxWidth, height: constraints.maxHeight, fit: BoxFit.cover, ), ClipRRect( // Clip it cleanly. child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), child: Container( color: Colors.black.withOpacity(0.8), alignment: Alignment.center, child: SizedBox( width: constraints.maxWidth, height: constraints.maxHeight, ), ), )), _buildCenter(), ], ), ); } Widget _buildCenter() { return fluent.Center( child: SizedBox( width: 250, child: Column( mainAxisAlignment: fluent.MainAxisAlignment.center, crossAxisAlignment: fluent.CrossAxisAlignment.center, children: [ Material( borderRadius: const BorderRadius.all( Radius.circular(18.0), ), clipBehavior: Clip.hardEdge, child: CoverImage( width: 250, height: 250, music: currMusic, fit: BoxFit.cover, backgroundColor: Colors.black12, icon: const Icon(FluentIcons.music_note_2_24_regular, size: 90), ), ), const SizedBox(height: 40), Text( currMusic.title, style: const TextStyle(fontSize: 22), textAlign: TextAlign.center, ), const SizedBox(height: 20), Text(currMusic.artist, textAlign: TextAlign.center), const fluent.SizedBox(height: 40), fluent.Row( children: [ IconButton( icon: const PlayerIcon(fluent.FluentIcons.previous), onPressed: currMusicPos == 0 ? null : _playPrevious, ), const Spacer(), IconButton( icon: PlayerIcon(_playerState == PlayerState.PLAYING ? fluent.FluentIcons.pause : fluent.FluentIcons.play), onPressed: _playerState == PlayerState.PLAYING ? _pause : _play, ), const Spacer(), IconButton( icon: const PlayerIcon(fluent.FluentIcons.next), onPressed: _playNext, ), ], ) ], ), ), ); } } class PlayerIcon extends StatelessWidget { final IconData icon; const PlayerIcon(this.icon, {Key? key}) : super(key: key); @override Widget build(BuildContext context) => Icon(icon, size: 35); }