-1

I want to scroll infinitely as much my product has. I have total 412 pages. When I scroll to the end I want to show more item of next pages. that means infinite scrolling. how to do this? i have implemented the ScrollController.

this is my Api: https://www.moharaj.com.bd/api/new/collection/products?page=1

this is my model class:

// To parse this JSON data, do
//
//     final newCollectionProductModel = newCollectionProductModelFromJson(jsonString);

import 'dart:convert';

NewCollectionProductModel newCollectionProductModelFromJson(String str) =>
    NewCollectionProductModel.fromJson(json.decode(str));

String newCollectionProductModelToJson(NewCollectionProductModel data) =>
    json.encode(data.toJson());

class NewCollectionProductModel {
  NewCollectionProductModel({
    required this.currentPage,
    required this.data,
    required this.firstPageUrl,
    required this.from,
    required this.lastPage,
    required this.lastPageUrl,
    required this.nextPageUrl,
    required this.path,
    required this.perPage,
    required this.prevPageUrl,
    required this.to,
    required this.total,
  });

  final int currentPage;
  final List<Datum> data;
  final String firstPageUrl;
  final int from;
  final int lastPage;
  final String lastPageUrl;
  final String nextPageUrl;
  final String path;
  final int perPage;
  final dynamic prevPageUrl;
  final int to;
  final int total;

  factory NewCollectionProductModel.fromJson(Map<String, dynamic> json) =>
      NewCollectionProductModel(
        currentPage: json["current_page"],
        data: List<Datum>.from(json["data"].map((x) => Datum.fromJson(x))),
        firstPageUrl: json["first_page_url"],
        from: json["from"],
        lastPage: json["last_page"],
        lastPageUrl: json["last_page_url"],
        nextPageUrl: json["next_page_url"],
        path: json["path"],
        perPage: json["per_page"],
        prevPageUrl: json["prev_page_url"],
        to: json["to"],
        total: json["total"],
      );

  Map<String, dynamic> toJson() => {
        "current_page": currentPage,
        "data": List<dynamic>.from(data.map((x) => x.toJson())),
        "first_page_url": firstPageUrl,
        "from": from,
        "last_page": lastPage,
        "last_page_url": lastPageUrl,
        "next_page_url": nextPageUrl,
        "path": path,
        "per_page": perPage,
        "prev_page_url": prevPageUrl,
        "to": to,
        "total": total,
      };
}

class Datum {
  Datum({
    required this.id,
    required this.name,
    required this.price,
    required this.salePrice,
    required this.slug,
    required this.discount,
    required this.thumnail,
    required this.productImage,
  });

  final int id;
  final String name;
  final String price;
  final String salePrice;
  final String slug;
  final int discount;
  final String thumnail;
  final List<ProductImage> productImage;

  factory Datum.fromJson(Map<String, dynamic> json) => Datum(
        id: json["id"],
        name: json["name"],
        price: json["price"],
        salePrice: json["sale_price"],
        slug: json["slug"],
        discount: json["discount"],
        thumnail: json["thumnail"],
        productImage: List<ProductImage>.from(
            json["product_image"].map((x) => ProductImage.fromJson(x))),
      );

  Map<String, dynamic> toJson() => {
        "id": id,
        "name": name,
        "price": price,
        "sale_price": salePrice,
        "slug": slug,
        "discount": discount,
        "thumnail": thumnail,
        "product_image":
            List<dynamic>.from(productImage.map((x) => x.toJson())),
      };
}

class ProductImage {
  ProductImage({
    required this.id,
    required this.productId,
    required this.productImage,
    required this.createdAt,
    required this.prefixUrl,
    required this.updatedAt,
  });

  final int id;
  final int productId;
  final String productImage;
  final DateTime createdAt;
  final String prefixUrl;
  final DateTime updatedAt;

  factory ProductImage.fromJson(Map<String, dynamic> json) => ProductImage(
        id: json["id"],
        productId: json["product_id"],
        productImage: json["product_image"],
        createdAt: DateTime.parse(json["created_at"]),
        prefixUrl: json["prefix_url"],
        updatedAt: DateTime.parse(json["updated_at"]),
      );

  Map<String, dynamic> toJson() => {
        "id": id,
        "product_id": productId,
        "product_image": productImage,
        "created_at": createdAt.toIso8601String(),
        "prefix_url": prefixUrl,
        "updated_at": updatedAt.toIso8601String(),
      };
}

this is my Service class Code:

import 'dart:convert';
import 'package:infinite_pagination/NewArrival.dart';
import 'package:infinite_pagination/NewCollectionProductModel.dart';

import 'package:http/http.dart' as http;

/**
 * This is a Service Class. 
 * This Service Class is used for New COllection Product.
 * 
 */

class NewCollectionProductService {
  static var product;
  static Future<NewCollectionProductModel>
      getNewCollectionProductService() async {
    try {
      final response = await http.get(Uri.parse(
          "https://www.moharaj.com.bd/api/new/collection/products?page=$pageNumber"));
      //print(response);
      if (response.statusCode == 200 || response.statusCode == 201) {
        final decode = jsonDecode(response.body);
        product = NewCollectionProductModel.fromJson(decode);
        return product;
      } else {
        return product;
      }
    } catch (error) {
      throw Exception();
    }
  }
}

this is product Class:

import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';

var size = 180.0;
var iconSize = 10.0;

Widget Products(String ImgLocation, name, price, discountPrice, discountPercent,
    reviews, BuildContext context) {
  return Container(
    height: 300,
    child: Card(
      child: Padding(
          padding: EdgeInsets.all(5),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisAlignment: MainAxisAlignment.start,
            children: [
              Expanded(
                child: ClipRRect(
                  borderRadius: BorderRadius.circular(10),
                  child: Image.network(
                    '$ImgLocation',
                    fit: BoxFit.cover,
                    loadingBuilder: (context, child, loadingProgress) {
                      return loadingProgress == null
                          ? child
                          : Center(
                              child: LinearProgressIndicator(
                              value: loadingProgress.expectedTotalBytes != null
                                  ? loadingProgress.cumulativeBytesLoaded /
                                      loadingProgress.expectedTotalBytes!
                                  : null,
                            ));
                      // : LinearProgressIndicator();
                    },
                    width: MediaQuery.of(context).size.width,
                    height: size,
                  ),
                ),
              ),
              Text(
                '$name',
                textAlign: TextAlign.start,
              ),
              Text(
                '৳ $price',
                style: TextStyle(
                    fontSize: 15,
                    color: Colors.red,
                    fontWeight: FontWeight.bold),
                textAlign: TextAlign.left,
              ),
              Text.rich(
                TextSpan(
                  children: <TextSpan>[
                    // ignore: unnecessary_new
                    TextSpan(
                      text: '৳ $discountPercent',
                      style: const TextStyle(
                        color: Colors.grey,
                        decoration: TextDecoration.lineThrough,
                      ),
                    ),
                    TextSpan(
                      text: ' -$discountPrice%',
                    ),
                  ],
                ),
              ),
              Row(
                children: [
                  Icon(
                    Icons.star,
                    color: Color(0xFFFfebf50),
                    size: iconSize,
                  ),
                  Icon(
                    Icons.star,
                    color: Color(0xFFFfebf50),
                    size: iconSize,
                  ),
                  Icon(
                    Icons.star,
                    color: Color(0xFFFfebf50),
                    size: iconSize,
                  ),
                  Icon(
                    Icons.star,
                    color: Color(0xFFFfebf50),
                    size: iconSize,
                  ),
                  Icon(
                    Icons.star,
                    color: Color(0xFFFfee9c3),
                    size: iconSize,
                  ),
                  SizedBox(
                    width: 5,
                  ),
                  Text(
                    '($reviews)',
                    style: TextStyle(fontSize: iconSize),
                  )
                ],
              )
            ],
          )),
    ),
  );
}

this is my home page:

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:infinite_pagination/NewArrivalController.dart';
import 'package:infinite_pagination/NewCollectionProductModel.dart';
import 'package:infinite_pagination/NewCollectionProductService.dart';
import 'package:infinite_pagination/Products.dart';

//------------------------------------------------------------
//        this widget is for Upcoming categories
//------------------------------------------------------------

class NewArrival extends StatefulWidget {
  @override
  State<NewArrival> createState() => _NewArrivalState();
}

var pageNumber = 1;

class _NewArrivalState extends State<NewArrival> {
  NewArrivalController newArrivalController = Get.put(NewArrivalController());
  late Future<NewCollectionProductModel> getData;

  ScrollController scrollController = ScrollController();

  @override
  void initState() {
    getData = NewCollectionProductService.getNewCollectionProductService();
    // TODO: implement initState
    super.initState();
    scrollController.addListener(() {
      print(scrollController.position.pixels);
      if (scrollController.position.pixels ==
          scrollController.position.maxScrollExtent) {
        pageNumber++;

        print(pageNumber);
      }
    });
  }

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    scrollController.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: SingleChildScrollView(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  const Padding(
                    padding: EdgeInsets.only(left: 10),
                    child: Text('New Arrival',
                        style: TextStyle(
                          fontSize: 17,
                          fontWeight: FontWeight.bold,
                        )),
                  ),
                  Padding(
                    padding: const EdgeInsets.all(10.0),
                    child: MaterialButton(
                        color: Colors.red,
                        child: Text("View All"),
                        onPressed: () {}),
                  )
                ],
              ),
              Container(
                  // height: 200,
                  child: collectionOfData())
            ],
          ),
        ),
      ),
    );
  }

  collectionOfData() {
    return FutureBuilder<NewCollectionProductModel>(
        future: getData,
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            return ListView.builder(
                controller: scrollController,
                physics: NeverScrollableScrollPhysics(),
                // scrollDirection: Axis.horizontal,
                shrinkWrap: true,
                itemCount: snapshot.data!.data.length,
                // gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                //     crossAxisCount: 2),
                itemBuilder: (context, int index) {
                  var product = snapshot.data!.data[index];
                  //slug = product.slug;
                  String image = product.productImage[0].prefixUrl.toString() +
                      product.productImage[0].productImage.toString();
                  return GestureDetector(
                    onTap: () {},
                    child: Container(
                      height: 300,
                      width: 200,
                      child: Products(
                        image,
                        product.name,
                        product.price,
                        product.discount,
                        product.salePrice,
                        product.id,
                        context,
                      ),
                    ),
                  );
                });
          } else {
            return Center(child: CircularProgressIndicator());
          }
        });
  }
}
imran sifat
  • 165
  • 1
  • 13

4 Answers4

4

you can manage in 2 ways.

  1. without package: manage scrolling point & total page, current page & per page items.
  2. using pagination_view and other packages.

you can also check the below links.

  1. raywenderlich-pagination
  2. mobikul-pageination
  3. stack-overfllow-query

Simple Example without packages:

Declare the below code initially.

ScrollController _scrollController = new ScrollController();
  var pageNumber = 1;// update in API CALL
  total_page = 0; // update in API CALL
  current_page = 0; // update in API CALL
  List<ModelClass> arrList = [];

Add below code init method

_scrollController.addListener(() async {
      if (_scrollController.position.pixels ==
          _scrollController.position.maxScrollExtent) {
        if (currentPage != total) { // on bottom scroll API Call until last page
          currentPage += 1; 
          apiCall(page: currentPage);
        }
      }
    });

If No data is found & add Pull to refresh widget.

Widget noDataFound() {
    return RefreshIndicator(
      onRefresh: apiCall(),
      child: Center(
        child: Text('No Data Found!'),
      ),
    );
  }

in Build Widget

arrList.isNotEmpty ? ListView.separated(
                          padding: const EdgeInsets.all(16.0),
                          separatorBuilder: (c, index) {
                            return SizedBox(
                              height: 20.0,
                            );
                          },
                          physics: AlwaysScrollableScrollPhysics(),
                          controller: _scrollController,
                          itemCount: arrList.size + 1,
                          itemBuilder: (_, index) {
                            if (index == arrList.length) { // Always one widget 'Loading...' added end of list it will Hide/Show based on condtion.
                              return Visibility(
                                visible: 
                                current_page != totalPage ? false:true,
                                child: Center(
                                  child: Text('Loading...',),
                              );
                            } else { 
                              return ... listViewItemWidget;
                            }
                          }) : noDataFound()

API Call Function

apiCall(int page = 0){

// update all pagenation variables After API get & map data sucessfully 
 currentPage , Total page , list value 

   // Add one condition to store data
   if (page == 0) { // If Pull to refresh.
    arrList.clear();
    arrList = [mapData list from API]; // New data load. 
   } else {
      arrList.add([mapData list from API]); // Append new data in old list
   }

}
Vishal Zaveri
  • 1,372
  • 2
  • 5
  • 12
1

You can use custom ScrollController to listen when to load more items:

import 'package:flutter/widgets.dart';

class InfiniteListenerController extends ScrollController {
  final VoidCallback? onLoadMore;

  InfiniteListenerController({this.onLoadMore}) {
    if(onLoadMore != null) addListener(_endListener);
  }

  void _endListener() {
    if (position.pixels == position.maxScrollExtent) {
      onLoadMore?.call();
    }
  }

  @override
  void dispose() {
    if(onLoadMore != null) removeListener(_endListener);
    super.dispose();
  }
}
Witoldio
  • 811
  • 9
  • 12
0

You can look at the infinite_scroll_pagination package

Kartik Malhotra
  • 239
  • 3
  • 4
0

User infinite_scroll_pagination package of flutter.

This package contains ListView, SliverList and SliverGrid infinite scroll options with refresh indicator.

Here is demo.