flutter music current index not updating - flutter

List<SongModel> songs = item.data!;
return ListTile(
onTap: () {
audioPlayer.setAudioSource(
createPlayList(songs),
initialIndex: index,
);
audioPlayer.play();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PlayerView(
audioPlayer: audioPlayer,
songsList: songs,
initialIndex: index,
),
),
);
ConcatenatingAudioSource createPlayList(List<SongModel> songs) {
List<AudioSource> sources = [];
for (var song in songs) {
sources.add(AudioSource.uri(Uri.parse(song.uri!)));
}
return ConcatenatingAudioSource(children: sources);
}
I am using this method to get all the songs from audio query and it's working fine if i want to play a single song on the player view which is :
class PlayerView extends StatefulWidget {
PlayerView({
Key? key,
required this.audioPlayer,
required this.songsList,
required this.initialIndex,
}) : super(key: key);
AudioPlayer audioPlayer;
List<SongModel> songsList;
int initialIndex;
#override
State<PlayerView> createState() => _PlayerViewState();
}
class _PlayerViewState extends State<PlayerView> {
bool isPlaying = false;
String currentSongTitle = '';
int currentIndex = 0;
Stream<DurationState> get durationState =>
Rx.combineLatest2<Duration, Duration?, DurationState>(
widget.audioPlayer.positionStream,
widget.audioPlayer.durationStream,
(position, duration) =>
DurationState(position: position, total: duration ?? Duration.zero),
);
#override
void initState() {
super.initState();
widget.audioPlayer.currentIndexStream.listen((index) {
if (index != null) {
_updateCurrentPlayingSongDetails(index);
}
});
}
#override
void dispose() {
super.dispose();
}
i am updating title and artquery in this way
widget.songsList[widget.initialIndex].title,
QueryArtworkWidget(
id: widget.songsList[widget.initialIndex].id,
and this method for skipping to next or previous song which is working fine means it's skipping to next or previous but only one like if i am playing song 1 and i skip to next means song2 and then try to skip again it will keep playing song 2 which as far as i think is due to not updating the song list and i tried to do it but it's not working as it's supposed to be.
void _updateCurrentPlayingSongDetails(int index) {
setState(() {
if (widget.songsList.isNotEmpty) {
currentSongTitle = widget.songsList[index].title;
currentIndex = index;
}
});
}
So the conclusion of the problem is :
Main page view which is the first one is sending single index to other screen due to which only first played song can be skipped and it's also not updating the current state.
Hope i will find a solution from any of you and i will be waiting cuz i am trying to fix it from days but i am new in flutter so you can expect this.

You can use currentIndexStream, eg:
final AudioPlayer player = GetIt.I.get<AudioPlayer>();
player.currentIndexStream.listen((index) {
if (index != null) {
// your code
}
});

Related

I want to call images and audio lists in order using AudioPlayer()

I'm going to make an app that plays a list of images and audio in order. After audio playback, the following images and audio are played.
I update image to setState() when the playback is completed. And each time use setState(), I used play() function inside the build() method to recall play().
However, the method in the build is called twice, so the audio file is also played twice.
How do I run this method only once when I build it?
class PlayingScreen extends StatefulWidget {
final PostModel postModel;
PlayingScreen({
Key? key,
required this.postModel,
}) : super(key: key);
#override
State<PlayingScreen> createState() => _PlayingScreenState();
}
class _PlayingScreenState extends State<PlayingScreen> {
final _audioPlayer = AudioPlayer();
List<String> _audios = []; // audio file path list
int _playIndex = 0;
#override
void initState() {
_getAudios();
super.initState();
}
#override
void dispose() {
_audioPlayer.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
_play(_playIndex);
return Scaffold(
appBar: AppBar(
title: Text(widget.postModel.title),
),
body: InkWell(
onTap: () {
Get.back();
},
// image widget
),
),
);
}
Future _play(int index) async {
await _audioPlayer.play(DeviceFileSource(_audios[index]));
logger.d("current index: $_playIndex / ${_audios.length - 1}");
//complete event
_audioPlayer.onPlayerComplete.listen((event) {
logger.d("complet, next");
setState(() {
if (_playIndex >= _imageSets.length - 1) {
_playIndex = 0;
logger.d("return first index");
} else {
_playIndex++;
}
});
});
}
}
// ...
int _playIndex = 0;
bool played = false
// ...
// build
if(! played) {
_play(_playIndex);
played = true;
}
Add the variable "played" at the class level
Add a check in the build method
Calling a future inside the build method will always rebuild multiple times not only twice.
I would probably use it in a FutureBuilder() like so:
FutureBuilder(
future:_play(_playIndex),
builder:(context, snapshot){
...
}
)

Flutter change state on all list item

Hey I am a newbie in flutter, I am trying to build an interview radio app
The issue I am facing is when changing state on switching music from one station to another, the "playing" and icon still don't change for the previously clicked object, but the new station starts playing
Here the station has switched but playing text has not yet gone, as well as the icon changed
Ideally, I want when switched, the playing and icon change should be switched to the one playing now, and one that's switched from should have icon showing as the rest
My code
class ItemWidget extends StatefulWidget {
final Item item;
ItemWidget({Key? key, required this.item}) : super(key: key);
#override
State<ItemWidget> createState() => _ItemWidgetState();
}
class _ItemWidgetState extends State<ItemWidget> {
static AudioPlayer player = AudioPlayer();
static String name = "";
static bool isPlaying = false;
static String error = '';
initRadioPlayer(namepassed, url) async {
try {
if (name == namepassed) {
player.stop();
name = "";
isPlaying = false;
setState(() {});
} else if (name != namepassed) {
if (isPlaying == true) {
await player.stop();
}
await player.setUrl(url);
player.play();
name = namepassed;
isPlaying = true;
setState(() {});
}
} catch (err) {
error = err.toString();
setState(() {});
}
}
#override
Widget build(BuildContext context) {
return Card(
child: ListTile(
onTap: () {
initRadioPlayer(widget.item.name, widget.item.url);
},
title: Text(widget.item.name),
subtitle: name == widget.item.name
? isPlaying
? Text('Playing')
: Text('')
: Text(''),
trailing: Icon(isPlaying
? name == widget.item.name
? CupertinoIcons.stop_circle
: CupertinoIcons.play_circle
: CupertinoIcons.play_circle),
textColor: Color.fromARGB(255, 31, 22, 22),
),
);
}
}
I used a callback function and kept the state of which one is playing in the parent just like Ivo Becker suggested:
void _update(String name, bool isPlaying) {
setState(() {
_name = name;
_isPlaying = isPlaying;
});
}
This goes in the parent and then is passed on to and called from the child:
class ItemWidget extends StatefulWidget {
final Item item;
final String name;
final bool isPlaying;
Function callback;
and on tap you call it:
widget.callback(station_name, true);

State and Scroll position restore Flutter

I have an app which fetches posts from a site using a API and then displays it. There are three navigation options, which are basically filters.
The problem is, whenever I switch to another navigation tab (I'm using bottom navigation bar), it ends up rebuilding the whole page, meaning it will fetch all that data again and it might potentially contain new data.
What I want to do is to keep restore this data in a way that is fast and my initState() doesn't get called(because that is what fetches the data). I did try using all the different kind of keys but I cant get it to work.
Main page:
class AppHomePage extends StatefulWidget {
AppHomePage({Key? key}) : super(key: key);
#override
_AppHomePageState createState() => _AppHomePageState();
}
List<Widget> _navs = [
BestPostsRoute(key: PageStorageKey("bestP")),
HotPostsRoute(key: PageStorageKey("hotP")),
NewPostsRoute(key: PageStorageKey("newP"))
];
class _AppHomePageState extends State<AppHomePage> {
int _currentIndex = 0;
onTap(index) => {
setState(() => {_currentIndex = index})
};
#override
Widget build(BuildContext context) {
return Scaffold(
/* appbar ... */
body: _navs.elementAt(_currentIndex),
bottomNavigationBar: BottomNavigationBar(
items: [
/* nav items */
],
currentIndex: _currentIndex,
onTap: onTap,
),
);
}
}
One of the three pages(the code is similar in all three):
/* imports... */
class HotPostsRoute extends StatefulWidget {
HotPostsRoute({Key? key}) : super(key: key);
#override
_HotPostsRouteState createState() => _HotPostsRouteState();
}
class _HotPostsRouteState extends State<HotPostsRoute> {
late PostInstance postInstance;
List<Post> _posts = [];
bool _loaded = false;
fetchPosts(String? after) async {
var stream = postInstance.front.hot(limit: 10, after: after);
await for (UserContent post in stream) {
Submission submission = post as Submission;
Post pPost = Post(submission);
pPost.parse().then((value) => setState(() {
_posts.add(pPost);
}));
}
setState(() {
_loaded = true;
});
}
#override
void initState() {
super.initState();
if (mounted) {
setState(() {
redditInstance =
Provider.of<PostInstanceState>(context, listen: false)
.getInstance;
});
fetchPosts("");
}
}
// Fetches and generates posts
Widget _buildPosts() {
return ListView.builder(
itemCount: _posts.length + 1,
itemBuilder: (ctx, index) {
if (index < _posts.length) {
return _buildPost(_posts.elementAt(index));
} else {
fetchPosts(_posts.last.fullname);
return SpinKitDualRing(color: Colors.white);
}
},
);
}
// A singular post
Widget _buildPost(Post post) {
print(post.object);
return PostCard(post, key: ObjectKey(post.object)); // .object just creates a map of all fields
}
#override
Widget build(BuildContext context) {
setState(() {});
return Container(
child: _loaded ? _buildPosts() : SpinKitDualRing(color: Colors.white),
);
}
}
So I kept searching and eventually a post on Medium led me to the IndexedStack Widget.
Its a widget that is made from the Stack widget and basically loads and stores the state of all its childrens. Unlike Stack, it shows its children one at a time and thus is perfect to use with BottomNavigationBar.
Here's the Blog post for anyone looking out.

Flutter mobile, playing midi files

I would like to know how can I play midi files on mobile app
Ive found two midi libraries, but don't see how to implement function to play midi files
https://pub.dev/packages/flutter_midi
here Im using this package in my app to play sf2 audio, though I can only play single notes at the time, here is code I use to do that
class KeyPage extends StatefulWidget {
#override
_KeyPageState createState() => _KeyPageState();
}
class _KeyPageState extends State<KeyPage> {
//final _flutterMidi = FlutterMidi(); //TODO 1 check TODO 2
#override
void initState() {
if (!kIsWeb) {
load(_value);
} else {
FlutterMidi.prepare(
sf2:
null); //TODO 2 check what to do here with static issue Original line was:
// _flutterMidi.prepare(sf2: null);
}
super.initState();
}
void load(String asset) async {
print("Loading File...");
FlutterMidi.unmute();
ByteData _byte = await rootBundle.load(asset);
// 'assets/sf2/SmallTimGM6mb.sf2';
// 'assets/sf2/Piano1.sf2';
FlutterMidi.prepare(sf2: _byte, name: _value.replaceAll("assets/sf2/", ""));
}
https://pub.dev/packages/dart_midi
didn't yet try this package, but from reading it I don't see function in it to play midi file, though language they use is new to me, so I guess this library does that, but I don't understand how
Thank
Here's my implementation using Flutter_midi.
I've used a timer to play the whole sequence of notes, one after the another.
For it to work you need to provide the number of notes you want to play and the time between them.
This does work on the IOS emulator, but I want to say that Flutter_midi contains some deprecated code and it doesn't provide the methods to play a full song in midi format.
For example it is not possible to set the velocity of the notes.
I have implemented this solution anyway, but I personally do not recommend this package.
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late FlutterMidi fm;
final songFile = 'assets/Piano.sf2';
#override
void initState() {
super.initState();
fm = FlutterMidi();
_load(songFile);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('MidiPlayer'),
),
body: Center(
child: TextButton(
onPressed: () {
_playSong(
timing: 100,
notes: 30,
);
},
child: const Text('Play'),
),
),
);
}
void _load(String asset) async {
fm.unmute();
ByteData _byte = await rootBundle.load(asset);
fm.prepare(sf2: _byte, name: songFile.replaceAll('assets/', ''));
}
void _playSong({
required int timing, //Time between notes
required int notes, //Number of notes to be played
}) {
var note = 0;
Timer.periodic(Duration(milliseconds: timing), (timer) {
//You played the last note
if (note == notes) {
fm.stopMidiNote(midi: note - 1);
timer.cancel();
return;
}
//Interrupt the previous note
if (note > 0) {
fm.stopMidiNote(midi: note - 1);
}
//Play the current note and increase the counter
fm.playMidiNote(midi: note++);
});
}
}

flutter [Only static members can be accessed in initializers]

I am a true beginner in flutter and dart.
I have a problem concerning playing youtube videos using [ youtube_player_flutter: ^6.1.1]
I create a Json file with youtube links and I want to link it with [ youtube_player_flutter: ^6.1.1]. but it always displays the error message [Only static members can be accessed in initializers]
#override
Widget build(BuildContext context) {
// this function is called before the build so that
// the string assettoload is avialable to the DefaultAssetBuilder
setasset();
// and now we return the FutureBuilder to load and decode JSON
return FutureBuilder(
future:
DefaultAssetBundle.of(context).loadString(assettoload, cache: true),
builder: (context, snapshot) {
List mydata = json.decode(snapshot.data.toString());
if (mydata == null) {
return Scaffold(
body: Center(
child: Text(
"Loading",
),
),
);
} else {
return quizpage(mydata: mydata);
}
},
);
}
}
class quizpage extends StatefulWidget {
final dynamic mydata;
////////var youtubeUrl;
quizpage({Key key, #required this.mydata}) : super(key: key);
#override
_quizpageState createState() => _quizpageState(mydata);
}
class _quizpageState extends State<quizpage> {
var mydata;
_quizpageState(this.mydata);
int marks = 0;
int i = 1;
#override
void setState(fn) {
if (mounted) {
super.setState(fn);
}
}
YoutubePlayerController _controller;
#override
void initState() {
_controller = YoutubePlayerController(
initialVideoId: YoutubePlayer.convertUrlToId(mydata[4]["1"]));
super.initState();
}
void nextquestion() {
setState(() {
if (i < 10) {
i++;
} else {
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) => resultpage(marks: marks),
));
}
The problem is that I want to make the [String videoURL ] plays the list of videos in my json data file.
Thanks in advance.
Possibility is that you coded the variable mydata twice. This is the format you should follow. And in order to make use of the variable from the StatefulWidget from the constructor, use widget.mydata. Don't have to declare it twice.
Code:
class Quizpage extends StatefulWidget {
final dynamic mydata;
quizpage({Key key, #required this.mydata}) : super(key: key);
#override
_QuizpageState createState() => _QuizpageState();
}
class _QuizpageState extends State<Quizpage> {
/*
You can make use of your mydata in this class like this:
widget.mydata, and you will be able to make it work
*/
Color colortoshow = Colors.indigoAccent;
Color right = Colors.green;
Color wrong = Colors.red;
int marks = 0;
int i = 1;
// String videoURL ="https://www.youtube.com/watch?v=2OAdfB2U88A&t=593s";
YoutubePlayerController _controller;
// Use like this to make use of your array mydata
String videoURL = widget.myData[4]["1"];
#override
void initState() {
_controller = YoutubePlayerController(
initialVideoId: YoutubePlayer.convertUrlToId(videoURL));
super.initState();
}
}
Also, this is for coding point of view. Please follow the correct way of naming classes in Flutter. Always use CamelCase or Have your first letter of the class as capital. This is the best practice while you write your code. I hope the above helps you in some sense. Thanks :)