如何在flutter中实现无限滚动?

imr*_*fat 0 dart flutter flutter-http

我想无限滚动我的产品。我总共有 412 页。当我滚动到最后时,我想显示下一页的更多项目。这意味着无限滚动。这个怎么做?我已经实现了 ScrollController。

\n

这是我的 Api:\n https://www.moharaj.com.bd/api/new/collection/products?page=1

\n

这是我的模型类:

\n
// To parse this JSON data, do\n//\n//     final newCollectionProductModel = newCollectionProductModelFromJson(jsonString);\n\nimport \'dart:convert\';\n\nNewCollectionProductModel newCollectionProductModelFromJson(String str) =>\n    NewCollectionProductModel.fromJson(json.decode(str));\n\nString newCollectionProductModelToJson(NewCollectionProductModel data) =>\n    json.encode(data.toJson());\n\nclass NewCollectionProductModel {\n  NewCollectionProductModel({\n    required this.currentPage,\n    required this.data,\n    required this.firstPageUrl,\n    required this.from,\n    required this.lastPage,\n    required this.lastPageUrl,\n    required this.nextPageUrl,\n    required this.path,\n    required this.perPage,\n    required this.prevPageUrl,\n    required this.to,\n    required this.total,\n  });\n\n  final int currentPage;\n  final List<Datum> data;\n  final String firstPageUrl;\n  final int from;\n  final int lastPage;\n  final String lastPageUrl;\n  final String nextPageUrl;\n  final String path;\n  final int perPage;\n  final dynamic prevPageUrl;\n  final int to;\n  final int total;\n\n  factory NewCollectionProductModel.fromJson(Map<String, dynamic> json) =>\n      NewCollectionProductModel(\n        currentPage: json["current_page"],\n        data: List<Datum>.from(json["data"].map((x) => Datum.fromJson(x))),\n        firstPageUrl: json["first_page_url"],\n        from: json["from"],\n        lastPage: json["last_page"],\n        lastPageUrl: json["last_page_url"],\n        nextPageUrl: json["next_page_url"],\n        path: json["path"],\n        perPage: json["per_page"],\n        prevPageUrl: json["prev_page_url"],\n        to: json["to"],\n        total: json["total"],\n      );\n\n  Map<String, dynamic> toJson() => {\n        "current_page": currentPage,\n        "data": List<dynamic>.from(data.map((x) => x.toJson())),\n        "first_page_url": firstPageUrl,\n        "from": from,\n        "last_page": lastPage,\n        "last_page_url": lastPageUrl,\n        "next_page_url": nextPageUrl,\n        "path": path,\n        "per_page": perPage,\n        "prev_page_url": prevPageUrl,\n        "to": to,\n        "total": total,\n      };\n}\n\nclass Datum {\n  Datum({\n    required this.id,\n    required this.name,\n    required this.price,\n    required this.salePrice,\n    required this.slug,\n    required this.discount,\n    required this.thumnail,\n    required this.productImage,\n  });\n\n  final int id;\n  final String name;\n  final String price;\n  final String salePrice;\n  final String slug;\n  final int discount;\n  final String thumnail;\n  final List<ProductImage> productImage;\n\n  factory Datum.fromJson(Map<String, dynamic> json) => Datum(\n        id: json["id"],\n        name: json["name"],\n        price: json["price"],\n        salePrice: json["sale_price"],\n        slug: json["slug"],\n        discount: json["discount"],\n        thumnail: json["thumnail"],\n        productImage: List<ProductImage>.from(\n            json["product_image"].map((x) => ProductImage.fromJson(x))),\n      );\n\n  Map<String, dynamic> toJson() => {\n        "id": id,\n        "name": name,\n        "price": price,\n        "sale_price": salePrice,\n        "slug": slug,\n        "discount": discount,\n        "thumnail": thumnail,\n        "product_image":\n            List<dynamic>.from(productImage.map((x) => x.toJson())),\n      };\n}\n\nclass ProductImage {\n  ProductImage({\n    required this.id,\n    required this.productId,\n    required this.productImage,\n    required this.createdAt,\n    required this.prefixUrl,\n    required this.updatedAt,\n  });\n\n  final int id;\n  final int productId;\n  final String productImage;\n  final DateTime createdAt;\n  final String prefixUrl;\n  final DateTime updatedAt;\n\n  factory ProductImage.fromJson(Map<String, dynamic> json) => ProductImage(\n        id: json["id"],\n        productId: json["product_id"],\n        productImage: json["product_image"],\n        createdAt: DateTime.parse(json["created_at"]),\n        prefixUrl: json["prefix_url"],\n        updatedAt: DateTime.parse(json["updated_at"]),\n      );\n\n  Map<String, dynamic> toJson() => {\n        "id": id,\n        "product_id": productId,\n        "product_image": productImage,\n        "created_at": createdAt.toIso8601String(),\n        "prefix_url": prefixUrl,\n        "updated_at": updatedAt.toIso8601String(),\n      };\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这是我的服务类代码:

\n
import \'dart:convert\';\nimport \'package:infinite_pagination/NewArrival.dart\';\nimport \'package:infinite_pagination/NewCollectionProductModel.dart\';\n\nimport \'package:http/http.dart\' as http;\n\n/**\n * This is a Service Class. \n * This Service Class is used for New COllection Product.\n * \n */\n\nclass NewCollectionProductService {\n  static var product;\n  static Future<NewCollectionProductModel>\n      getNewCollectionProductService() async {\n    try {\n      final response = await http.get(Uri.parse(\n          "https://www.moharaj.com.bd/api/new/collection/products?page=$pageNumber"));\n      //print(response);\n      if (response.statusCode == 200 || response.statusCode == 201) {\n        final decode = jsonDecode(response.body);\n        product = NewCollectionProductModel.fromJson(decode);\n        return product;\n      } else {\n        return product;\n      }\n    } catch (error) {\n      throw Exception();\n    }\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这是产品类别:

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

这是我的主页:

\n
import \'package:flutter/material.dart\';\nimport \'package:get/get.dart\';\nimport \'package:infinite_pagination/NewArrivalController.dart\';\nimport \'package:infinite_pagination/NewCollectionProductModel.dart\';\nimport \'package:infinite_pagination/NewCollectionProductService.dart\';\nimport \'package:infinite_pagination/Products.dart\';\n\n//------------------------------------------------------------\n//        this widget is for Upcoming categories\n//------------------------------------------------------------\n\nclass NewArrival extends StatefulWidget {\n  @override\n  State<NewArrival> createState() => _NewArrivalState();\n}\n\nvar pageNumber = 1;\n\nclass _NewArrivalState extends State<NewArrival> {\n  NewArrivalController newArrivalController = Get.put(NewArrivalController());\n  late Future<NewCollectionProductModel> getData;\n\n  ScrollController scrollController = ScrollController();\n\n  @override\n  void initState() {\n    getData = NewCollectionProductService.getNewCollectionProductService();\n    // TODO: implement initState\n    super.initState();\n    scrollController.addListener(() {\n      print(scrollController.position.pixels);\n      if (scrollController.position.pixels ==\n          scrollController.position.maxScrollExtent) {\n        pageNumber++;\n\n        print(pageNumber);\n      }\n    });\n  }\n\n  @override\n  void dispose() {\n    // TODO: implement dispose\n    super.dispose();\n    scrollController.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return SafeArea(\n      child: Scaffold(\n        body: SingleChildScrollView(\n          child: Column(\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: [\n              Row(\n                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                children: [\n                  const Padding(\n                    padding: EdgeInsets.only(left: 10),\n                    child: Text(\'New Arrival\',\n                        style: TextStyle(\n                          fontSize: 17,\n                          fontWeight: FontWeight.bold,\n                        )),\n                  ),\n                  Padding(\n                    padding: const EdgeInsets.all(10.0),\n                    child: MaterialButton(\n                        color: Colors.red,\n                        child: Text("View All"),\n                        onPressed: () {}),\n                  )\n                ],\n              ),\n              Container(\n                  // height: 200,\n                  child: collectionOfData())\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n\n  collectionOfData() {\n    return FutureBuilder<NewCollectionProductModel>(\n        future: getData,\n        builder: (context, snapshot) {\n          if (snapshot.hasData) {\n            return ListView.builder(\n                controller: scrollController,\n                physics: NeverScrollableScrollPhysics(),\n                // scrollDirection: Axis.horizontal,\n                shrinkWrap: true,\n                itemCount: snapshot.data!.data.length,\n                // gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(\n                //     crossAxisCount: 2),\n                itemBuilder: (context, int index) {\n                  var product = snapshot.data!.data[index];\n                  //slug = product.slug;\n                  String image = product.productImage[0].prefixUrl.toString() +\n                      product.productImage[0].productImage.toString();\n                  return GestureDetector(\n                    onTap: () {},\n                    child: Container(\n                      height: 300,\n                      width: 200,\n                      child: Products(\n                        image,\n                        product.name,\n                        product.price,\n                        product.discount,\n                        product.salePrice,\n                        product.id,\n                        context,\n                      ),\n                    ),\n                  );\n                });\n          } else {\n            return Center(child: CircularProgressIndicator());\n          }\n        });\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

Vis*_*eri 5

您可以通过两种方式进行管理。

  1. 不带包:管理滚动点和总页、当前页和每页项目。
  2. 使用pagination_view和其他包。

您还可以查看以下链接。

  1. raywenderlich 分页
  2. mobikul 分页
  3. 堆栈溢出查询

没有包的简单示例:

首先声明以下代码。

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 = [];
Run Code Online (Sandbox Code Playgroud)

添加以下代码 init 方法

_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);
        }
      }
    });
Run Code Online (Sandbox Code Playgroud)

如果未找到数据并添加拉动以刷新小部件。

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

在构建小部件中

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()
Run Code Online (Sandbox Code Playgroud)

API调用功能

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
   }

}
Run Code Online (Sandbox Code Playgroud)