How to implement pull_to_refresh to streambuilder - flutter

i'm trying to implement pull_to_refresh . I have two stateless widget that i will use . First is _ProductCategoryDetailPageState here is the code
#override
Widget build(BuildContext context) {
return Material(
child: Scaffold(
appBar: AppBar(
title: Text(widget.categoryName),
),
body: Container(
height: MediaQuery.of(context).size.height,
child: Column(
children: <Widget>[
Divider(
height: 20.0,
),
Flexible(
child:StreamBuilder<List<Products>>(
stream: _productController.stream,
builder: (context, snapshot) {
if (snapshot.hasError) {
return errMess(context, "Failed to fetch data");
} else {
if (snapshot.hasData) {
if (snapshot.data.length > 0) {
return ProductList(category: snapshot.data);
} else {
return errMess(context,
"There is no available product in this category");
}
} else {
return errMess(context,
"There is no available product in this category");
}
}
},
)),
Divider(
height: 25.0,
),
],
),
)));
}
loadProduct(String categoryId, int limit, int offset) async {
List<Products> products = await fetchProducts(http.Client(), categoryId, limit, offset);
_productController.sink.add(products);
}
static List<Products> parseProducts(String responseBody) {
final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Products>((json) => Products.fromJson(json)).toList();
}
Future<List<Products>> fetchProducts(http.Client client, String categoryId, int limit, int offset) async {
final response = await http.post(Configuration.url +
"api/getProducts/" +
categoryId +
"/" +
limit.toString() +
"/" +
offset.toString());
if (response.statusCode < 200 || response.statusCode > 300) {
throw new Exception('Failed to fetch data');
} else {
return compute(parseProducts, response.body);
}
}
and here is my second stateless widget
class _ProductListState extends State<ProductList> {
int limit = 0;
int offset = 4;
RefreshController _refreshController2 =
RefreshController(initialRefresh: false);
#override
Widget build(BuildContext context) {
return SmartRefresher(
child: new GridView.builder(
itemCount: widget.category.length,
gridDelegate:
new SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemBuilder: (BuildContext context, int index) {
return new GestureDetector(
onTap: () {
print("Product detail");
},
child: Card(
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: Image.network(
Configuration.url +
"assets/app_assets/" +
widget.category[index].productImage,
width: 250,
height: 250,
filterQuality: FilterQuality.low,
),
),
SizedBox(
height: 25,
),
Text(
widget.category[index].productName,
style: TextStyle(fontSize: 15.0),
),
SizedBox(
height: 25,
),
],
),
),
);
},
),
controller: _refreshController2,
enablePullUp: true,
header: MaterialClassicHeader(),
onRefresh: () {
_onRefresh(_refreshController2, widget.category,
widget.category[0].categoryId);
},
onLoading: () {
_onLoading(_refreshController2, widget.category,
widget.category[0].categoryId);
},
);
}
void _onLoading(RefreshController controller, List<Products> data,String categoryId) async {
await Future.delayed(Duration(milliseconds: 2000));
setState(() {
limit = limit + offset;
offset = 6;
});
_ProductCategoryDetailPageState().loadProduct(categoryId, limit, offset);
controller.loadComplete();
}
void _onRefresh(RefreshController controller, List<Products> data,
String categoryId) async {
await Future.delayed(Duration(milliseconds: 1000));
controller.refreshCompleted();
}
}
when i pull the grid there is no error , but the data is not change. After i check on this part
Flexible(
child:StreamBuilder<List<Products>>(
stream: _productController.stream,
builder: (context, snapshot) {
print("run")
if (snapshot.hasError) {
return errMess(context, "Failed to fetch data");
} else {
if (snapshot.hasData) {
if (snapshot.data.length > 0) {
return ProductList(category: snapshot.data);
} else {
return errMess(context,
"There is no available product in this category");
}
} else {
return errMess(context,
"There is no available product in this category");
}
}
},
)),
As you can see i adding print("run") , it just only showing once.
my full script https://gist.github.com/bobykurniawan11/04f2584c6de97f1d9324bfe3b24f669f

This won't work as you are creating a new State instance of the State object. You should connect both widgets with a callback for example.
_ProductCategoryDetailPageState().loadProduct(categoryId, limit, offset);
Like this:
// Custom callback function
typedef void OnLoadProductsFunction(String categoryId, int limit, int offset);
class ProductList extends StatefulWidget {
OnLoadProductsFunction onLoad;
ProductList({
this.category,
this.onLoad,
})
}
...
void _onLoading(RefreshController controller, List<Products> data,String categoryId) async {
await Future.delayed(Duration(milliseconds: 2000));
setState(() {
limit = limit + offset;
offset = 6;
});
widget.onLoad(categoryId, limit, offset);
controller.loadComplete();
}
...
// In the parent widget
return ProductList(
category: snapshot.data
onLoad: (categoryId, limit, offset) {
loadProduct(categoryId, limit, offset);
}
);
Doing it this way the streamcontroller will be updated from the callback function. Other option you have is to pass the StreamController instance to the ProductList widget and is the child who adds the list of products to the sink.

Related

Flutter : scrollController.isAttached is always false

How can I scroll to a special widget in a ListView? For example, I want to automatically scroll to some container in ListView if I press a certain button on a previous screen. I will pass to the next screen an Id (from id I will know the index) and when I navigate to the next screen I want to automatically scroll to this widget.
the code in main screen : Navigator.push(context, MaterialPageRoute(builder: (_) => CreatedEstatesScreen(estateId: id)));
the code in the next screen :
class RecentEstateOrdersScreen extends StatefulWidget {
static const String id = "RecentEstateOrdersScreen";
String? estateId;
RecentEstateOrdersScreen({Key? key, this.estateId}) : super(key: key);
#override
_RecentEstateOrdersScreenState createState() =>
_RecentEstateOrdersScreenState();
}
class _RecentEstateOrdersScreenState extends State<RecentEstateOrdersScreen> {
late RecentEstatesOrdersBloc _recentEstatesOrdersBloc;
late ItemScrollController scrollController;
late ItemPositionsListener itemPositionsListener;
String? userToken;
List<EstateOrder> orders = [];
#override
void initState() {
super.initState();
_recentEstatesOrdersBloc = RecentEstatesOrdersBloc(EstateOrderRepository());
_onRefresh();
User? user = BlocProvider.of<UserLoginBloc>(context).user;
if (user != null && user.token != null) {
userToken = user.token;
}
scrollController = ItemScrollController();
itemPositionsListener = ItemPositionsListener.create();
}
_onRefresh() {
if (BlocProvider.of<UserLoginBloc>(context).user!.token != null) {
_recentEstatesOrdersBloc.add(
RecentEstatesOrdersFetchStarted(
token: BlocProvider.of<UserLoginBloc>(context).user!.token!),
);
}
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text(
AppLocalizations.of(context)!.recent_created_orders,
),
),
body: BlocConsumer<RecentEstatesOrdersBloc, RecentEstatesOrdersState>(
bloc: _recentEstatesOrdersBloc,
listener: (context, recentOrdersState) async {
if (recentOrdersState is RecentEstatesOrdersFetchError) {
var error = recentOrdersState.isConnectionError
? AppLocalizations.of(context)!.no_internet_connection
: recentOrdersState.error;
await showWonderfulAlertDialog(
context, AppLocalizations.of(context)!.error, error);
}
},
builder: (BuildContext context, recentOrdersState) {
if (recentOrdersState is RecentEstatesOrdersFetchProgress) {
return const ClientsOrdersShimmer();
}
if (recentOrdersState is! RecentEstatesOrdersFetchComplete) {
return Container();
}
orders = recentOrdersState.estateOrders;
if (orders.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
documentOutlineIconPath,
width: 0.5.sw,
height: 0.5.sw,
color: Theme.of(context)
.colorScheme
.onBackground
.withOpacity(0.42),
),
48.verticalSpace,
Text(
AppLocalizations.of(context)!.have_not_recent_orders,
style: Theme.of(context).textTheme.headline4,
),
],
),
);
}
if (widget.estateId != null) {
SchedulerBinding.instance!.addPostFrameCallback((_) {
jumpToOrder(orders);
});
}
return RefreshIndicator(
color: Theme.of(context).colorScheme.primary,
onRefresh: () async {
_onRefresh();
},
child: ListView.builder(
itemCount: orders.length,
itemBuilder: (_, index) {
return EstateOrderCard(
estateOrder: orders.elementAt(index),
);
}),
);
},
),
),
);
}
jumpToOrder(List<EstateOrder> orders) {
int index = getIndexFromId(orders);
if (index != -1) {
if (scrollController.isAttached) {
scrollController.scrollTo(
index: index,
duration: const Duration(seconds: 2),
curve: Curves.easeInOutCubic);
}
}
}
getIndexFromId(List<EstateOrder> orders) {
for (int i = 0; i < orders.length; i++) {
if (orders.elementAt(i).id == int.parse(widget.estateId!)) {
return i;
}
}
return -1;
}
}```
If you are using the library then you have to use ScrollablePositionedList.builder, not the normal ListView.builder.

Flutter : Image.memory widget unable to load

We are trying to create custom gallery page, so that we have followed some tutorial website but facing some issue unable to compile the project.
Tutorial link ::
https://medium.com/#mhstoller.it/how-to-create-a-custom-media-picker-in-flutter-to-select-photos-and-videos-from-the-gallery-988eea477643
class MediaGrid extends StatefulWidget {
#override
_MediaGridState createState() => _MediaGridState();
}
class _MediaGridState extends State<MediaGrid> {
List<Widget> _mediaList = [];
int currentPage = 0;
late int lastPage;
#override
void initState() {
super.initState();
_fetchNewMedia();
}
_handleScrollEvent(ScrollNotification scroll) {
if (scroll.metrics.pixels / scroll.metrics.maxScrollExtent > 0.33) {
if (currentPage != lastPage) {
_fetchNewMedia();
}
}
}
Future<ui.Image> loadImage(Uint8List img) async {
final Completer<ui.Image> completer = Completer();
ui.decodeImageFromList(img, (ui.Image img) {
return completer.complete(img);
});
return completer.future;
}
static Future<ui.Image> bytesToImage(Uint8List imgBytes) async{
ui.Codec codec = await ui.instantiateImageCodec(imgBytes);
ui.FrameInfo frame = await codec.getNextFrame();
return frame.image;
}
_fetchNewMedia() async {
lastPage = currentPage;
final PermissionState _ps = await PhotoManager.requestPermissionExtend();
if (_ps.isAuth) {
// success
//load the album list
List<AssetPathEntity> albums =
await PhotoManager.getAssetPathList(onlyAll: true);
print(albums);
List<AssetEntity> media =
await albums[0].getAssetListPaged(page: currentPage, size: 60);
// await albums[0].getAssetListPaged(currentPage, 60);
List<Widget> temp = [];
for (var asset in media) {
temp.add(
FutureBuilder(
future: asset.thumbnailDataWithSize(const ThumbnailSize(200, 200)),
builder: (BuildContext context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Stack(
children: [
// Image.asset(
// "assets/images/gallery.png",
// fit: BoxFit.cover,
// ),
Positioned.fill(
child: Image.memory(
snapshot.data,
fit: BoxFit.cover,
),
),
if (asset.type == AssetType.video)
const Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: EdgeInsets.only(right: 5, bottom: 5),
child: Icon(
Icons.videocam,
color: Colors.white,
),
),
),
],
);
}
return Container();
},
),
);
}
setState(() {
_mediaList.addAll(temp);
currentPage++;
});
} else {
// fail
/// if result is fail, you can call `PhotoManager.openSetting();` to open android/ios applicaton's setting to get permission
}
}
#override
Widget build(BuildContext context) {
return NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification scroll) {
return _handleScrollEvent(scroll);
},
child: GridView.builder(
itemCount: _mediaList.length,
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
itemBuilder: (BuildContext context, int index) {
return _mediaList[index];
}),
);
}
}
The following code not working,
Positioned.fill(
child: Image.memory(
snapshot.data,**//Error here**
fit: BoxFit.cover,
),
),
I have updated the dependencies to photo_manager 2.1.2 and changed required method accordingly.
Finally its working after Type Casting it.
snapshot.data as Uint8List,
Positioned.fill(
child: Image.memory(
snapshot.data! as Uint8List,
fit: BoxFit.cover,
),
),

how to change the circular progress indicator after loading the data Flutter

the circular progress indicator dont disappear after loading the data .
this is my code where im using the progress indicator
and when i reach the end of the grid view it should load the other data but
the progress indicator makes the same thing it loads and dont disappear after getting data .
i tried to make a boolean isLoading and tried to change it true or false but couldnt find the place where i can do this
int pageNumber = 1;
String filterName = '';
class ShowsListDesign extends StatefulWidget {
#override
_ShowsListDesignState createState() => _ShowsListDesignState();
}
class _ShowsListDesignState extends State<ShowsListDesign> {
ScrollController controller = ScrollController();
ServicesClass service = ServicesClass();
bool isLoading = false;
#override
void initState() {
controller.addListener(listenScrolling);
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: service.getFilms('posts/$pageNumber/$filterName'),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.hasData) {
return Stack(
alignment: Alignment.bottomCenter,
children: [
GridView.builder(
itemCount: snapshot.data.length,
gridDelegate: const
SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 250,
crossAxisSpacing: 24,
mainAxisSpacing: 24,
childAspectRatio: (3 / 5),
),
controller: controller,
itemBuilder: (context, index) {
return FilmsCard(
image: snapshot.data[index]['thumbnailUrl'],
title: snapshot.data[index]['title'],
year: snapshot.data[index]['year'],
);
},
),
FloatingActionButton(
onPressed: () {
scrollUp();
},
elevation: 24,
backgroundColor: PRIMARY,
child: const Text(
'Scroll Up',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
),
),
),
],
);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
);
}
void scrollUp() {
const double start = 0;
controller.animateTo(start,
duration: const Duration(seconds: 1, milliseconds: 50),
curve: Curves.easeIn);
}
void listenScrolling() {
if (controller.position.atEdge) {
final isTop = controller.position.pixels == 0;
if (isTop) {
} else {
setState(() {
pageNumber++;
ShowsListDesign();
});
}
}
}
}
It is possible to get errors or no data on future, it will be better with handling those states.
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
} else if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
return Stack(
alignment: Alignment.bottomCenter,
children: [...],
);
} else if (!snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
return const Text("couldn't find any data");
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
} else {
return const Text(" any other");
}
},
More about FutureBuilder class.
You can't use FutureBuilder if it's not a one page load. Try this:
(I don't understand your scrolling mechanism though), also call super.initState when you override
String filterName = '';
class ShowsListDesign extends StatefulWidget {
#override
_ShowsListDesignState createState() => _ShowsListDesignState();
}
class _ShowsListDesignState extends State<ShowsListDesign> {
ScrollController controller = ScrollController();
ServicesClass service = ServicesClass();
bool isLoading = false;
// New
int pageNumber = 1;
List? data;
Future<void> load() async {
setState(() {
isLoading = true;
});
data = await service.getFilms('posts/1/$filterName');
setState(() => isLoading = false);
}
#override
void initState() {
// Make sure you call super.initState() when you override
controller.addListener(listenScrolling);
super.initState();
load();
}
// Also call dispose to remove listener
#override
void dispose() {
controller.removeListener(listener);
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Builder(
builder: (BuildContext context) {
if (data != null) {
return Stack(
alignment: Alignment.bottomCenter,
children: [
GridView.builder(
itemCount: data!.length,
gridDelegate: const
SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 250,
crossAxisSpacing: 24,
mainAxisSpacing: 24,
childAspectRatio: (3 / 5),
),
controller: controller,
itemBuilder: (context, index) {
return FilmsCard(
image: data![index]['thumbnailUrl'],
title: data![index]['title'],
year: data![index]['year'],
);
},
),
FloatingActionButton(
onPressed: () {
scrollUp();
},
elevation: 24,
backgroundColor: PRIMARY,
child: const Text(
'Scroll Up',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
),
),
),
],
);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
);
}
void scrollUp() {
const double start = 0;
controller.animateTo(start,
duration: const Duration(seconds: 1, milliseconds: 50),
curve: Curves.easeIn);
}
Future<void> listenScrolling() async {
// Change this depending on the scrolling works ?!
if (controller.position.pixels == controller.position.maxScrollExtent && data != null) {
List new_data = await service.getFilms('posts/${pageNumber + 1}/$filterName');
data.addAll(new_data);
setState(() {
pageNumber++;
});
}
}
}

Flutter weird bug with widget function, sometimes in runs 2 times instead of one

This function have futureBuilder inside and returns another function that returns ListView.
Also I do not want the user to download information from the Internet every time they visit the page, so I put the information downloaded from the Internet into a static variable in class, so i make if else. If this static variable length != 0 , it return me listView instead of make server request with futureBuilder
function that works 2 times instead of one is ListView body
And also this bug created after i added purchases to my application, maybe something wrong there. I don't have any ideas why this happend
this is my complete code
class AddCheckList extends StatefulWidget {
const AddCheckList({Key? key}) : super(key: key);
#override
_AddCheckListState createState() => _AddCheckListState();
}
class _AddCheckListState extends State<AddCheckList> {
String xQueryReqestForCheckListNames = "element CheckListList {for \$a in PACK/OBJECT where (\$a/#inUse = 'True') order by \$a/#name return element CheckList {attribute name {\$a/#name}, attribute sPaid {\$a/#isPaid},attribute oid {\$a/#oid} }}";
String serverLink = "http...";
int addCheckListMethod = 0;
// if addCheckListMethod == 0, then data will download from server and checkLists will be added from server xml data
// if addCheckListMethod == 1, then data will download from assets, and checkLists will be added from assets xml data with getXmlData function
final InAppPurchase _inAppPurchase = InAppPurchase.instance;
final String _productID = '1d7ea644f690ffa';
bool _available = true;
List<ProductDetails> _products = [];
List<PurchaseDetails> _purchases = [];
StreamSubscription<List<PurchaseDetails>>? _subscription;
#override
void initState() {
final Stream<List<PurchaseDetails>> purchaseUpdated = _inAppPurchase.purchaseStream;
_subscription = purchaseUpdated.listen((purchaseDetailsList) {
setState(() {
_purchases.addAll(purchaseDetailsList);
_listenToPurchaseUpdated(purchaseDetailsList);
});
}, onDone: () {
_subscription!.cancel();
}, onError: (error) {
_subscription!.cancel();
});
_initialize();
super.initState();
}
#override
void dispose() {
_subscription!.cancel();
super.dispose();
}
void _initialize() async {
_available = await _inAppPurchase.isAvailable();
List<ProductDetails> products = await _getProducts(
productIds: Set<String>.from(
[_productID],
),
);
setState(() {
_products = products;
});
}
void _listenToPurchaseUpdated(List<PurchaseDetails> purchaseDetailsList) {
purchaseDetailsList.forEach((PurchaseDetails purchaseDetails) async {
switch (purchaseDetails.status) {
case PurchaseStatus.pending:
// _showPendingUI();
break;
case PurchaseStatus.purchased:
case PurchaseStatus.restored:
// bool valid = await _verifyPurchase(purchaseDetails);
// if (!valid) {
// _handleInvalidPurchase(purchaseDetails);
// }
break;
case PurchaseStatus.error:
print(purchaseDetails.error!);
// _handleError(purchaseDetails.error!);
break;
default:
break;
}
if (purchaseDetails.pendingCompletePurchase) {
await _inAppPurchase.completePurchase(purchaseDetails);
}
});
}
Future<List<ProductDetails>> _getProducts({required Set<String> productIds}) async {
ProductDetailsResponse response = await _inAppPurchase.queryProductDetails(productIds);
return response.productDetails;
}
void _subscribe({required ProductDetails product}) {
final PurchaseParam purchaseParam = PurchaseParam(productDetails: product);
_inAppPurchase.buyNonConsumable(
purchaseParam: purchaseParam,
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(padding: EdgeInsets.all(14.0), child: listViweBody(addCheckListMethod, serverLink, xQueryReqestForCheckListNames, _products,_subscribe)),
);
}
}
Widget listView(addCheckListMethod, serverLink, product,_subscribe) {
return ListView.separated(
itemCount: CheckListModel.checkListModelNamesFromServer.length,
itemBuilder: (BuildContext context, int index) {
if (CheckListModel.checkListModelNamesFromServer[index].ispaid == false) {
return InkWell(
onTap: () async {
CheckListModel checkList = CheckListModel('', 0, '', 0, 0, 0, '', [], '');
if (addCheckListMethod == 0) {
String xQueryReqestForCheckList = 'for \$a in PACK/OBJECT where \$a/#name="${CheckListModel.checkListModelNamesFromServer[index].name}" return \$a';
var data = await CheckListModel.getDataFromServer(xQueryReqestForCheckList, serverLink);
CheckListModel.addCheckListFromServer(checkList, data);
} else {
CheckListModel.addCheckList(index, checkList);
}
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Добавить описание"),
content: Container(
decoration: BoxDecoration(border: Border.all(color: Color.fromARGB(Desing.colorFromARGBBtn[0], Desing.colorFromARGBBtn[1], Desing.colorFromARGBBtn[2], Desing.colorFromARGBBtn[3]), width: 1), borderRadius: BorderRadius.circular(10)),
child: TextField(
decoration: new InputDecoration.collapsed(hintText: "Описание", border: InputBorder.none),
maxLines: null,
onChanged: (String value) async {
checkList.description = value;
},
),
),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text("Отменить"),
),
TextButton(
onPressed: () async {
await checkList.writeToFile(checkList.toJson());
CheckListModel.checkLists.add(checkList);
Navigator.pushNamed(context, '/beforeMainCheckList', arguments: {'index': CheckListModel.checkLists.length - 1});
},
child: Text('Добавить'))
],
);
});
},
child: Container(
decoration: BoxDecoration(border: Border.all(color: Color.fromARGB(Desing.colorFromARGBBtn[0], Desing.colorFromARGBBtn[1], Desing.colorFromARGBBtn[2], Desing.colorFromARGBBtn[3]), width: 1), borderRadius: BorderRadius.circular(10)),
width: 50,
height: 80,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"${CheckListModel.checkListModelNamesFromServer[index].name}",
style: TextStyle(fontSize: 30),
)
],
),
),
);
} else {
if (product.length != 0) {
return showPurchaseCheckLists(product, index,_subscribe);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}
},
separatorBuilder: (BuildContext context, int index) {
return Container(
height: 14,
);
});
}
Widget showPurchaseCheckLists(product, index,_subscribe) {
int getCurrentProduct() {
String? checkListModelid = CheckListModel.checkListModelNamesFromServer[index].checkListId?.toLowerCase();
int indexx = 0;
for (int i = 0; i < product.length; i++) {
if (checkListModelid == product[i].id) {
indexx = i;
}
}
return indexx;
}
return InkWell(
child: Container(
decoration: BoxDecoration(border: Border.all(color: Color.fromARGB(Desing.colorFromARGBBtn[0], Desing.colorFromARGBBtn[1], Desing.colorFromARGBBtn[2], Desing.colorFromARGBBtn[3]), width: 1), borderRadius: BorderRadius.circular(10), color: Color.fromARGB(255, 240, 240, 240)),
width: 50,
height: 80,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"${CheckListModel.checkListModelNamesFromServer[index].name}",
style: TextStyle(fontSize: 25),
),
Text("Купить за ${product[getCurrentProduct()].price}")
],
),
),
onTap: () {
_subscribe(product: product[getCurrentProduct()]);
},
);
}
Widget listViweBody(addCheckListMethod, serverLink, xQueryReqestForCheckListNames, product,_subscribe) {
if (CheckListModel.checkListModelNamesFromServer.length == 0) {
return FutureBuilder(
future: CheckListModel.getDataFromServer(xQueryReqestForCheckListNames, serverLink),
builder: (context, data) {
if (data.connectionState == ConnectionState.done) {
return listView(addCheckListMethod, serverLink, product,_subscribe);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
});
} else {
return listView(addCheckListMethod, serverLink, product,_subscribe);
}
}

Pagination with Grid List in Flutter

I want to have a pagination in my grid list.
I have a list of items which is coming from an API endpoint. and currently I am displaying 21 records. but the original list is very long. so, I want to have a pagination type where users can see all the items from the list coming from API.
here's my code.
class _CategoryPageState extends State<CategoryPage> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getCategoryList(widget.slug, widget.isSubList, widget.token),
builder: (context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return CircularProgress();
default:
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
else
return createListView(context, snapshot, widget.isSubList, widget.token);
}
},
);
}
}
Widget createListView(BuildContext context, AsyncSnapshot snapshot, bool isSubList, String token) {
List<CategoryModel> values = snapshot.data;
return GridView.count(
crossAxisCount: 3,
// physics: NeverScrollableScrollPhysics(),
// values[index].imageUrl
padding: EdgeInsets.all(1.0),
/*childAspectRatio: 8.0 / 9.0,*/
children: List<Widget>.generate(values.length, (index) {
if(values[index].imageUrl == null){
values[index].imageUrl = 'dummy.jpg';
}
return GridTile(
child: GridTilesCategory(
name: values[index].name,
imageUrl: values[index].imageUrl,
slug: values[index].slug,
fromSubProducts: isSubList,
token: token,
imageType: 'category-images',
));
}),
);
}
Future<List<CategoryModel>> getCategoryList(String slug, bool isSubList, String token) async {
if (isSubList) {
categories = null;
}
if (categories == null) {
final response = await http.post(
Urls.ROOT_URL + slug,
body: json.encode({
"filter" : {"categoryname":null,"status" : 1},
"sortOrder" : "ASC",
"sortField" : "",
"pageNumber" : 0,
"pageSize" : 21
}),
headers: {
'content-type': 'application/json',
'Authorization': 'Bearer $token',
}
);
print(response.toString());
final responseData = json.decode(response.body);
int statusCode = response.statusCode;
final body = json.decode(response.body);
print(body);
if (statusCode == 200) {
categories = (body['items'] as List).map((i) => CategoryModel.fromJson(i)).toList();
return categories;
} else {
return categories = List();
}
} else {
return categories;
}
}
my requirement is like a continue scrolling type pagination.
Sice No one found this question worthy enough to answer.
I have found a way to implement pagination. here's my final code.
class _ProductListWidgetState extends State<ProductListWidget> {
int pageNumber = 0;
int totalCount = 0;
int totalPages = 0;
#override
void initState() {
// TODO: implement initState
super.initState();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getProductList(widget.slug, false, widget.token, widget.name, widget.identifier, pageNumber),
builder: (context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return CircularProgress();
default:
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
else
return createListView(context, snapshot, widget.token);
}
},
);
}
ProductsModels products;
Future<ProductsModels> getProductList(String slug, bool isSubList, String token, String name, String identifier, int page) async {
if (isSubList) {
products = null;
}
final response = await http.post(
Urls.ROOT_URL + slug,
body: json.encode({
"filter" : {"categoryname":identifier,"status" : 1},
"sortOrder" : "ASC",
"sortField" : "",
"pageNumber" : page,
"pageSize" : 20
}),
headers: {
'content-type': 'application/json',
'Authorization': 'Bearer $token',
}
);
int statusCode = response.statusCode;
final body = json.decode(response.body);
if (statusCode == 200) {
products = ProductsModels.fromJson(body);
totalCount = products.count;
totalPages = totalCount~/20;
print('total count: $totalCount');
print('total pages: $totalPages');
return products;
} else {
return products = ProductsModels();
}
}
Widget createListView(BuildContext context, AsyncSnapshot snapshot, String token) {
ProductsModels values = snapshot.data;
List<Results> results = values.results;
print(results.toString());
return SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: Column(
children: [
GridView.count(
shrinkWrap: true,
primary: true,
physics: NeverScrollableScrollPhysics(),
crossAxisCount: 2,
padding: EdgeInsets.all(1.0),
children: List<Widget>.generate(results.length, (index) {
if(results[index].imageUrls == null){
results[index].imageUrls = 'dummy.jpg';
}
return GridTile(
child: GridTilesProducts(
name: results[index].name,
imageUrl: results[index].imageUrls,
slug: results[index].slug,
price: results[index].price.toString(),
token: token,
));
}),
),
Divider(height: 1.0, color: Color(0xFFe2e2e2),thickness: 1.0,),
Padding(
padding: EdgeInsets.all(20),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
OutlineButton(
borderSide: BorderSide(color: Color(0xFFe1e5e8)),
onPressed: (){
if((pageNumber > totalPages) || (pageNumber > 0)){
setState(() {
pageNumber--;
});
}
else{
Toast.show("Page 1 Reached", context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM);
}
},
color: Color(0xFFe1e5e8),
textColor: Color(0xFFe1e5e8),
child: Container(
height: 30.0,
child: Center(
child: Text('Previous Page',
style: TextStyle(fontSize: 15.0, fontFamily: 'Brand-Bold', color: Color(0xFF383635))),
),
)
),
RaisedButton(
onPressed: (){
if(pageNumber < totalPages){
setState(() {
pageNumber++;
});
}
else{
Toast.show("No More Items Found", context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM);
}
},
color: Color(0xFFd16608),
textColor: Colors.white,
child: Container(
height: 30,
child: Center(
child: Text(
'Next Page',
style: TextStyle(fontSize: 15, fontFamily: 'Poppins-Bold'),
),
),
),
)
],
),
),
],
),
);
}
}
try to use ListView.builder like that:
ListView.builder(
key: PageStorageKey<String>('savePositionKey'),
itemCount: widget.activities.length + 1,
itemBuilder: (builderContext, index) {
if (index == (widget.activities.length - 1)) {
getNextActivitiesPage();
}if (index == (widget.activities.length)) {
return !widget.activitiesFetchState.isCompleted() &&
!widget.isNewData //when you start again from page 0
? Center(child: CircularProgressIndicator())
: SizedBox.shrink();
}
you code- on tap and more.....
);
and in your manager create getNext function that gets the next page and insert it to the end of the list