StreamBuilder doesn't updating items when I get more - flutter

I have a list with all the items from that folder, and I'm retrieving the data with 10 items per time (first load 10, when user reach the list finish, it loads more 10). The problem Is, when list have to be updated, it is not.
It doesn't add the new items in the list.
This is the method I get data from firebase:
Future<void> loadnovo(
{String submenu,
int limit = 10,
bool cls = false,
bool initialLoad = false,
int lastIndex}) async {
if (cls) {
conteudo.clear();
hasMore = true;
}
if (_isLoading || !hasMore) {
return Future.value();
}
_isLoading = true;
var parts = submenu.split('/');
var pathSlashless = parts[0].trim();
var subPathSlashless = parts.sublist(1).join('/').trim();
var snapshot = await _storage.ref().child("/${submenu}");
var retorno = await snapshot.listAll();
if (subPathSlashless.isEmpty || subPathSlashless == null) {
retorno.prefixes.forEach((element) {
conteudo.add(
ItemLab(
tipo: 'PASTA',
elemento: element,
),
);
_streamController.add(conteudo);
});
}
for (int i = lastIndex; i < lastIndex + limit; i++) {
var url = await retorno.items[i].getDownloadURL();
conteudo.add(
ItemLab(
tipo: 'FILE',
elemento: retorno.items[i],
imageUrl: url,
),
);
print(conteudo);
print(conteudo.length);
_streamController.add(conteudo);
}
hasMore = true;
}
This is my Screen with the Stream builder, a gridView (which show the items) and the scrollListener:
LabController ctrlLab;
final lab = LabMdScreen();
inal scrollController = ScrollController();
int lastIndex = 0;
scrollListener() async {
if (scrollController.position.maxScrollExtent == scrollController.offset) {
lastIndex += 10;
ctrlLab.loadList(submenu: "ph/Res", lastIndex: lastIndex);
}
}
#override
void initState() {
ctrlLab = LabController();
ctrlLab.loadList(submenu: "ph/Res", lastIndex: lastIndex,cls: true, initialLoad: true);
scrollController.addListener(scrollListener);
super.initState();
}
#override
void dispose() {
scrollController.removeListener(scrollListener);
super.dispose();
}
loadBasicStructureDetail(submenu ,callback, context, deviceSize){
return StreamBuilder(
stream: ctrlLab.stream,
builder: (ctx, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else if (snapshot.error != null) {
print(snapshot.error);
return Center(child: Text('Ocorreu um erro!'));
}else {
return GridView.builder(
padding: EdgeInsets.all(10.0),
controller: scrollController,
itemCount: snapshot.data.length +1,
itemBuilder: (ctx, i) {
path = callback;
if (i < snapshot.data.length) {
ItemLab item = snapshot.data[i];
>>>> here my code to format the tiles...
What I'm missing here

Try removing
if (_isLoading || !hasMore) {
return Future.value();
}

Related

Concurrent modification during iteration: Instance(length:3) of '_GrowableList'

I am new in flutter and i want to do pagination. I am using one package which name is pull to refresh. All work are doing well but when I reach last data of list then current page is increment with +1 and api call of page=2. in my code its open new page, i want to append with current listview. When i write code for append its throw error "Concurrent modification during iteration: Instance(length:3) of '_GrowableList'."(my english is not good but I hope you are understood).
Following are my code.
int currentPage = 1;
bool isRefersh = false;
Future<UserPost> getUserPost() async {
var url =
"https://myApiLink/getUserPost.php?page=$currentPage";
var response = await http.get(Uri.parse(url));
var jsondata = jsonDecode(response.body.toString());
var _apiData = UserPost.fromJson(jsondata);
if (response.statusCode == 200) {
if(isRefersh == true){
setState((){
isRefersh = false;
});
refreshController.refreshCompleted();
return UserPost.fromJson(jsondata);
}
else{
print(_apiData.hasNextPage.toString());
if(_apiData.hasNextPage == 0){
refreshController.loadNoData();
}else{
refreshController.loadComplete();
}
return UserPost.fromJson(jsondata);
}
} else {
return UserPost.fromJson(jsondata);
}
}
My list view code:
FutureBuilder<UserPost>(
future: getUserPost(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return SmartRefresher(
controller: refreshController,
enablePullUp: true,
child: ListView.builder(
//close keyboard while scroll
keyboardDismissBehavior:
ScrollViewKeyboardDismissBehavior.onDrag,
//itemCount: isFilter? ComparisonText.length : snapshot.data!.data!.length,
itemCount: snapshot.data!.data!.length,
itemBuilder: (context, i) {
var postList = snapshot.data!.data![i];
String tempSearch =
postList.azakhanaName.toString();
)}
onRefersh for refreshing and onLoading for pagination.
onRefresh: () async{
await Future.delayed(Duration(milliseconds: 1000));
setState(() {
isRefersh = true;
currentPage = 1;
});
},
onLoading: () async {
if(snapshot.data!.hasNextPage == 0){
refreshController.loadNoData();
}else{
setState(() {
currentPage++;
snapshot.data!.data!.addAll(snapshot.data!.data!);
});
await Future.delayed(Duration(milliseconds: 1000));
refreshController.loadComplete();
}
},
I am trying to append listview with new data like this and and error also throw in this code:
snapshot.data!.data!.addAll(snapshot.data!.data!);

How to solve the given algorithm issue?

Basically, I am trying to fetch all comments through pagination and it's working nicely. But in case of reply, I am trying to fetch and show reply which comment has reply only and I am showing in listview builder. But problem is all reply is showing below every comment. I am leaving my code.
late bool isReplyBox = false;
final _numberOfCommentPerRequest = 5;
late List<ReplyTile> _replyList = [];
final PagingController<int, CommentModel> _pagingController =
PagingController(firstPageKey: 1);
Future<void> _fetchAllComments(int pageKey) async {
try {
final response = await getAllComments(widget.classId, pageKey);
late List<CommentModel> _commentList = [];
response['data']['results'].forEach((element) {
String userFName = element['creator_first_name'] ?? 'No Name';
String userLName = element['creator_last_name'];
String userName = "$userFName $userLName";
String userImage = element['avatar'] ?? '';
String commentMessage = element['comment'] ?? 'No comment';
int commentId = element['id'] ?? 0;
if(element['reply_comments'] != null){
setState(() {
isReplyBox = true;
});
var replyList = element['reply_comments'];
replyList.forEach((replyData) {
var replyMessage = replyData['comment'] ?? '';
String creatorFName = replyData['creator_first_name'] ?? 'No Name';
String creatorLName = replyData['creator_last_name'];
String creatorName = "$creatorFName $creatorLName";
String creatorImage = replyData['avatar'] ?? '';
setState(() {
_replyList.add(ReplyTile(
commentMessage: replyMessage,
userName: creatorName,
userImagePath: creatorImage));
});
});
}else{
_replyList;
print('No reply comments');
}
_commentList.add(CommentModel(commentId,userImage,userName,commentMessage));
});
final isLastPage = _commentList.length < _numberOfCommentPerRequest;
if (isLastPage) {
_pagingController.appendLastPage(_commentList);
} else {
final nextPageKey = pageKey + 1;
_pagingController.appendPage(_commentList, nextPageKey);
}
} catch (e) {
print("error --> $e");
_pagingController.error = e;
}
}
#override
void initState() {
_pagingController.addPageRequestListener((pageKey) {
_fetchAllComments(pageKey);
});
super.initState();
}
#override
void dispose() {
_pagingController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container(
height: MediaQuery.of(context).size.height / 4,
child: RefreshIndicator(
onRefresh: () => Future.sync(() => _pagingController.refresh()),
child: PagedListView<int, CommentModel>(
pagingController: _pagingController,
builderDelegate: PagedChildBuilderDelegate<CommentModel>(
itemBuilder: (context, item, index) =>
CommentTile(
replyList: isReplyBox == true ? replyChild() : const SizedBox.shrink(),
isReplyBox: isReplyBox,
classId: widget.classId,
commentId: item.commentId,
commentMessage: item.commentMessage,
userName: item.userName,
userImagePath: item.userImagePath)),
),
),
);
}
Widget replyChild() {
final double height = MediaQuery.of(context).size.height / 3.8;
return Container(
height: _replyList.length <= 1 ? 00 : height,
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: _replyList.length,
itemBuilder: (BuildContext context, index) {
return _replyList[index];
}
),
);
}
Could anyone please tell how may I solve this issue? Thanks in advance.

Flutter: Need to load values and then make a firebase query for Futurebuilder caused RangeError (index)

I'm trying to load the geo location first. Then I use this value to start a query which events are in a certain radius from this location.
I want to display these events in a FutureBuilder.
My problem:
I have to initialize Future<List> futureEvents in the onInit state, otherwise Flutter complains. At the time he didn't have the location yet.
So I call the function again at the end.
So it happens that the error "RangeError (index): Invalid value: Valid value range is empty :1" is thrown until the method is called again after receiving the geo-location and I get the data.
Sorry for bad coding. One of my first Projects
The InitState:
class _LocationPageState extends State<LocationPage> {
String? _currentAddress;
Position? _currentPosition;
late Future<List<Events>> futureEvents;
double locationRadius = 5;
#override
void initState() {
super.initState();
_getCurrentPosition();
futureEvents = _getEvents();
}
The called functions:
Future<void> _getCurrentPosition() async {
final hasPermission = await _handleLocationPermission();
if (!hasPermission) return;
await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high)
.then((Position position) {
setState(() => _currentPosition = position);
_getAddressFromLatLng(_currentPosition!);
}).catchError((e) {
debugPrint(e);
});
}
Future<void> _getAddressFromLatLng(Position position) async {
await placemarkFromCoordinates(
_currentPosition!.latitude, _currentPosition!.longitude)
.then((List<Placemark> placemarks) {
Placemark place = placemarks[0];
setState(() {
_currentAddress = ' ${place.postalCode} ${place.locality} ';
});
currentPLZ = place.postalCode.toString();
futureEvents = _getEvents() as Future<List<Events>>;
}).catchError((e) {
debugPrint(e);
});
}
Future<List<Events>> _getEvents() async {
// get all PLZ in a radius
final response = await http.get(Uri.parse(
'https://www.suche-postleitzahl.org/geo-api.json?action=plz-umkreis&subaction=umkreis-osm&plz=' +
currentPLZ +
'&radius=' + locationRadius.toString()));
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
var jsondataPlz = jsonDecode(response.body);
List<PLZData> plzdataList = [];
for (var u in jsondataPlz) {
PLZData plzData = PLZData(u['postal_code'], u['name'], u['distance']);
plzdataList.add(plzData);
}
print(plzdataList.length);
print(plzdataList[1].name +
" Distanz:" +
plzdataList[1].distance +
" PLZ only" +
plzdataList[1].postal_code);
// get all events in the radius
List<Events> events = [];
if (plzdataList.isNotEmpty) {
for (var i = 0; plzdataList.length > i; i++) {
var singleEvent = await FirebaseFirestore.instance
.collection('Events')
.where('postCode', isEqualTo: plzdataList[i].postal_code)
.get();
if (singleEvent.docs.isNotEmpty) {
var singleEventList =singleEvent.docs.map((d) => Events.fromJson(d.data())).toList();
//add distance and regionname
for(var j = 0; singleEventList.length > j; j++){
singleEventList[j].distance = plzdataList[i].distance;
singleEventList[j].regionName = plzdataList[i].name;
}
events = events + singleEventList;
if (events[0].userID != null) {
print(events[0].userID);
print(events[i].distance);
}
}
}
}
//get userdata to the events
if (events.isEmpty) {
print("Es wurden keine Events gefunden");
} else {
for (var i = 0; events.length > i; i++) {
var userInformationSnap = await FirebaseFirestore.instance
.collection('users')
.where('__name__', isEqualTo: events[i].userID)
.get();
events[i].userInformation = userInformationSnap.docs
.map((d) => UsersForPosts.fromJson(d.data()))
.toList();
print(events[i].userInformation[0].username);
}
}
return events;
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load');
}
}
The FutureBuilder
FutureBuilder<List<Events>>(
future: futureEvents,
builder: (context, snapshot) {
if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(), //<--here
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
return Eventcard(
userPhoto: snapshot.data?[index].userInformation[0]
.userPhoto ?? "keine Info",
age: snapshot.data?[index].userInformation[0].age ??
"keine Info",
username: snapshot.data?[index].userInformation[0]
.username ?? "keine Info",
gender: snapshot.data?[index].userInformation[0]
.gender ?? "keine Info",
meetState: snapshot.data?[index].meetState ??
"keine Info",
postCode: snapshot.data?[index].postCode ??
"keine Info",
distance: snapshot.data?[index].distance ??
"keine Info",
regionName: snapshot.data?[index].regionName ??
"keine Info",
comment: snapshot.data?[index].comment ??
"keine Info",
headline: snapshot.data?[index].headline ??
"keine Info",
);
},
);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
} else {
// By default, show a loading spinner.
return const CircularProgressIndicator();
}
},
),
Have you tried to just put the _getEvents() Future inside the FutureBuilder directly instead of using it as a late variable? I’m also confused by this format… why use a Future and a FutureBuilder? It seems like you could just create an empty list events = [] then in the initState call a new async function where you fetch the events from Firestore and use that data to update the events list through setState. Then take away the FutureBuilder and just use a ListView.builder. Just search how to create a ListView from a list, it’s very simple.

How to use pagination in flutter using FutureBuilder with ListView?

I'm new as a Flutter programmer and I'm having difficulty working with a list that has pagination, if I remove the pagination the list doesn't load. Need help. The state manager I'm using is Getx.
This is my DTO class.
class PaginationFilter {
int? page;
int? pagesize;
#override
String toString() {
return 'PaginationFilterAlbum{page: $page, pagesize: $pagesize}';
}
#override
bool operator ==(Object other) =>
identical(this, other) ||
other is PaginationFilter &&
runtimeType == other.runtimeType &&
page == other.page &&
pagesize == other.pagesize;
#override
int get hashCode => page.hashCode ^ pagesize.hashCode;
}
Here is where I consume the API.
Future<List<Post>> getAlbum(PaginationFilter filter, {
bool isFavoritedPage = false,
bool isNewEdition = false,
}) async {
dio.options.headers['Cookie'] = 'ASP.NET_SessionId=${user.sessionID}';
final response = await dio.get(
isFavoritedPage ? AppConstants.apiFavoritedsPost : AppConstants.apiPosts,
queryParameters: {
'sessionId': user.sessionID,
'CodUserProfile': '${user.codUser!}',
'CodUserLogged': '${user.codUser!}',
'Page': '${filter.page}', /*Aqui fica a pagina*/
'pagesize': '${filter.pagesize}', /*Aqui a quantidade de registros em cada pagina*/
'myPostOnly': isFavoritedPage ? 'true' : 'false',
},
);
final body = response.data['ListPosts'] as List;
return body.map((post) => Post.fromJson(post)).toList();
}
Here is my controller class
final _posts = <Post>[].obs;
var response = null;
final _paginationFilterAlbum = PaginationFilter().obs;
int? get pagesize => _paginationFilterAlbum.value.pagesize;
int? get _page => _paginationFilterAlbum.value.page;
final _lastPageAlgum = false.obs;
bool get lastPageAlgum => _lastPageAlgum.value;
#override
void onInit(){
ever(_paginationFilterAlbum, (_) => getAlbum());
_changePaginationFilterAlgum(1, 10);
super.onInit();
}
void _changePaginationFilterAlgum(int page, int pagesize){
_paginationFilterAlbum.update((val) {
val?.page = page;
val?.pagesize = pagesize;
});
}
void changeTotalPerPage(int limitValue){
_posts.clear();
_lastPageAlgum.value = false;
_changePaginationFilterAlgum(1, limitValue);
}
void nextPage() => _changePaginationFilterAlgum(_page! + 1, pagesize!);
Future<List<Post>> getAlbum([bool isFavoritedPage = false]) async {
response =
await repository.getAlbum(_paginationFilterAlbum.value,isFavoritedPage: isFavoritedPage);
if(response.isEmpty){
_lastPageAlgum. value = true;
}
_posts.addAll(response);
return response;
}
here is my my list
Expanded(
child: FutureBuilder<List<Post>>(
future: controller.getAlbum(),
builder: (context, snapshot) {
final List<Post> posts = snapshot.data ?? [];
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CupertinoActivityIndicator());
}
return ListView.builder(
physics: const BouncingScrollPhysics(),
itemCount: posts.length,
itemBuilder: (context, index) {
return PostWidget(post: posts[index]);
},
);
},
),
),
Could you please help me how do I load the entire list using pagination?

How to return Future List from DataSnapshot

I want to return a Future List from Firebase Database snapshot and this is my code but I cant get it work properly:
Future<List<CocheDetailItem>> getCoches(ids) async {
List<CocheDetailItem> coches = [];
final dbRef = FirebaseDatabase.instance.reference().child('17082019');
for (var i = 0; i < ids.length; i++) {
var id = ids[i];
dbRef.child(id).once().then((DataSnapshot snapshot) {
if (snapshot.value != null) {
Map<dynamic, dynamic> jsres = snapshot.value;
CocheDetailItem coche = CocheDetailItem.fromJson(jsres);
coches.add(coche);
}
});
print('here is i ${ids[i]} ');
}
return coches;
}
The return I get is empty Area. Can anyone help me with this, please?
Note, dbRef.child(id).once(); is a async function, so you must wait it ends to get your data. Use await keyword to do it.
Future<List<CocheDetailItem>> getCoches(ids) async {
List<CocheDetailItem> coches = [];
final dbRef = FirebaseDatabase.instance.reference().child('17082019');
for (var i = 0; i < ids.length; i++) {
var id = ids[i];
var dataSnapshot = await dbRef.child(id).once();
if (dataSnapshot.value != null) {
Map<dynamic, dynamic> jsres = dataSnapshot.value;
CocheDetailItem coche = CocheDetailItem.fromJson(jsres);
coches.add(coche);
}
print('here is i ${ids[i]} ');
}
return coches;
}
well.. I don't use firebase but I send a request to my database with this (you have to use async and await)
Future<List<PlaceModel>> getPlaces(String ciudad, String tipo) async {
Uri request = Uri.http('domain.com', '/getPlaces/$ciudad/$tipo');
ResponseModel response = ResponseModel.fromJsonMap(json.decode((await http.get(request)).body));
List<PlaceModel> items = [];
if(response.res) {
if(response.value != null) {
for(var item in response.value) {
final place = PlaceModel.fromJsonMap(item);
items.add(place);
}
}
}
print("Places Loaded: ${items.length}");
return items;
}
I use my ResponseModel to convert the json answer in an object.
Then I show it with the future builder:
class PlacesListPage extends StatelessWidget{
final _selectedLocation, _selectedList;
PlacesListPage(this._selectedLocation, this._selectedList);
final _provider = PlaceProvider();
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(8.0),
child: FutureBuilder(
future: _provider.getPlaces(_selectedLocation, _selectedList), // async request to database
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) { // check when your request is done
if(snapshot.data.length != 0) { // check if any data has been downloaded
return ListView.builder( // build a listview of any widget with snapshot data
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
// i just return containers but you can use any custom widget, it's like a forEach and use the index var
return Container(
child: Text(snapshot.data[index]),
);
},
);
} else {
// If you don't have anything in your response shows a message
return Text('No data');
}
} else {
// shows a charge indicator while the request is made
return Center(
child: CircularProgressIndicator(),
);
}
},
),
);
}
}