Tau: can we do looping in fluttersound ?

Created on 7 Feb 2020  路  5Comments  路  Source: Canardoux/tau

Version of flutter_sound

flutter doctor

Platforms you faced the error (IOS or Android or both?)

Expected behavior

Actual behavior

Tested environment (Emulator? Real Device?)

Steps to reproduce the behavior

Most helpful comment

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,
          );
        },
      ),
    );
  }
}

All 5 comments

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

Was this page helpful?
0 / 5 - 0 ratings

Related issues

PabloduPontavice picture PabloduPontavice  路  3Comments

mhstoller picture mhstoller  路  5Comments

alby97 picture alby97  路  5Comments

deepbluev7 picture deepbluev7  路  5Comments

palfrey picture palfrey  路  3Comments