I used infinite_scroll_pagination: ^3.2.0 to implement pagination in the flutter web.
I got response from API but in UI it still shows CircularProgressIndicator. I am using GetX .
UI part is,
PagedListView<int, dynamic>(
pagingController:
collectionController.pagingControllerLibraryDetails,
shrinkWrap: true,
builderDelegate:
PagedChildBuilderDelegate<dynamic>(
itemBuilder: (context, item, indexs) {
return Obx(
() {
return
Text(collectionController.libraryAllContentResponse2.value.data!.rows![indexs].categoryName.toString());
}
);
},
noItemsFoundIndicatorBuilder: (_) =>
CollectionEmptyScreen()))
Api call in Getxcontroller,
final PagingController<int, dynamic> pagingControllerLibraryDetails=
PagingController(firstPageKey: 1);
getLibInsideContent(
pages
) async {
try {
SharedPreferences pref = await SharedPreferences.getInstance();
libraryAllContentRequest. page= pages;
libraryAllContentRequest.pageSize = 10;
libraryAllContentRequest.loggedInUserID = pref.getString("userId");
qry.libraryId = libraryId.value;
libraryAllContentRequest.query = Query(libraryId:libraryId.value
);
try {
var res = await libraryProvider
.getAllLibContent(libraryAllContentRequest.toJson());
if (res.statusCode == 200) {
try {
LibraryAllContentResponse libraryAllContentResponse =
LibraryAllContentResponse.fromJson(res.body);
if (libraryAllContentResponse2.value.data != null) {
final isLastPage = int.parse(libraryAllContentResponse.data!.rows!.length.toString())< 10;
if(isLastPage){
pagingControllerLibraryDetails.appendLastPage(
libraryAllContentResponse.data!.rows!,
);
}
else{
final nextPageKey = pages +1;
pagingControllerLibraryDetails.appendPage( libraryAllContentResponse.data!.rows!, nextPageKey);
}
libraryAllContentResponse.data!.rows!.forEach((element) {
if (!libraryAllContentResponse2.value.data!.rows!.contains(element)) {
libraryAllContentResponse2.value.data!.rows!.add(element);
}
});
} else {
libraryAllContentResponse2.value = libraryAllContentResponse;
}
return libraryAllContentResponse;
} on Exception catch (e) {
// print(e);
return e;
}
}
Get.snackbar('Loding', 'Issue');
return null;
} on Exception catch (e) {
return "e";
}
} on Exception catch (e) {
}
}
I called getLibInsideContent(pages) functions in the success response of another api calls.(getLibInsideContent(1);)
Issue resolved.
Added listener in initstate();
#override
void onInit() {
pagingControllerLibraryDetails.addPageRequestListener((page) => getLibInsideContent(page));
super.onInit();
}
Related
I have a StateNotifierProvider that calls an async function which loads some images from the internal storage and adds them to the AsyncValue data:
//Provider declaration
final paginationImagesProvider = StateNotifierProvider.autoDispose<PaginationNotifier, AsyncValue<List<Uint8List?>>>((ref) {
return PaginationNotifier(folderId: ref.watch(localStorageSelectedFolderProvider), itemsPerBatch: 100, ref: ref);
});
//Actual class with AsyncValue as State
class PaginationNotifier extends StateNotifier<AsyncValue<List<Uint8List?>>> {
final int itemsPerBatch;
final String folderId;
final Ref ref;
int _numberOfItemsInFolder = 0;
bool _alreadyFetching = false;
bool _hasMoreItems = true;
PaginationNotifier({required this.itemsPerBatch, required this.folderId, required this.ref}) : super(const AsyncValue.loading()) {
log("PaginationNotifier created with folderId: $folderId, itemsPerBatch: $itemsPerBatch");
init();
}
final List<Uint8List?> _items = [];
void init() {
if (_items.isEmpty) {
log("fetchingFirstBatch");
_fetchFirstBatch();
}
}
Future<List<Uint8List?>> _fetchNextItems() async {
List<AssetEntity> images = (await (await PhotoManager.getAssetPathList())
.firstWhere((element) => element.id == folderId)
.getAssetListRange(start: _items.length, end: _items.length + itemsPerBatch));
List<Uint8List?> newItems = [];
for (AssetEntity image in images) {
newItems.add(await image.thumbnailData);
}
return newItems;
}
void _updateData(List<Uint8List?> result) {
if (result.isEmpty) {
state = AsyncValue.data(_items);
} else {
state = AsyncValue.data(_items..addAll(result));
}
_hasMoreItems = _numberOfItemsInFolder > _items.length;
}
Future<void> _fetchFirstBatch() async {
try {
_numberOfItemsInFolder = await (await PhotoManager.getAssetPathList()).firstWhere((element) => element.id == folderId).assetCountAsync;
state = const AsyncValue.loading();
final List<Uint8List?> result = await _fetchNextItems();
_updateData(result);
} catch (e, stk) {
state = AsyncValue.error(e, stk);
}
}
Future<void> fetchNextBatch() async {
if (_alreadyFetching || !_hasMoreItems) return;
_alreadyFetching = true;
log("data updated");
state = AsyncValue.data(_items);
try {
final result = await _fetchNextItems();
_updateData(result);
} catch (e, stk) {
state = AsyncValue.error(e, stk);
log("error catched");
}
_alreadyFetching = false;
}
}
Then I use a scroll controller attached to a CustomScrollView in order to call fetchNextBatch() when the scroll position changes:
#override
void initState() {
if (!controller.hasListeners && !controller.hasClients) {
log("listener added");
controller.addListener(() {
double maxScroll = controller.position.maxScrollExtent;
double position = controller.position.pixels;
if ((position > maxScroll * 0.2 || position == 0) && ref.read(paginationImagesProvider.notifier).mounted) {
ref.read(paginationImagesProvider.notifier).fetchNextBatch();
}
});
}
super.initState();
}
The problem is that when the StateNotifierProvider is fetching more data in the async function fetchNextBatch() and I go back on the navigator (like navigator.pop()), Flutter gives me an error:
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Bad state: Tried to use PaginationNotifier after dispose was called.
Consider checking mounted.
I think that the async function responsible of loading data completes after I've popped the page from the Stack (which triggers a Provider dispose).
I'm probably missing something and I still haven't found a fix for this error, any help is appreciated.
The method goes to the line that calls the method that calls it, without moving to the lines after the await keyword
class WordAndMeaning extends StatelessWidget {
WordAndMeaning({super.key});
final viewModel = locator<HomeViewModel>();
#override
Widget build(BuildContext context) {
return Observer(
builder: (_) {
if (viewModel.userDailyRight == null) {
return const CircularProgressIndicator();
} else if (viewModel.userDailyRight == 0) {
return const Text(
"Out of your limit",
style: TextStyle(color: Colors.red, fontSize: 20),
);
} else if (viewModel.userDailyRight != 0 && !viewModel.isWordsPulled) {
viewModel.runPullWord();
if (viewModel.isStartedWordPulling) {
return const CircularProgressIndicator();
}
I run runPullWord after check userDailyRight and isWordsPulled at this stateless widget.
List<String> wordList = [
"pencil",
"headphone",
"bag",
"door",
"dress",
"bin",
];
final String _apiLink = "https://api.dictionaryapi.dev/api/v2/entries/en/";
#action
runPullWord() async {
isStartedWordPulling = true;
learnedWordKey = await pullLearnedWordsKeys();
wordAndMeanList = await pullWord();
setRandomWordIndexRightNow();
}
#action
Future<List<String>> pullLearnedWordsKeys() async {
return (await _firestore
.doc("students/$userID/learnedwords/learnedwords")
.get())
.data()!
.keys
.toList();
}
#action
Future<List<WordModel>> pullWord() async {
List<WordModel> localWordAndMeanList = [];
isWordsPulled = false;
try {
for (String word in learnedWordKey) {
wordList.remove(word);
}
for (int i = 0; i < wordList.length; i++) {
_response = await Dio().get(_apiLink +
wordList[
i]);
_pulledData = _response.data;
List<WordModel> currentWord =
((_pulledData as List).map((e) => WordModel.fromMap(e)).toList());
localWordAndMeanList.add(currentWord[0]);
}
isStartedWordPulling = false;
isWordsPulled = true;
return localWordAndMeanList;
} catch (e) {
debugPrint(e.toString());
return Future.error(e);
}
}
When the line with _response works here, the program goes back to the viewModel.runPullWord() line in the widget as soon as it comes to the await keyword and the pullWord method is not completed(_response is still null). In this way, the pullWord method is repeatedly executed.
This situation made me question my knowledge. Why did this situation occur? How can I solve this?
I'm in a Flutter project using Getx. Every time I enter the screen that lists the records I get an error message as you can see below;
I don't know where I'm going wrong, but I'll leave the main parts of the code. I need to find where I'm going wrong.
Class Repository
Future<List<Post>> getAlbum({
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': '${page}',
'pagesize': '10',
'myPostOnly': isFavoritedPage ? 'true' : 'false',
},
);
final body = response.data['ListPosts'] as List;
return body.map((post) => Post.fromJson(post)).toList();
}
Class Controller
var lstPost = List<Post>.empty(growable: true).obs;
var page = 1;
var isDataProcessing = false.obs;
// For Pagination
ScrollController scrollController = ScrollController();
var isMoreDataAvailable = true.obs;
#override
void onInit() async {
super.onInit();
// Fetch Data
getPost(page);
//For Pagination
paginateTask();
}
void getPost(var page) {
try {
isMoreDataAvailable(false);
isDataProcessing(true);
getAlbum(page).then((resp) {
isDataProcessing(false);
lstPost.addAll(resp);
}, onError: (err) {
isDataProcessing(false);
showSnackBar("Error", err.toString(), Colors.red);
});
} catch (exception) {
isDataProcessing(false);
showSnackBar("Exception", exception.toString(), Colors.red);
}
}
showSnackBar(String title, String message, Color backgroundColor) {
Get.snackbar(title, message,
snackPosition: SnackPosition.BOTTOM,
backgroundColor: backgroundColor,
colorText: Colors.white);
}
void paginateTask() {
scrollController.addListener(() {
if (scrollController.position.pixels ==
scrollController.position.maxScrollExtent) {
print("reached end");
page++;
getMoreTask(page);
}
});
}
void getMoreTask(var page) {
try {
getAlbum(page).then((resp) {
if (resp.length > 0) {
isMoreDataAvailable(true);
} else {
isMoreDataAvailable(false);
showSnackBar("Message", "Não existem registro", Colors.lightBlueAccent);
}
lstPost.addAll(resp);
}, onError: (err) {
isMoreDataAvailable(false);
showSnackBar("Error", err.toString(), Colors.red);
});
} catch (exception) {
isMoreDataAvailable(false);
showSnackBar("Exception", exception.toString(), Colors.red);
}
}
#override
void onClose() {
searchDrawerEC.dispose();
super.onClose();
}
Future<List<Post>> getAlbum(pagina,[bool isFavoritedPage = false]) async {
final response =
await repository.getAlbum(isFavoritedPage: isFavoritedPage);
return response;
}
Class Page
Expanded(
child: ListView.builder(
itemBuilder: (BuildContext context, int index) {
if (index == controller.lstPost.length - 1 &&
controller.isMoreDataAvailable.value == true) {
return Center(child: CircularProgressIndicator());
}
return PostWidget(post: controller.lstPost[index]);
}
),
),
I'm basing myself on this github project.
https://github.com/RipplesCode/FlutterGetXTodoAppWithLaravel/tree/master/lib/app/modules/home
I don't use getx, but I see something odd in your Listview.builder. It feels as if you're abusing it a little, to also show the "no data" case, and there's also no count. I think it should have a count, so something like this:
if (lstPost.isEmpty) {
return Center(child: CircularProgressIndicator());
} else {
return ListView.builder(
itemCount: lstPost.length,
itemBuilder: (BuildContext context, int index) {
return PostWidget(...);
}
);
}
I have a simple flutter code to retrieve some data from Firestore. the data is retireved correctly, however passing the data from the future function making the result always null. can you advise how to adapt the code to return the list?
that is the class where the actual query is happening:
class DatabaseManager {
final CollectionReference BusinessProfilesCollection =
FirebaseFirestore.instance.collection("BusinessProfilesCollection");
Future GetBusinessProfilesCollection() async {
List businessprofileslist = [];
try {
await BusinessProfilesCollection.get().then((QuerySnapshot) {
QuerySnapshot.docs.forEach((element) {
businessprofileslist.add(element.data());
print(businessprofileslist[0]);
});
});
} catch (e) {
print(e.toString());
return null;
}
}
}
here is the page where I am calling the function: (however the result is always null)
class _ProfilesListPageState extends State<ProfilesListPage> {
List businessprofileslist = [];
#override
void initState() {
super.initState();
fetchBusinessProfilesList();
}
fetchBusinessProfilesList() async {
dynamic result = await DatabaseManager().GetBusinessProfilesCollection();
print(result.toString());
if (result == null) {
print('enable to retieve');
} else {
print('success');
setState(() {
businessprofileslist = result;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold();
}
}
You're not returning anything from GetBusinessProfilesCollection but null, so the result seems somewhat expected.
I guess you want to do:
class DatabaseManager {
final CollectionReference BusinessProfilesCollection =
FirebaseFirestore.instance.collection("BusinessProfilesCollection");
Future GetBusinessProfilesCollection() async {
List businessprofileslist = [];
try {
var QuerySnapshot = await BusinessProfilesCollection.get();
querySnapshot.docs.forEach((element) {
businessprofileslist.add(element.data());
});
return businessprofileslist;
} catch (e) {
print(e.toString());
return null;
}
}
}
Btw: returning null when the load fails, is just going to lead to a null pointer exception when you then do print(result.toString());. So I recommend not catching the error and just letting it bubble up. With that your code can be simplified to:
class DatabaseManager {
final CollectionReference BusinessProfilesCollection =
FirebaseFirestore.instance.collection("BusinessProfilesCollection");
Future GetBusinessProfilesCollection() async {
var QuerySnapshot = await BusinessProfilesCollection.get();
return querySnapshot.docs.map((element) => element.data());
}
}
You just need to return the list
return businessprofileslist;
CODE :
class DatabaseManager {
final CollectionReference BusinessProfilesCollection =
FirebaseFirestore.instance.collection("BusinessProfilesCollection");
Future GetBusinessProfilesCollection() async {
List businessprofileslist = [];
try {
await BusinessProfilesCollection.get().then((QuerySnapshot) {
QuerySnapshot.docs.forEach((element) {
businessprofileslist.add(element.data());
print(businessprofileslist[0]);
});
// you just need to return the list here after filling it up
return businessprofileslist;
});
} catch (e) {
print(e.toString());
return null;
}
}
}
Code with a little improvement:
class DatabaseManager {
final CollectionReference BusinessProfilesCollection =
FirebaseFirestore.instance.collection("BusinessProfilesCollection");
Future GetBusinessProfilesCollection() async {
await BusinessProfilesCollection.get().then((QuerySnapshot) {
QuerySnapshot.docs.map((doc) => doc.data()).toList();
});
}
}
Try that with calling the function in feching
fetchBusinessProfilesList()
async {
dynamic result ;
await DatabaseManager().GetBusinessProfilesCollection().then((value){
result=value;
print(result.toString());
if (result == null) {
print('enable to retieve');
} else {
print('success');
setState(() {
businessprofileslist = result;
});
}
});
}
I am trying to use this plugin https://pub.dev/packages/connectivity/example Issue is its not showing or print internet is connected or not.
This is my code
class _HomePageState extends State<HomePage> {
String _connectionStatus = 'Unknown';
final Connectivity _connectivity = Connectivity();
StreamSubscription<ConnectivityResult> _connectivitySubscription;
#override
void initState() {
super.initState();
initConnectivity();
_connectivitySubscription =
_connectivity.onConnectivityChanged.listen(_updateConnectionStatus);
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
if (mounted) {
if (userManager.getCurrentDriver() != null &&
userManager.getCurrentDriver().isNotEmpty) {
FirebaseFirestore.instance
.collection(FIREBASE_PATH_TRIP)
.doc(userManager.getCurrentDriver())
.get()
.then((event) {
if (event != null) {
var trip =
DriverModel.fromMap(Map<String, dynamic>.from(event.data()));
Provider.of<TripState>(context, listen: false).driver = trip;
Provider.of<BottomSheetSelector>(context, listen: false)
.changeSheet(SheetType.Profile);
} else {
userManager.saveCurrentDriver('');
}
});
}
if (Theme.of(context).platform == TargetPlatform.android) {
checkForAndroidUpdate(context);
}
}
});
}
#override
void dispose() {
_connectivitySubscription.cancel();
super.dispose();
}
Future<void> initConnectivity() async {
ConnectivityResult result;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
result = await _connectivity.checkConnectivity();
} on PlatformException catch (e) {
print(e.toString());
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) {
return Future.value(null);
}
return _updateConnectionStatus(result);
}
#override
Widget build(BuildContext context) {
final _drawerKey = GlobalKey<ScaffoldState>();
ScreenUtil.init(context);
return SafeArea(
child: WillPopScope(
child: Scaffold(
key: _drawerKey,
backgroundColor: Colors.black,
resizeToAvoidBottomInset: false,
drawer: ViteDrawer(),
body: null,
),
));
}
Future<void> _updateConnectionStatus(ConnectivityResult result) async {
switch (result) {
case ConnectivityResult.wifi:
case ConnectivityResult.mobile:
case ConnectivityResult.none:
setState(() => _connectionStatus = result.toString());
break;
default:
setState(() => _connectionStatus = 'Failed to get connectivity.');
break;
}
}
}
What i need to do is simple print if internet is connected or not. I want to show alert but print is ok so ill manage it. But dont know why its not printing anything
You can try with this
Future<bool> check() async {
var connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.mobile) {
print("Connected}");
return true;
} else if (connectivityResult == ConnectivityResult.wifi) {
print("Connected}");
return true;
}
print("not Connected}");
// return You can add your dialog for notify user to your connectivity is off
}
you can use below code to check the connectivity
Future<bool> checkInternetConnectivity() async {
try {
final result = await InternetAddress.lookup('google.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
return true;
} else {
return false;
}
} on SocketException catch (_) {
return false;
}
}
simple
Future<bool> isConnected() async {
var result = await Connectivity().checkConnectivity();
return result != ConnectivityResult.none;
}