Load new data form server on scrolling | Pagination | Flutter/Dart - flutter

I'm trying to implement pagination on my flutter app. The task is to load/fetch new data on scrolling. I'm able to fetch data from the server on every scroll but the thing is every time the same data is fetched.
Here's my code for your review:
Future<Map<String, dynamic>> fetchEvents(String accessToken,int vehicleId,) async {
String url = '${AppConstants.baseUrl}v2/event/paginated?size=10&offset=${offset++}&eventsId=${widget.eventsId}';
final response = await get(
Uri.parse('$url'),
headers: {
'Authorization': "bearer $accessToken",
},
);
Map<String, dynamic> responseBody = json.decode(response.body);
fetchedEvents = [...responseBody['eventResponses']];
for (var event in fetchedEvents) {
setState(() {
events.add(event);
});
}
return responseBody;
}
On every scroll the offset gets updated yet returns the same data.
But on Postman, if I change the offset new set of data gets fetched. Where am I going wrong?
Here's my initState()'s code:
#override
initState() {
super.initState();
loadMoreData(); //initial data is fetched
_controller.addListener(() {
if (_controller.position.pixels == _controller.position.maxScrollExtent) { //data fetched on scroll
loadMoreData();
}
});
}
Here's laodMoreData():
loadMoreData() {
fetchEvents(widget.accessToken, widget.eventId);
}
Please help me overcome this. Thanks in advance.

You dont have to use controller for this
you can add 1 to the itemCount to load next Page
ListView.builder(
itemCount: list.length+1,
itemBuilder: (context, i) {
if(i<list.length)
{
return listItem()
}
else{
loadMoreData()//this function will be called with the last listitem
return Loader() //loader for pagination
}
},
)

Related

How to add json to an autocomplete widget in flutter

Im trying to pass the data from an API to a list so that I can view it in the Autocomplete widget but I keep getting errors. I tried below code.
This is the code I have tried which passes data to the autocomplete as instance of 'Bus'
GetBuilder<BusesListController>(
init: BusesListController(),
builder: (_) {
_.viewPartners();
return DottedBorder(
child: Padding(
padding:
const EdgeInsets.only(left: 8.0),
child: Autocomplete<Bus>(
optionsBuilder: (TextEditingValue
textEditingValue) {
List<Bus> partnercos = [];
partnercos = _.partners.value as List<Bus>;
// (_.partners).map((value) => Bus.fromJson(value as Map<String, dynamic>)).toList();
print(partnercos);
return partnercos
.where((bus) => bus.company!
.toLowerCase()
.contains(textEditingValue
.text
.toLowerCase()))
.toList();
},
)),
);
}),
I also tried passing _.partners directly but it doesn't work either
Other fix I tried is passing _.partners instead of _.partners. Value above which invokes errors in arena.dart in void _tryToResolveArena which shows that state. Members.length == 1 hence scheduleMicrotask(() => _resolveByDefault(pointer, state));
Contoller code
class BusesListController extends GetxController {
var partners = [].obs;
var isLoaded = false.obs;
final loginController = Get.put(LoginController());
Future<void> viewPartners() async {
final token = loginController.rxToken.value;
var headers = {
'Authorization': 'Bearer $token'
};
try {
var url =
Uri.parse(ApiEndPoints.baseUrl + ApiEndPoints.endpoints.listBusAdmin);
http.Response response = await http.get(url, headers: headers);
if (response.statusCode == 200) {
final json = jsonDecode(response.body);
partners. Value =
(json as List).map((json) => Bus.fromJson(json)).toList();
isLoaded.value = true;
} else {
throw jsonDecode(response.body)["Message"] ?? "Unknown Error Occured";
}
} catch (error) {
// Get.snackbar('Error', error.toString());
}
}
#override
void onInit() {
super.onInit();
viewPartners();
}
}
I am able to print the response so I know the api works but I'm having problems with passing partners list into the autocomplete

How can I store the values ​of the json or request in a variable without using future builder or list builder in flutter?

I want to use the information I get from the json or request and be able to use it in a useraccountheader drawer but WITHOUT, using a list builder or future builder.
I usually use a future builder and display the information from the database.
I want to get the json or request information and store it in a variable or use it directly in a text widget.
It is also to have loaded user information.
In the infoinitialuser2 list, the values ​​of the json or request are stored and I show them in the list builder or future builder, but as I just mentioned, I don't want to do it that way.
code::
Class State<NombreCabeceraDrawer> extends StatefulWidget{return nombrecabeceradrawer()}
class nombrecabeceradrawer extends State<NombreCabeceraDrawer> {
verride
void initState() {
cabeceradrawerservices.MostrarInfoInicialUser().then((value) {
setState(() {
info.addAll(value);
});
} );
super.initState();
}
UserAccountsDrawerHeader(
accountName: Text("here i want to show the value of the json or request"),
accountEmai: Text("here i want to show the value of the json or request too")
),
}
-------------------
class InfoUsuarioInicialServices{
Future MostrarInfoInicialUser() async{
Map<String, String> headers = {
'Content-Type':'application/json;charset=UTF-8',
'Charset':'utf-8'
};
var Url= Uri.parse("http://");
final response = await http.get((Url),headers: headers);
print(response.body);
return productInfoUsuarioInicialromJson(response.body);
}
}
---------------------
List productInfoUsuarioInicialromJson(String str) => List<InfoInicialUserModel>.from(json.decode(str).map((x) => InfoInicialUserModel.fromJson(x)));// con esto hago el get
class InfoInicialUserModel{
String UsuarioPk;
String FotoUsuario;
String CorreoUsuario;
String NombreUsuario;
InfoInicialUserModel({this.UsuarioPk,this.FotoUsuario,this.NombreUsuario,this.CorreoUsuario});
factory InfoInicialUserModel.fromJson(Map<String, dynamic> parsedJson){
return InfoInicialUserModel(
UsuarioPk: parsedJson['Usuari'],
FotoUsuario:parsedJson['image'],
NombreUsuario: parsedJson['Usuario_A'],
CorreoUsuario:parsedJson['Usuario_C']
);
}
}
This is how I would do it:
Future GetMostrarInfoInicialUser() async {
Map<String, String> headers = {
'Content-Type': 'application/json;charset=UTF-8',
'Charset': 'utf-8'
};
var Url = Uri.parse("http://");
final response = await http.get((Url), headers: headers);
if (response.statusCode == 200) {
print(response.body);
var jsonData = json.decode(response.body);
if (jsonData == "Error") {
} else {
if (mounted) {
setState(() {
accountEmail = jsonData['accountEmail'];
accountName = jsonData['accountName'];
});
}
}
}
}
#override
void initState() {
GetMostrarInfoInicialUser();
}

Flutter: How to Access Data & NotifyListeners Outside a Stream

I have a list of items and a stream within a class. The stream triggers a future where then notifylisteners is called to update the list of items. It works, but it only shows updates within the stream. How do I notifylistners outside the stream as well?
Where, if I were to call Provider.of(context).items it won't return as empty.
Here is the following code structure.
class Mans with ChangeNotifier {
List<Man> _items = [];
List<Man> get items {
return [..._items];
}
Stream<List<Man>> stream;
bool hasMore;
bool _isLoading;
List<Man> _data;
StreamController<List<Man>> _controller;
Mans({page = 1}) {
_data = List<Man>();
_controller = StreamController<List<Man>>.broadcast();
_isLoading = false;
// Test if list prints #1
items.forEach((list) {
print("nono: ${list.id}");
});
stream = _controller.stream.map((List<Man> mansData) {
// Test if list prints #2
items.forEach((list) {
print("nono: ${list.id}");
});
return mansData;
});
// Test if list prints #3
items.forEach((list) {
print("nono2: ${list.id}");
});
hasMore = true;
refresh();
}
Future<void> refresh() {
return loadMore(
page: 1,
clearCachedData: true,
);
}
Future<void> loadMore(
{bool clearCachedData = false,
page = 1}) async {
if (clearCachedData) {
_data = List<Man>();
hasMore = true;
}
if (_isLoading || !hasMore) {
return Future.value();
}
_isLoading = true;
return await fetchAndSetMans(page)
.then((mansData) {
_isLoading = false;
_data.addAll(mansData);
hasMore = (mansData.isNotEmpty);
_controller.add(_data);
});
}
Future<List<Man>> fetchAndSetMans(page) async {
var cookie = '';
try {
print('Called_API_Mans');
var response = await SiteApi(serverConfig["url"]).getAsync("api_link?view_id=$page");
List<Man> list = [];
for (var item in response) {
//This just adds an instance of Man to the list from a Model not added to this Stack Question. It works.
list.add(Man.fromJson(item));
}
_items = list;
notifyListeners();
return _items;
} catch (error) {
return [];
}
}
As you can see, I placed three different instances where I can print the items after notifyListeners() is called in the Future 'fetchAndSetMans'.
Unfortunately, only in the one where the comment says "Test if list prints #2" does it show that the list has been updated. Basically, within the stream data.
#1 and #3 are empty.
So, anything outside of the stream, notifyListeners() doesn't update the items list.
I wish to know how I can update the value outside the stream when the future is called.
So, if I call a Provider.... like, Provider.of(context).items... I can actually get results.
Thanks, I'd appreciate any help.

When I am using the provider package in Flutter to load data from an API into a list it repeatedly calls the API, how do I fix it?

I am trying to lode data from an api call that retrieves a map, I am able to get the map from the api to display how I want it to, however it repeatedly calls the api meaning the list keeps on refreshing. Even though I have tried setting the listener to false, it works but I have to manually refresh the app for it to work?
Additional Info: Assigning and Retrieving Data
import 'package:http/http.dart' as http;
class Stores with ChangeNotifier {
var s_length;
Future<List<Store>> getStores(String storeCatName) async {
final queryParameters = {
"store_category_name": storeCatName,
};
try {
//TODO this is the issue - must fix.
final uri = Uri.http("url", 'url', queryParameters);
//print(uri);
final response = await http.get(uri);
//print(response.statusCode);
//print(response.body);
if (response.statusCode == 200) {
final List<Store> stores = storeFromJson(response.body);
_stores = stores;
//print(_stores);
print("lenght: ${_stores.length}");
Store store;
for(store in _stores) {
store.products = Products().products(store.storeId);
}
//check if this is correct
notifyListeners();
//return stores;
} else {
print("error1");
return List<Store>();
}
} catch (e) {
print(e.toString());
return List<Store>();
}
//notifyListeners();
print(_stores);
}
List<Store> get favoriteItems {
//return _stores.where((storeItem) => storeItem.isFavorite).toList();
}
bool isNotFull(){
if (_stores.isEmpty){
return true;
} else {
return false;
}
}
int get numberOfStores{
return s_length;
}
List<Store> _stores = [];
List<Store> stores (String storeCatName){
getStores(storeCatName);
//print("cpp; + $s_length");
//notifyListeners();
return _stores;
}
}
final storesProvider = Provider.of<Stores>(
context, listen: false
);
storesProvider.getStores(categoryName);
final providerStoreList = storesProvider.stores(category.storeCategoryName);
Additional Info: Builder for List:
child: ListView.builder(
itemCount: providerStoreList.length,
itemBuilder: (context, index) => ChangeNotifierProvider.value(
value: providerStoreList[index],
child: StoreItem(),
)));
If any additional information is required just let me know. Any help would be greatly appreciated.
Thanks
Use
listen: false;
var ourClient = Provider.of<CartBlock>(context, listen: false);
Setting the listener to false means that your widget won't build again when notifyListeners() is called.
So, that might not be the issue.
The only reason I can think of is calling the API again from the build method,
which might happen if you are using a ListView builder.
So, every time you might be scrolling the ListView your API would call again.

Flutter http request from Rapid Api not loading

I am tying retrieve data from an api on Rapid Api using Dart's http package and displaying it using Flutter however the content never loads and the api doesn't return an error.
class APIService {
// API key
static const _api_key = <MYAPIKEY>;
// Base API url
static const String _baseUrl = "covid-19-data.p.rapidapi.com";
// Base headers for Response url
static const Map<String, String> _headers = {
"content-type": "application/json",
"x-rapidapi-host": "covid-19-data.p.rapidapi.com",
"x-rapidapi-key": _api_key,
};
Future<CovidNumbers> fetchData(
{#required String endpoint, #required Map<String, String> query}) async {
Uri uri = Uri.https(_baseUrl, endpoint, query);
final response = await http.get(uri, headers: _headers);
if (response.statusCode == 200) {
return CovidNumbers.fromJson(json.decode(response.body));
} else {
throw Exception('Failed to load Data');
}
}
}
The method is then called onInit
Future<CovidNumbers> data;
APIService apiService = APIService();
#override
void initState() {
super.initState();
data = apiService.fetchData(
endpoint: "/country", query: {"format": "json", "name": "malta"});
}
And finally I display it in a FutureBuilder
FutureBuilder<CovidNumbers>(
//future: futureCovidNumbers,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(
"Confirmed Cases: ${snapshot.data.confirmed.toString()}");
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return CircularProgressIndicator();
},
));
The app remains stuck on the CircularProgressIndicator and does not display an error.
you future is empty, for that reason always is returning a CircularProgressIndicator, place your "data" variable inside the future and try again