Flutter audioplayers triggers onComplete event before the track is actually completed - flutter

I am trying to achieve a direct task, I want to autoplay the next track after the current track finishes, but sometimes the onComplete event is triggered before even the track is completed which lead to skip one of the tracks
the package I am using is: audioplayers ^0.14.2
the tracks are fetched from the database they are not local tracks
void play(List<SoundTrack> tracks){
audioPlayer.play(tracks[currentIndex].url);
setIsTalking = true;
audioPlayer.onPlayerCompletion.listen((event) {
if(currentIndex < tracks.length-1) {
next(tracks);
} else {
audioPlayer.release();
setIsTalking = false;
setPlayerState = PlayerState.paused;
}
print('completed this track, current index is' + currentIndex.toString());
});
}
void next(List<SoundTrack> tracks){
setCurrentIndex = currentIndex +1;
play(tracks);
}```

Try something like this:
define
int currentIndex = 0;
and
play(List<String> urlList, int currentIndex) async {
int result = await audioPlayer.play(urlList[currentIndex]);
if (result == 1) {
print('Success: is playing');
} else {
print('Error on audio play');
}
audioPlayer.onPlayerCompletion.listen((event) {
if(currentIndex < urlList.length-1){
currentIndex = currentIndex + 1;
nextTrack(urlList, currentIndex);
print("NEXT AUDIO! $currentIndex");
} else {
print("AUDIO COMPLETED PLAYING");
}
});
}
void nextTrack(List<String> urlList, int currentIndex) {
play(urlList, currentIndex);
}
In your play botton you call
nextTrack(urlList, currentIndex)
I tested and for me it is working.

Related

Listen to a new value of a bloc and generate a list of listened items

I have this StreamSubscription field called followSubscribtion. It listens if there is a new follower and then calls populateFollower to load follower profile.
followsSubscription =
getBloc(context).followsHandler.stream.listen((value) async {
if (value.status == Status.success) {
await populateFollows();
}
});
});
populateFollows() async{
if (getBloc(context).followsModel.length > 0) {
for (var i = 0; i < getBloc(context).followsModel.length; i++) {
getBloc(context).loadFollowsProfile(getBloc(context).followsModel[i].userId);
break;
}
}
}
This works fine, But I want each profile that will be loaded to be added to a list, How do I do that?
loadFollowsProfile method
loadFollowsProfile(int id , List<UserProfileModel> profileList) {
getFollowsProfileHandler.addNetworkTransformerStream(
Network.getInstance().getUserProfile(id), (_) {
userProfileModelBloc = UserProfileModel.fromJson(_);
profileList.add(userProfileModelBloc);
return userProfileModelBloc;
});
}
You can do this by setting up loadFollowsProfile() to return a UserProfileModel, adding that to a list in the for loop of populateFollows(), and then returning that list from populateFollows().
List<ProfileObject> populateFollows() async{
List<ProfileObject> profileList = [];
if (getBloc(context).followsModel.length > 0) {
for (var i = 0; i < getBloc(context).followsModel.length; i++){
profileList.add(getBloc(context).loadFollowsProfile(
getBloc(context).followsModel[i].userId
));
break;
}
}
return profileList;
}
followsSubscription =
getBloc(context).followsHandler.stream.listen((value) async {
if (value.status == Status.success) {
profileList = await populateFollows();
}
});
});

Flutter: Concurrent modification during iteration

So I know that my mistake lies in trying to change my data when im iterating through it
this is the exact error
"Expected Non-error `Concurrent modification during iteration:
Instance of 'LinkedMap<dynamic, dynamic>'.
and this is my code:
_products.forEach((key, value) {
if (key.titel == product.titel) {
_products[key] += 1;
} else {
_products[product] = 1;
}
}
but I don't really get how I would be able to modify my data without iterating through it. Any suggestions?
----------------my Attempt -------------------
var _products = {}.obs;
var temp = {}.obs;
void addProduct(Product product) {
temp = _products;
if (_products.length > 0) {
_products.forEach((key, value) {
if (key.titel == product.titel) {
temp[key] += 1;
} else {
temp[product] = 1;
}
});
}
I get the same error; I think that is because in this line:
temp = _products;
I just get the reference on _products & I don't write _products in temp
So as mmcdon suggested you need make a copy of your existing thing that you want to iterate upon & iterate through that instead of your original piece
So in my case I need to do this:
var _products = {}.obs;
var temp = {}.obs;
void addProduct(Product product) {
temp = RxMap<dynamic, dynamic>.from(_products);
if (_products.length > 0) {
temp.forEach((key, value) {
if (key.titel == product.titel) {
_products[key] += 1;
} else {
_products[product] = 1;
}
});
}
So as you can see I iterate through temp, that basically is _products & Im writing the data that I need in _products

Why loadInitTopStory method called after calling loaded state in flutter?

HackerNewsLoadedState is called before HackerNewsLoadingState and it loads all data from API but does not integrate to _topStories list as well as store data to _topstories after calling HackerNewsLoadedState.
#override
Stream<HackerNewsState> mapEventToState(HackerNewsEvent event) async* {
if (event is FetchHackerNewsEvent) {
yield HackerNewsLoadingState();
try {
_loadInitTopStories();
yield HackerNewsLoadedState(story: _topStories);
} catch (e) {
yield HackerNewsErrorState(message: e.toString());
}
}
}
void _loadInitTopStories() async {
try {
_topStoryIds.addAll(await _repository.loadTopStoryIds());
} catch (e) {
_topStoriesStreamController.sink.addError('Unknown Error');
return;
}
loadMoreTopStories(pageSize: INIT_PAGE_SIZE);
}
void loadMoreTopStories({int pageSize = PAGE_SIZE}) async {
if (_isLoadingMoreTopStories) return;
_isLoadingMoreTopStories = true;
final storySize = min(_currentStoryIndex + pageSize, _topStoryIds.length);
for (int index = _currentStoryIndex; index < storySize; index++) {
try {
_topStories.add(await _repository.loadStory(_topStoryIds[index]));
} catch (e) {
print('Failed to load story with id ${_topStoryIds[index]}');
}
}
_currentStoryIndex = _topStories.length;
_topStoriesStreamController.sink.add(_topStories);
_isLoadingMoreTopStories = false;
}
Found the solution it's return void but it's a mistake, it returns Stream
Stream<HackerNewsState> _loadInitTopStories() async* {
yield HackerNewsLoadingState();
try {
_topStoryIds.addAll(await repository.loadTopStoryIds());
} catch (e) {
_topStoriesStreamController.sink.addError('Unknown Error');
return;
}
yield* loadMoreTopStories(pageSize: INIT_PAGE_SIZE);
yield HackerNewsLoadedState(story: _topStories);
}
Stream<HackerNewsState> loadMoreTopStories(
{int pageSize = PAGE_SIZE}) async* {
if (_isLoadingMoreTopStories) return;
_isLoadingMoreTopStories = true;
final storySize = min(_currentStoryIndex + pageSize, _topStoryIds.length);
for (int index = _currentStoryIndex; index < storySize; index++) {
try {
_topStories.add(await repository.loadStory(_topStoryIds[index]));
} catch (e) {
print('Failed to load story with id ${_topStoryIds[index]}');
}
}
_currentStoryIndex = _topStories.length;
_topStoriesStreamController.sink.add(_topStories);
_isLoadingMoreTopStories = false;
}

block of coding running twice

So am having a scroll listener to get more data now the problem is that when the listener calls the function for more data then am having 4 if conditions but the function is running the if condition twice i don't understand the logic behind it.
MyCode:-
void _scrollListener()async{
if (!loading) {
if (scrollController.position.pixels == scrollController.position.maxScrollExtent) {
setState(() => load = true);
await clearEmptyDocs();
getMoreData();
}
}
}
getMoreData()async
{
var quicks;
var posts;
if(publicPostsDocuments.isNotEmpty)
{
print('1\n\n\n\n');
posts=await getPublicPosts();
}
if(publicQuicksDocuments.isNotEmpty)
{
print('2\n\n\n\n');
quicks=await getPublicQuicks();
}
if(publicPostsDocuments.isEmpty)
{
print('3\n\n\n\n');
posts=await getPublicDocuments();
}
if(publicQuicksDocuments.isEmpty)
{
print('4\n\n\n\n');
quicks=await getPublicDocumentsForQuicks();
}
setState(() {
load=false;
});
}
Here am having four lists and based on them am having if blocks so when lists are not empty then it should simply print 1 and 2 and then exit but it is printing 1,2,1,2 i.e. first 2 if blocks are running twice.

How to apply pagination load more function using PageController

I am working on live video streaming application with flutter. Everything is working fine except load more function. i am loading first 10 records from server and when user reach to last video i want to load more 10 records. I am using page controller to control the video pages. how can i make load more function to work. Any help would be appreciated.
Below is my page controller class
class VideoListController {
/// Construction method
VideoListController();
/// Snap to slide to realize page turning
void setPageContrller(PageController pageController) {
pageController.addListener(() {
int pageIndex = pageController.page.round();
_HomePageState home = _HomePageState();
if(pageIndex==home.videoDataList.length)
{
home.loadMore();
print("TAG loading more now");
}
var p = pageController.page;
if (p % 1 == 0) {
int target = p ~/ 1;
if (index.value == target) return;
//Play the current one, pause the others
var oldIndex = index.value;
var newIndex = target;
playerOfIndex(oldIndex).seekTo(0);
playerOfIndex(oldIndex).pause();
playerOfIndex(newIndex).start();
// carry out
index.value = target;
}
});
}
//Get specified indexçš„player
FijkPlayer playerOfIndex(int index) => playerList[index];
/// Total number of videos
int get videoCount => playerList.length;
/// Continue to add videos behind the current list and preload the cover
addVideoInfo(List<VideoModel> list) {
for (var info in list) {
playerList.add(
FijkPlayer()
..setDataSource(
Glob.ITEM_BASE_URL + info.post_video,
autoPlay: playerList.length == 0,
showCover: true,
)
..setLoop(0),
);
}
}
/// initialization
init(PageController pageController, List<VideoModel> initialList) {
addVideoInfo(initialList);
setPageContrller(pageController);
}
/// Current video sequence number
ValueNotifier<int> index = ValueNotifier<int>(0);
/// Video list
List<FijkPlayer> playerList = [];
///
FijkPlayer get currentPlayer => playerList[index.value];
bool get isPlaying => currentPlayer.state == FijkState.started;
/// Destroy all
void dispose() {
// Destroy all
for (var player in playerList) {
player.dispose();
}
playerList = [];
}
}
Below is load more function
Future<List<VideoModel>> loadMore() async {
Video menu = await getVideos();
if (menu.status == true) {
print("TAG res success is true " + menu.message);
setState(() {
videoDataList.addAll(menu.data);
start=videoDataList.length.toString();
print("TAG start " + start);
});
_videoListController.addVideoInfo(menu.data);
}
else {
print("No Data Found Paras");
}
return videoDataList;
}
Future<Video> getVideos() {
String data_type="application/x-www-form-urlencoded";
Map<String,String> headers = new Map();
headers['Content-Type'] = data_type;
return _netUtil.post(Glob.POST_LIST, body: {"count": count, "start": start,"type": type, "my_user_id": my_user_id},headers: headers).then((dynamic obj)
{
var results;
bool success = obj["status"];
print("TAG success =$success");
if (success == true)
results = Video.fromJson(obj);
else
results = Video.fromJsondata(obj);
return results;
});
}
Any help would be appreciated.
You're checking in the wrong way. You're actually checking the pageIndex value with the list size. The problem with that is index starts from 0 while length from 1 so pageIndex is never gonna match home.videoDataList.length. You have to check with home.videoDataList.length - 1
if(pageIndex == home.videoDataList.length - 1)
{
home.loadMore();
print("TAG loading more now");
}