How to apply pagination load more function using PageController - flutter

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

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

ever() does not work when bool.obs value changed GetXController

I have var hasActivatedIndex = false.obs; and
#override
void onInit() {
super.onInit();
ever(hasActivatedIndex, (callback) {
print('hasActivatedIndex !!== $hasActivatedIndex'); // not activated
});
}
with following method I changed hasActivatedIndex true or false, and hope every time this ever() will get this boolean changed state and print something
int updateActivatedIndex({double scrollPy}){
activatedIndex = eventPyList.indexWhere((element) {
if (scrollPy >= element && scrollPy <= element + 20) {
hasActivatedIndex.value = true;
return true;
} else {
hasActivatedIndex.value = false;
showEventCard = false;
return false;
}
});
return activatedIndex;
}
The hasActivatedIndex.value has indeed changed to true or false, but ever() in onInit does not react to this change, why? thanks in advance for the explanation!

Flutter, bug in classes

In main.dart I call Quiz's function getQuestionText and getQuestionAnswer, getQuestionText works as expected but the other doesn't work, if returns me always the first result of the list. I just placed a debugPrint() and as expected getQuestionText() prints the correct number, getQuestionAnswer() always print 0, how is that possible?
class Quiz {
int _questionNumber = 0;
List<Question> _questions = [
Question('Some cats are actually allergic to humans', true),
Question('You can lead a cow down stairs but not up stairs.', false),
];
void nextQuestion() {
if (_questionNumber < _questions.length - 1) {
_questionNumber++;
}
}
String getQuestionText() {
print('$_questionNumber'); // <-- print the correct number
return _questions[_questionNumber].questionText;
}
bool getQuestionAnswer() {
print('$_questionNumber'); // <-- always print 0
return _questions[_questionNumber].questionAnswer;
}
}
Here how I call the functions
void checkAnswer(bool userAnswer) {
bool correctAnswer = Quiz().getQuestionAnswer();
setState(() {
if (userAnswer == correctAnswer) {
// right answer
} else {
// wrong pick
);
}
quiz.nextQuestion();
});
}
The problem is that you always create a fresh instance of your class Quiz by calling bool correctAnswer = Quiz().getQuestionAnswer(); inside checkAnswer().
Try to store the Quiz instance ouside:
const myQuiz = Quiz();
void checkAnswer(bool userAnswer) {
bool correctAnswer = myQuiz.getQuestionAnswer();
setState(() {
if (userAnswer == correctAnswer) {
// right answer
} else {
// wrong pick
}
myQuiz.nextQuestion();
});
}

HERE API Autosuggest Geocoding search

I am looking to use the HERE Geocoding Autosuggest. I understand how the API works and it is the implementation in Flutter I seek guidance on.
There is a Javascript example for the previous version
https://developer.here.com/documentation/examples/rest/geocoding_suggestions
This demonstrates the call and Json return, but I wondered if there were any Flutter examples/ guidance on implementation when it comes to displaying the data.
For example, the API returns address results for 'London', does Flutter have build in functionality to display these to the user (Such as TypeAheadField), in a dropdown style box below entry field for example, like the HERE screenshot below, where the user can select the correct suggestion? How would the call be implemented with this function?
I presume I will utilise an onChanged/SetState style function to call and display, but it is how to make the call as user types and then display the returned suggestion that I would find an example useful.
Any resources/ tips welcome
Thank you
Please check the below code ,which explains the use of auto suggest in the flutter.
import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'package:here_sdk/core.dart';
import 'package:here_sdk/core.errors.dart';
import 'package:here_sdk/gestures.dart';
import 'package:here_sdk/mapview.dart';
import 'package:here_sdk/search.dart';
import 'SearchResultMetadata.dart';
// A callback to notify the hosting widget.
typedef ShowDialogFunction = void Function(String title, String message);
class SearchExample {
HereMapController _hereMapController;
MapCamera _camera;
MapImage? _poiMapImage;
List<MapMarker> _mapMarkerList = [];
late SearchEngine _onlineSearchEngine;
late OfflineSearchEngine _offlineSearchEngine;
bool useOnlineSearchEngine = true;
ShowDialogFunction _showDialog;
SearchExample(ShowDialogFunction showDialogCallback, HereMapController hereMapController)
: _showDialog = showDialogCallback,
_hereMapController = hereMapController,
_camera = hereMapController.camera {
double distanceToEarthInMeters = 5000;
MapMeasure mapMeasureZoom = MapMeasure(MapMeasureKind.distance, distanceToEarthInMeters);
_camera.lookAtPointWithMeasure(GeoCoordinates(52.520798, 13.409408), mapMeasureZoom);
try {
_onlineSearchEngine = SearchEngine();
} on InstantiationException {
throw ("Initialization of SearchEngine failed.");
}
try {
// Allows to search on already downloaded or cached map data.
_offlineSearchEngine = OfflineSearchEngine();
} on InstantiationException {
throw ("Initialization of OfflineSearchEngine failed.");
}
_setTapGestureHandler();
_setLongPressGestureHandler();
_showDialog("Note", "Long press on the map to get the address for that location with reverse geocoding.");
}
Future<void> searchButtonClicked() async {
// Search for "Pizza" and show the results on the map.
_searchExample();
// Search for auto suggestions and log the results to the console.
_autoSuggestExample();
}
Future<void> geocodeAnAddressButtonClicked() async {
// Search for the location that belongs to an address and show it on the map.
_geocodeAnAddress();
}
void useOnlineSearchEngineButtonClicked() {
useOnlineSearchEngine = true;
_showDialog('Switched to SearchEngine', 'Requests will be calculated online.');
}
void useOfflineSearchEngineButtonClicked() {
useOnlineSearchEngine = false;
// Note that this app does not show how to download offline maps. For this, check the offline_maps_app example.
_showDialog(
'Switched to OfflineSearchEngine', 'Requests will be calculated offline on cached or downloaded map data.');
}
void _searchExample() {
String searchTerm = "Pizza";
print("Searching in viewport for: " + searchTerm);
_searchInViewport(searchTerm);
}
void _geocodeAnAddress() {
// Set map to expected location.
GeoCoordinates geoCoordinates = GeoCoordinates(52.53086, 13.38469);
double distanceToEarthInMeters = 1000 * 5;
MapMeasure mapMeasureZoom = MapMeasure(MapMeasureKind.distance, distanceToEarthInMeters);
_camera.lookAtPointWithMeasure(geoCoordinates, mapMeasureZoom);
String queryString = "Invalidenstraße 116, Berlin";
print("Finding locations for: $queryString. Tap marker to see the coordinates.");
_geocodeAddressAtLocation(queryString, geoCoordinates);
}
void _setTapGestureHandler() {
_hereMapController.gestures.tapListener = TapListener((Point2D touchPoint) {
_pickMapMarker(touchPoint);
});
}
void _setLongPressGestureHandler() {
_hereMapController.gestures.longPressListener = LongPressListener((GestureState gestureState, Point2D touchPoint) {
if (gestureState == GestureState.begin) {
GeoCoordinates? geoCoordinates = _hereMapController.viewToGeoCoordinates(touchPoint);
if (geoCoordinates == null) {
return;
}
_addPoiMapMarker(geoCoordinates);
_getAddressForCoordinates(geoCoordinates);
}
});
}
Future<void> _getAddressForCoordinates(GeoCoordinates geoCoordinates) async {
SearchOptions reverseGeocodingOptions = SearchOptions.withDefaults();
reverseGeocodingOptions.languageCode = LanguageCode.enGb;
reverseGeocodingOptions.maxItems = 1;
if (useOnlineSearchEngine) {
_onlineSearchEngine.searchByCoordinates(geoCoordinates, reverseGeocodingOptions,
(SearchError? searchError, List<Place>? list) async {
_handleReverseGeocodingResults(searchError, list);
});
} else {
_offlineSearchEngine.searchByCoordinates(geoCoordinates, reverseGeocodingOptions,
(SearchError? searchError, List<Place>? list) async {
_handleReverseGeocodingResults(searchError, list);
});
}
}
// Note that this can be called by the online or offline search engine.
void _handleReverseGeocodingResults(SearchError? searchError, List<Place>? list) {
if (searchError != null) {
_showDialog("Reverse geocoding", "Error: " + searchError.toString());
return;
}
// If error is null, list is guaranteed to be not empty.
_showDialog("Reverse geocoded address:", list!.first.address.addressText);
}
void _pickMapMarker(Point2D touchPoint) {
double radiusInPixel = 2;
_hereMapController.pickMapItems(touchPoint, radiusInPixel, (pickMapItemsResult) {
if (pickMapItemsResult == null) {
// Pick operation failed.
return;
}
List<MapMarker>? mapMarkerList = pickMapItemsResult.markers;
if (mapMarkerList.length == 0) {
print("No map markers found.");
return;
}
MapMarker topmostMapMarker = mapMarkerList.first;
Metadata? metadata = topmostMapMarker.metadata;
if (metadata != null) {
CustomMetadataValue? customMetadataValue = metadata.getCustomValue("key_search_result");
if (customMetadataValue != null) {
SearchResultMetadata searchResultMetadata = customMetadataValue as SearchResultMetadata;
String title = searchResultMetadata.searchResult.title;
String vicinity = searchResultMetadata.searchResult.address.addressText;
_showDialog("Picked Search Result", title + ". Vicinity: " + vicinity);
return;
}
}
double lat = topmostMapMarker.coordinates.latitude;
double lon = topmostMapMarker.coordinates.longitude;
_showDialog("Picked Map Marker", "Geographic coordinates: $lat, $lon.");
});
}
Future<void> _searchInViewport(String queryString) async {
_clearMap();
GeoBox viewportGeoBox = _getMapViewGeoBox();
TextQueryArea queryArea = TextQueryArea.withBox(viewportGeoBox);
TextQuery query = TextQuery.withArea(queryString, queryArea);
SearchOptions searchOptions = SearchOptions.withDefaults();
searchOptions.languageCode = LanguageCode.enUs;
searchOptions.maxItems = 30;
if (useOnlineSearchEngine) {
_onlineSearchEngine.searchByText(query, searchOptions, (SearchError? searchError, List<Place>? list) async {
_handleSearchResults(searchError, list, queryString);
});
} else {
_offlineSearchEngine.searchByText(query, searchOptions, (SearchError? searchError, List<Place>? list) async {
_handleSearchResults(searchError, list, queryString);
});
}
}
// Note that this can be called by the online or offline search engine.
void _handleSearchResults(SearchError? searchError, List<Place>? list, String queryString) {
if (searchError != null) {
// Note: When using the OfflineSearchEngine, the HERE SDK searches only on cached map data and
// search results may not be available for all zoom levels.
// Please also note that it may take time until the required map data is loaded.
// Subsequently, the cache is filled when a user pans and zooms the map.
_showDialog("Search", "Error: " + searchError.toString());
return;
}
// If error is null, list is guaranteed to be not empty.
int listLength = list!.length;
_showDialog("Search for $queryString", "Results: $listLength. Tap marker to see details.");
// Add new marker for each search result on map.
for (Place searchResult in list) {
Metadata metadata = Metadata();
metadata.setCustomValue("key_search_result", SearchResultMetadata(searchResult));
// Note: getGeoCoordinates() may return null only for Suggestions.
_addPoiMapMarkerWithMetadata(searchResult.geoCoordinates!, metadata);
}
}
Future<void> _autoSuggestExample() async {
GeoCoordinates centerGeoCoordinates = _getMapViewCenter();
SearchOptions searchOptions = SearchOptions.withDefaults();
searchOptions.languageCode = LanguageCode.enUs;
searchOptions.maxItems = 5;
TextQueryArea queryArea = TextQueryArea.withCenter(centerGeoCoordinates);
if (useOnlineSearchEngine) {
// Simulate a user typing a search term.
_onlineSearchEngine.suggest(
TextQuery.withArea(
"p", // User typed "p".
queryArea),
searchOptions, (SearchError? searchError, List<Suggestion>? list) async {
_handleSuggestionResults(searchError, list);
});
_onlineSearchEngine.suggest(
TextQuery.withArea(
"pi", // User typed "pi".
queryArea),
searchOptions, (SearchError? searchError, List<Suggestion>? list) async {
_handleSuggestionResults(searchError, list);
});
_onlineSearchEngine.suggest(
TextQuery.withArea(
"piz", // User typed "piz".
queryArea),
searchOptions, (SearchError? searchError, List<Suggestion>? list) async {
_handleSuggestionResults(searchError, list);
});
} else {
// Simulate a user typing a search term.
_offlineSearchEngine.suggest(
TextQuery.withArea(
"p", // User typed "p".
queryArea),
searchOptions, (SearchError? searchError, List<Suggestion>? list) async {
_handleSuggestionResults(searchError, list);
});
_offlineSearchEngine.suggest(
TextQuery.withArea(
"pi", // User typed "pi".
queryArea),
searchOptions, (SearchError? searchError, List<Suggestion>? list) async {
_handleSuggestionResults(searchError, list);
});
_offlineSearchEngine.suggest(
TextQuery.withArea(
"piz", // User typed "piz".
queryArea),
searchOptions, (SearchError? searchError, List<Suggestion>? list) async {
_handleSuggestionResults(searchError, list);
});
}
}
void _handleSuggestionResults(SearchError? searchError, List<Suggestion>? list) {
if (searchError != null) {
print("Autosuggest Error: " + searchError.toString());
return;
}
// If error is null, list is guaranteed to be not empty.
int listLength = list!.length;
print("Autosuggest results: $listLength.");
for (Suggestion autosuggestResult in list) {
String addressText = "Not a place.";
Place? place = autosuggestResult.place;
if (place != null) {
addressText = place.address.addressText;
}
print("Autosuggest result: " + autosuggestResult.title + " addressText: " + addressText);
}
}
Future<void> _geocodeAddressAtLocation(String queryString, GeoCoordinates geoCoordinates) async {
_clearMap();
AddressQuery query = AddressQuery.withAreaCenter(queryString, geoCoordinates);
SearchOptions geocodingOptions = SearchOptions.withDefaults();
geocodingOptions.languageCode = LanguageCode.deDe;
geocodingOptions.maxItems = 30;
if (useOnlineSearchEngine) {
_onlineSearchEngine.searchByAddress(query, geocodingOptions, (SearchError? searchError, List<Place>? list) async {
_handleGeocodingResults(searchError, list, queryString);
});
} else {
_offlineSearchEngine.searchByAddress(query, geocodingOptions,
(SearchError? searchError, List<Place>? list) async {
_handleGeocodingResults(searchError, list, queryString);
});
}
}
// Note that this can be called by the online or offline search engine.
void _handleGeocodingResults(SearchError? searchError, List<Place>? list, String queryString) {
if (searchError != null) {
_showDialog("Geocoding", "Error: " + searchError.toString());
return;
}
String locationDetails = "";
// If error is null, list is guaranteed to be not empty.
for (Place geocodingResult in list!) {
// Note: getGeoCoordinates() may return null only for Suggestions.
GeoCoordinates geoCoordinates = geocodingResult.geoCoordinates!;
Address address = geocodingResult.address;
locationDetails = address.addressText +
". GeoCoordinates: " +
geoCoordinates.latitude.toString() +
", " +
geoCoordinates.longitude.toString();
print("GeocodingResult: " + locationDetails);
_addPoiMapMarker(geoCoordinates);
}
int itemsCount = list.length;
_showDialog("Geocoding result for $queryString. Results: $itemsCount", locationDetails);
}
Future<MapMarker> _addPoiMapMarker(GeoCoordinates geoCoordinates) async {
// Reuse existing MapImage for new map markers.
if (_poiMapImage == null) {
Uint8List imagePixelData = await _loadFileAsUint8List('poi.png');
_poiMapImage = MapImage.withPixelDataAndImageFormat(imagePixelData, ImageFormat.png);
}
MapMarker mapMarker = MapMarker(geoCoordinates, _poiMapImage!);
_hereMapController.mapScene.addMapMarker(mapMarker);
_mapMarkerList.add(mapMarker);
return mapMarker;
}
Future<Uint8List> _loadFileAsUint8List(String fileName) async {
// The path refers to the assets directory as specified in pubspec.yaml.
ByteData fileData = await rootBundle.load('assets/' + fileName);
return Uint8List.view(fileData.buffer);
}
Future<void> _addPoiMapMarkerWithMetadata(GeoCoordinates geoCoordinates, Metadata metadata) async {
MapMarker mapMarker = await _addPoiMapMarker(geoCoordinates);
mapMarker.metadata = metadata;
}
GeoCoordinates _getMapViewCenter() {
return _camera.state.targetCoordinates;
}
GeoBox _getMapViewGeoBox() {
GeoBox? geoBox = _camera.boundingBox;
if (geoBox == null) {
print(
"GeoBox creation failed, corners are null. This can happen when the map is tilted. Falling back to a fixed box.");
GeoCoordinates southWestCorner = GeoCoordinates(
_camera.state.targetCoordinates.latitude - 0.05, _camera.state.targetCoordinates.longitude - 0.05);
GeoCoordinates northEastCorner = GeoCoordinates(
_camera.state.targetCoordinates.latitude + 0.05, _camera.state.targetCoordinates.longitude + 0.05);
geoBox = GeoBox(southWestCorner, northEastCorner);
}
return geoBox;
}
void _clearMap() {
_mapMarkerList.forEach((mapMarker) {
_hereMapController.mapScene.removeMapMarker(mapMarker);
});
_mapMarkerList.clear();
}
}
For more details please check enter link description here

Flutter audioplayers triggers onComplete event before the track is actually completed

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.