I have done it manually using provider.
NOTE: I have used cache_manager to cache the file locally and play it from buffer the next time
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:flutter_sound/flutter_sound.dart';
import 'package:graphql_flutter/graphql_flutter.dart';
enum REPEAT_MODE { REPEAT_ONE, REPEAT_ALL, NONE, SHUFFLE }
class AudioPlayerProvider extends ChangeNotifier {
//static public variable
final FlutterSound audioPlayer = FlutterSound();
final DefaultCacheManager cache = DefaultCacheManager();
//static private variable
bool _isPlaying = false;
bool _isLoading = false;
LazyCacheMap _music;
List _playlist = [];
double _maxDuration = 1;
double _currentDuration = 0;
REPEAT_MODE _repeatMode = REPEAT_MODE.NONE;
_subscribe() {
audioPlayer.onPlayerStateChanged.listen((state) {
if (state != null) {
if (_maxDuration != state.duration) maxDuration(state.duration);
if (_currentDuration != state.currentPosition)
currentDuration(state.currentPosition);
//when the music has stopped play
if (_currentDuration >= _maxDuration) {
if (repeatMode() == REPEAT_MODE.NONE) {
isPlaying(false);
} else {
switch (repeatMode()) {
case REPEAT_MODE.REPEAT_ALL:
skipToNext();
break;
case REPEAT_MODE.REPEAT_ONE:
play(music());
break;
case REPEAT_MODE.SHUFFLE:
print('shuffle');
int index = Random().nextInt(playlist().length);
LazyCacheMap music = playlist()[index];
play(music);
break;
default:
break;
}
}
}
}
});
}
//main methods setter / getter
music([LazyCacheMap music]) {
if (music != null) {
_music = music;
notifyListeners();
}
return _music;
}
List playlist([List playlist]) {
if (playlist != null) {
_playlist = playlist;
notifyListeners();
}
return _playlist;
}
REPEAT_MODE repeatMode([REPEAT_MODE repeatMode]) {
if (repeatMode != null) {
if (repeatMode == _repeatMode) {
_repeatMode = null;
} else {
_repeatMode = repeatMode;
}
notifyListeners();
}
return _repeatMode;
}
bool isPlaying([bool isPlaying]) {
if (isPlaying != null) {
_isPlaying = isPlaying;
notifyListeners();
}
return _isPlaying;
}
bool isLoading([bool isLoading]) {
if (isLoading != null) {
_isLoading = isLoading;
notifyListeners();
}
return _isLoading;
}
//main method getter
double maxDuration([double duration]) {
if (duration != null) {
_maxDuration = duration;
notifyListeners();
}
return _maxDuration;
}
double currentDuration([double duration]) {
if (duration != null) {
_currentDuration = duration;
notifyListeners();
}
return _currentDuration;
}
getMusic() => _music;
getPlaylist() => _playlist;
//main action's method
play(LazyCacheMap newMusic, [List newPlaylist]) async {
// cache.emptyCache();
if (newMusic == null) return;
if (newMusic == _music &&
audioPlayer.audioState != t_AUDIO_STATE.IS_STOPPED) {
return;
}
music(newMusic);
if (playlist != null) playlist(newPlaylist);
try {
isLoading(true);
String url = newMusic['url'];
FileInfo info = await cache.getFileFromCache(url);
if (audioPlayer.audioState != t_AUDIO_STATE.IS_STOPPED) {
await audioPlayer.stopPlayer();
}
if (info != null) {
await audioPlayer.startPlayerFromBuffer(
info.file.readAsBytesSync(),
);
} else {
await audioPlayer.startPlayer(url);
}
isPlaying(true);
isLoading(false);
_subscribe();
notifyListeners();
await cache.getSingleFile(url);
} catch (e) {
isPlaying(false);
print(e.message);
}
}
resumePause() async {
try {
switch (audioPlayer.audioState) {
case t_AUDIO_STATE.IS_PLAYING:
isPlaying(false);
return await audioPlayer.pausePlayer();
case t_AUDIO_STATE.IS_PAUSED:
isPlaying(true);
return await audioPlayer.resumePlayer();
case t_AUDIO_STATE.IS_STOPPED:
return await play(_music, _playlist);
default:
break;
}
notifyListeners();
} catch (e) {
isPlaying(false);
print(e);
}
}
skipToPrevious() async {
final int _currentIndex = _playlist.indexOf(_music);
int _nextIndex =
_currentIndex <= 0 ? _playlist.length - 1 : _currentIndex - 1;
final music = _playlist[_nextIndex];
notifyListeners();
play(music);
}
skipToNext() async {
final int _currentIndex = _playlist.indexOf(_music);
int _nextIndex =
_currentIndex >= _playlist.length - 1 ? 0 : _currentIndex + 1;
final music = _playlist[_nextIndex];
notifyListeners();
play(music);
}
seek(int duration) {
audioPlayer.seekToPlayer(duration);
notifyListeners();
}
}
woww, thankyou very much, i will try it
playlist
can u give me an example page bro ?
can u give me an example page bro ?
Note that I am using graphql_flutter, that is why the music is of type LazyCache.
in fact it is a Map of this shape:
//sorry for the js
const obj = {
title: "my title",
artist: {
pseudo: "artist name"
}
//etc...
}
I am getting the playlist by assigning every musics of the same kind (eg: music from the same album) to the play method, which then save them as a playlist in the provider so that I can easily loop or shuffle them.
This is the card that play the music on click
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart' show Entypo;
import 'package:hashtune/providers/audio_player_provider.dart';
import 'package:hashtune/utils/utils.dart';
import 'package:hashtune/widgets/image_shadow.dart';
import 'package:hashtune/widgets/music_tag.dart';
import 'package:provider/provider.dart';
enum MusicBoxType { TILE, CARD }
class MusicBox extends StatefulWidget {
final MusicBoxType boxType;
final List<dynamic> data;
final dynamic current;
MusicBox({
Key key,
@required this.data,
@required this.current,
this.boxType = MusicBoxType.TILE,
}) : super(key: key);
@override
_MusicBoxState createState() => _MusicBoxState();
}
class _MusicBoxState extends State<MusicBox> {
AudioPlayer audioPlayer =
AudioPlayer(playerId: MAIN_PLAYER_ID, mode: PlayerMode.MEDIA_PLAYER);
final DefaultCacheManager cache = DefaultCacheManager();
@override
Widget build(BuildContext context) {
final _audioPlayer = Provider.of<AudioPlayerProvider>(context);
return InkWell(
onTap: () async {
// always set data on click
final _current = widget.current;
final _list = widget.data;
_audioPlayer.play(_current, _list);
},
child: widget.boxType == MusicBoxType.TILE
? MusicTile(
data: widget.current,
)
: MusicCard(
data: widget.current,
),
);
}
}
class MusicCard extends StatelessWidget {
final dynamic data;
const MusicCard({
Key key,
@required this.data,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(right: 20),
width: 190,
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
ImageShadow(
imageURL: data['cover'],
width: 190,
height: 130,
),
Padding(
padding: EdgeInsets.only(top: 5),
),
Text(
data['title'],
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.body2,
),
Text(data['artist']['pseudo']),
Padding(
padding: EdgeInsets.only(bottom: 2),
),
MusicTag(
tag: data['tag'],
dense: true,
),
],
),
// ),
);
}
}
class MusicTile extends StatelessWidget {
final dynamic data;
const MusicTile({
Key key,
@required this.data,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
height: 70,
margin: EdgeInsets.only(bottom: 20),
child: Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
ImageShadow(
width: 65,
height: 65,
imageURL: data['cover'],
),
Expanded(
child: Padding(
padding: EdgeInsets.only(left: 15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text(
data['title'],
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.body2,
),
Text(
data['artist']['pseudo'],
),
MusicTag(
dense: true,
tag: data['tag'],
)
],
),
),
),
Align(
child: IconButton(
icon: Icon(
Entypo.dots_two_horizontal,
color: Colors.black.withOpacity(.5),
),
onPressed: () {},
),
),
],
),
);
}
}
example of list
import 'package:flutter/material.dart';
import 'package:hashtune/widgets/music_tile.dart';
import 'package:hashtune/widgets/search_input.dart';
//made stateful because of loading state of the data
//and search
//possible inifity scroll
// TODO: replace database by data props
class MusicListAndSearch extends StatefulWidget {
final List musics;
final bool hideSearch;
final EdgeInsetsGeometry padding;
MusicListAndSearch({
Key key,
@required this.musics,
this.padding,
this.hideSearch = false,
}) : super(key: key);
@override
_MusicListAndSearchState createState() => _MusicListAndSearchState();
}
class _MusicListAndSearchState extends State<MusicListAndSearch> {
@override
Widget build(BuildContext context) {
return Container(
child: ListView.builder(
padding: widget.padding,
itemCount: widget.musics != null ? widget.musics.length : 0,
primary: false,
shrinkWrap: true,
itemBuilder: (_, i) {
final _data = widget.musics[i];
if (i == 0 && !widget.hideSearch) {
return Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(bottom: 20),
child: SearchInput(),
),
MusicBox(
current: _data,
data: widget.musics,
)
],
);
}
return MusicBox(
current: _data,
data: widget.musics,
);
},
),
);
}
}
can u give me an example page bro ?
Note that I am using graphql_flutter, that is why the music is of type LazyCache.
in fact it is a Map of this shape://sorry for the js const obj = { title: "my title", artist: { pseudo: "artist name" } //etc... }I am getting the playlist by assigning every musics of the same kind (eg: music from the same album) to the play method, which then save them as a playlist in the provider so that I can easily loop or shuffle them.
This is the card that play the music on click
import 'package:audioplayers/audioplayers.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:flutter_vector_icons/flutter_vector_icons.dart' show Entypo; import 'package:hashtune/providers/audio_player_provider.dart'; import 'package:hashtune/utils/utils.dart'; import 'package:hashtune/widgets/image_shadow.dart'; import 'package:hashtune/widgets/music_tag.dart'; import 'package:provider/provider.dart'; enum MusicBoxType { TILE, CARD } class MusicBox extends StatefulWidget { final MusicBoxType boxType; final List<dynamic> data; final dynamic current; MusicBox({ Key key, @required this.data, @required this.current, this.boxType = MusicBoxType.TILE, }) : super(key: key); @override _MusicBoxState createState() => _MusicBoxState(); } class _MusicBoxState extends State<MusicBox> { AudioPlayer audioPlayer = AudioPlayer(playerId: MAIN_PLAYER_ID, mode: PlayerMode.MEDIA_PLAYER); final DefaultCacheManager cache = DefaultCacheManager(); @override Widget build(BuildContext context) { final _audioPlayer = Provider.of<AudioPlayerProvider>(context); return InkWell( onTap: () async { // always set data on click final _current = widget.current; final _list = widget.data; _audioPlayer.play(_current, _list); }, child: widget.boxType == MusicBoxType.TILE ? MusicTile( data: widget.current, ) : MusicCard( data: widget.current, ), ); } } class MusicCard extends StatelessWidget { final dynamic data; const MusicCard({ Key key, @required this.data, }) : super(key: key); @override Widget build(BuildContext context) { return Container( margin: EdgeInsets.only(right: 20), width: 190, child: Column( mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ ImageShadow( imageURL: data['cover'], width: 190, height: 130, ), Padding( padding: EdgeInsets.only(top: 5), ), Text( data['title'], maxLines: 1, overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.body2, ), Text(data['artist']['pseudo']), Padding( padding: EdgeInsets.only(bottom: 2), ), MusicTag( tag: data['tag'], dense: true, ), ], ), // ), ); } } class MusicTile extends StatelessWidget { final dynamic data; const MusicTile({ Key key, @required this.data, }) : super(key: key); @override Widget build(BuildContext context) { return Container( height: 70, margin: EdgeInsets.only(bottom: 20), child: Row( mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ ImageShadow( width: 65, height: 65, imageURL: data['cover'], ), Expanded( child: Padding( padding: EdgeInsets.only(left: 15), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ Text( data['title'], maxLines: 1, overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.body2, ), Text( data['artist']['pseudo'], ), MusicTag( dense: true, tag: data['tag'], ) ], ), ), ), Align( child: IconButton( icon: Icon( Entypo.dots_two_horizontal, color: Colors.black.withOpacity(.5), ), onPressed: () {}, ), ), ], ), ); } }example of list
import 'package:flutter/material.dart'; import 'package:hashtune/widgets/music_tile.dart'; import 'package:hashtune/widgets/search_input.dart'; //made stateful because of loading state of the data //and search //possible inifity scroll // TODO: replace database by data props class MusicListAndSearch extends StatefulWidget { final List musics; final bool hideSearch; final EdgeInsetsGeometry padding; MusicListAndSearch({ Key key, @required this.musics, this.padding, this.hideSearch = false, }) : super(key: key); @override _MusicListAndSearchState createState() => _MusicListAndSearchState(); } class _MusicListAndSearchState extends State<MusicListAndSearch> { @override Widget build(BuildContext context) { return Container( child: ListView.builder( padding: widget.padding, itemCount: widget.musics != null ? widget.musics.length : 0, primary: false, shrinkWrap: true, itemBuilder: (_, i) { final _data = widget.musics[i]; if (i == 0 && !widget.hideSearch) { return Column( children: <Widget>[ Padding( padding: EdgeInsets.only(bottom: 20), child: SearchInput(), ), MusicBox( current: _data, data: widget.musics, ) ], ); } return MusicBox( current: _data, data: widget.musics, ); }, ), ); } }
ahhhh, thanks bro...
like it
Most helpful comment
Note that I am using graphql_flutter, that is why the music is of type LazyCache.
in fact it is a Map of this shape:
I am getting the playlist by assigning every musics of the same kind (eg: music from the same album) to the play method, which then save them as a playlist in the provider so that I can easily loop or shuffle them.
This is the card that play the music on click
example of list