Flutter分页插件x_pagintor的使用

Flutter分页插件x_pagintor的使用

x_paginator 是一个简单且灵活的 Flutter 包,用于在应用程序中实现分页功能。

安装

pubspec.yaml 文件中添加 x_paginator 作为依赖:

dependencies:
  x_paginator: ^0.0.6

使用

首先,导入包:

import 'package:x_paginator/x_paginator.dart';

然后,使用 XPaginator 小部件来显示数据:

XPaginator(
  fetchPage: (page) async {
    // 获取给定页面的数据。
    // 返回此页面的项目列表。
  },
  itemBuilder: (item) {
    // 构建显示项目的 widget。
    return ListTile(
      title: Text(item.title),
      subtitle: Text(item.subtitle),
      leading: Icon(Icons.star),
    );
  },
),

完整示例

以下是一个完整的示例,展示了如何使用 x_paginator 插件进行分页:

import 'dart:convert';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:x_pagintor/x_pagintor.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter X Paginator',
      home: PagintionTest(),
    );
  }
}

class PagintionTest extends StatefulWidget {
  const PagintionTest({super.key});

  [@override](/user/override)
  State<StatefulWidget> createState() {
    return PagintionTestState();
  }
}

class PagintionTestState extends State<PagintionTest> {
  GlobalKey<XPaginatorState> paginatorGlobalKey = GlobalKey();

  List<Product> products = [];

  [@override](/user/override)
  Widget build(BuildContext context) {
    var size = MediaQuery.of(context).size;

    /*24 is for notification bar on Android*/
    final double itemHeight = (size.height - kToolbarHeight - 24) / 2;
    final double itemWidth = size.width / 2;
    return Scaffold(
      body: XPaginator<Product>.gridView(
        key: paginatorGlobalKey,
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 2,
          childAspectRatio: (itemWidth / itemHeight),
        ),
        pageLoadFuture: (int page) => getProductsPagtion(page: page, context: context),
        pageItemsGetter: listItemsGetter,
        listItemBuilder: (pagemodel, index) => listItemBuilder(pagemodel, index),
        loadingWidgetBuilder: loadingWidgetMaker,
        errorWidgetBuilder: errorWidgetMaker,
        emptyListWidgetBuilder: emptyListWidgetMaker,
        totalItemsGetter: totalPagesGetter,
        pageErrorChecker: pageErrorChecker,
        scrollPhysics: const BouncingScrollPhysics(),
      ),
    );
  }

  List<dynamic> listItemsGetter(productsData) {
    List<Widget> list = [];
    for (var value in productsData.datat!) {
      list.add(ProductWidget(product: value));
    }
    return list;
  }

  Widget listItemBuilder(value, int index) {
    return value;
  }

  Widget loadingWidgetMaker() {
    return Container(
      alignment: Alignment.center,
      height: 160.0,
      child: const CircularProgressIndicator(),
    );
  }

  Widget errorWidgetMaker(productsData, retryListener) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Padding(
          padding: const EdgeInsets.all(16.0),
          child: Text(productsData.datat!.length.toString()),
        ),
        TextButton(
          onPressed: retryListener,
          child: const Text('尝试重新加载'),
        )
      ],
    );
  }

  Widget emptyListWidgetMaker(productsData) {
    return const Center(
      child: Text('当前无产品'),
    );
  }

  int totalPagesGetter(productsData) {
    return productsData.total;
  }

  bool pageErrorChecker(productsData) {
    return productsData.statusCode != 200;
  }
}

Future<PagModal> getProductsPagtion({
  required int page,
  required BuildContext context,
}) async {
  try {
    //------ 替换为你的 HTTP 请求   ------------//
    PagModal pagModal = PagModal.fromJson(jsonData[page - 1], 200);
    return pagModal;
  } catch (e) {
    if (e is IOException) {
      return PagModal.withError('请检查您的网络连接。');
    } else {
      debugPrint(e.toString());
      return PagModal.withError('发生错误。');
    }
  }
}

class ProductWidget extends StatelessWidget {
  const ProductWidget({Key? key, required this.product}) : super(key: key);
  final Product product;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {},
      child: Container(
        margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
        child: FittedBox(
          fit: BoxFit.contain,
          child: Card(
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(15),
            ),
            shadowColor: Colors.grey.withOpacity(1),
            elevation: 4,
            child: Container(
              height: MediaQuery.of(context).size.height / 1.5,
              width: MediaQuery.of(context).size.width / 1.6,
              alignment: Alignment.topRight,
              padding: const EdgeInsets.symmetric(vertical: 10),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.start,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Image.asset(
                    'assets/logo.png',
                    fit: BoxFit.cover,
                    height: MediaQuery.of(context).size.height / 6,
                    width: double.infinity,
                  ),
                  const SizedBox(
                    height: 3,
                  ),
                  const Divider(
                    color: Colors.grey,
                  ),
                  Directionality(
                    textDirection: TextDirection.rtl,
                    child: Expanded(
                      child: Container(
                        margin: const EdgeInsets.only(right: 10),
                        child: Column(
                          children: [
                            Text(
                              product.name!,
                              style: const TextStyle(
                                  color: Colors.red,
                                  fontSize: 25,
                                  fontWeight: FontWeight.bold,
                                  fontFamily: 'hanimation'),
                            ),
                            Text(
                              product.description ?? "暂不可用",
                              style: const TextStyle(
                                color: Colors.red,
                                fontSize: 20,
                                fontWeight: FontWeight.w600,
                                fontFamily: 'hanimation',
                              ),
                              overflow: TextOverflow.ellipsis,
                            ),
                            const Row(
                              mainAxisAlignment: MainAxisAlignment.start,
                              children: [
                                Text(
                                  "数量  : ",
                                  style: TextStyle(
                                      fontSize: 17,
                                      fontWeight: FontWeight.bold,
                                      fontFamily: 'hanimation'),
                                ),
                                Text(
                                  "0",
                                  style: TextStyle(
                                      color: Colors.red,
                                      fontSize: 17,
                                      fontWeight: FontWeight.bold,
                                      fontFamily: 'hanimation'),
                                ),
                              ],
                            ),
                            Row(
                              mainAxisAlignment: MainAxisAlignment.start,
                              children: [
                                const Text(
                                  "批发价: ",
                                  style: TextStyle(
                                      fontSize: 17,
                                      fontWeight: FontWeight.bold,
                                      fontFamily: 'hanimation'),
                                ),
                                Text(
                                  "${product.price} 元  ",
                                  style: const TextStyle(
                                      color: Colors.red,
                                      fontSize: 17,
                                      fontWeight: FontWeight.bold,
                                      fontFamily: 'hanimation'),
                                ),
                              ],
                            ),
                            Row(
                              mainAxisAlignment: MainAxisAlignment.start,
                              children: [
                                const Text(
                                  "零售价: ",
                                  style: TextStyle(
                                      fontSize: 17,
                                      fontWeight: FontWeight.bold,
                                      fontFamily: 'hanimation'),
                                ),
                                Text(
                                  "${product.price} 元",
                                  style: const TextStyle(
                                      color: Colors.red,
                                      fontSize: 17,
                                      fontWeight: FontWeight.bold,
                                      fontFamily: 'hanimation'),
                                ),
                              ],
                            ),
                            const SizedBox(
                              height: 25,
                            ),
                            Container(
                              alignment: Alignment.center,
                              child: ElevatedButton(
                                style: ElevatedButton.styleFrom(
                                  foregroundColor: Colors.red,
                                  backgroundColor: Colors.grey,
                                  shadowColor: Colors.red,
                                  elevation: 5,
                                  shape: RoundedRectangleBorder(
                                      borderRadius:
                                          BorderRadius.circular(30.0)),
                                  padding: const EdgeInsets.symmetric(
                                      vertical: 6, horizontal: 50),
                                ),
                                onPressed: () {},
                                child: const Text(
                                  "等待激活",
                                  textAlign: TextAlign.center,
                                  style: TextStyle(
                                      color: Colors.white,
                                      fontSize: 15,
                                      fontWeight: FontWeight.bold),
                                ),
                              ),
                            ),
                          ],
                        ),
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

class Product {
  final int? id;
  final String? name;
  final String? price;
  final String? image;
  final String? description;
  const Product({
    required this.id,
    required this.name,
    required this.image,
    required this.price,
    required this.description,
  });

  factory Product.fromJson(Map<String, dynamic> json) {
    return Product(
      id: json['id'],
      name: json['name'],
      image: json['image'] == null ? "" : json["image"],
      description: json['description'] == null ? "" : json["description"],
      price: json['price'],
    );
  }
}

class PagModal {
  List? datat;
  int? statusCode;
  String? errorMessage;
  String? next_page_url;
  int? total;
  int? nItems;

  PagModal.fromResponse(http.Response response) {
    statusCode = response.statusCode;
    total = json.decode(response.body)['total'];
    datat = json
        .decode(response.body)["data"]
        .map((e) => Product.fromJson(e))
        .toList();
    nItems = datat!.length;
  }

  PagModal.fromJson(Map<String, dynamic> response, int code) {
    statusCode = code;
    total = response['total'];
    next_page_url = response["next_page_url"];
    datat = response["data"].map((e) => Product.fromJson(e)).toList();
    nItems = datat!.length;
  }

  PagModal.withError(this.errorMessage);
}

//------ 替换服务器响应 ------------//
List jsonData = List.generate(30, (index) {
  return {
    "current_page": index,
    "first_page_url": '0',
    "from": 1,
    "last_page": 3,
    "last_page_url": 2,
    "next_page_url": '${index + 1}',
    "per_page": 9,
    "prev_page_url": null,
    "to": 3,
    "total": 90,
    "data": [
      {
        "id": 25,
        "name": "芝士兰德 Vita 四分之一公斤",
        "description": "一盒27个 - 每盒四分之一公斤",
        "image": "https://cdn-images-1.medium.com/max/1200/1*5-aoK8IBmXve5whBQM90GA.png",
        "price": "253.5"
      },
      {
        "id": 25,
        "name": "芝士兰德 Vita 四分之一公斤",
        "description": "一盒27个 - 每盒四分之一公斤",
        "image": "https://cdn-images-1.medium.com/max/1200/1*5-aoK8IBmXve5whBQM90GA.png",
        "price": "253.5"
      },
      {
        "id": 25,
        "name": "芝士兰德 Vita 四分之一公斤",
        "description": "一盒27个 - 每盒四分之一公斤",
        "image": "https://cdn-images-1.medium.com/max/1200/1*5-aoK8IBmXve5whBQM90GA.png",
        "price": "253.5"
      }
    ],
    "status": true,
    "error": null,
    "message": null
  };
});

更多关于Flutter分页插件x_pagintor的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter分页插件x_pagintor的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


x_paginator 是一个用于 Flutter 的分页插件,它可以帮助你轻松实现分页加载数据的功能。它支持多种分页模式,包括下拉刷新、上拉加载更多等。以下是 x_paginator 的基本使用方法:

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 x_paginator 依赖:

dependencies:
  flutter:
    sdk: flutter
  x_paginator: ^latest_version

然后运行 flutter pub get 来安装依赖。

2. 基本使用

2.1 创建一个分页控制器

你需要创建一个 PaginatorController 来控制分页逻辑。

import 'package:x_paginator/x_paginator.dart';

class MyPaginatorController extends PaginatorController<MyDataModel> {
  [@override](/user/override)
  Future<List<MyDataModel>> fetchPage(int page) async {
    // 这里根据页码获取数据
    // 例如:从 API 获取数据
    final response = await myApi.getData(page: page);
    return response.data;
  }
}

2.2 在 UI 中使用 PaginatorBuilder

PaginatorBuilder 是一个用于构建分页列表的 Widget。它可以根据分页状态自动加载更多数据。

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

class MyPaginatedList extends StatelessWidget {
  final MyPaginatorController controller = MyPaginatorController();

  [@override](/user/override)
  Widget build(BuildContext context) {
    return PaginatorBuilder<MyDataModel>(
      controller: controller,
      itemBuilder: (context, item, index) {
        return ListTile(
          title: Text(item.title),
          subtitle: Text(item.description),
        );
      },
      emptyWidget: Center(child: Text('No data available')),
      errorWidget: (error) => Center(child: Text('Error: $error')),
      loadingWidget: Center(child: CircularProgressIndicator()),
      paginationWidget: (context, state) {
        if (state == PaginationState.loading) {
          return Center(child: CircularProgressIndicator());
        } else if (state == PaginationState.error) {
          return Center(child: Text('Failed to load more data'));
        } else {
          return SizedBox.shrink();
        }
      },
    );
  }
}

2.3 下拉刷新

PaginatorBuilder 默认支持下拉刷新功能。你可以通过 onRefresh 回调来自定义刷新逻辑。

PaginatorBuilder<MyDataModel>(
  controller: controller,
  onRefresh: () async {
    // 自定义刷新逻辑
    await controller.refresh();
  },
  itemBuilder: (context, item, index) {
    return ListTile(
      title: Text(item.title),
      subtitle: Text(item.description),
    );
  },
);

2.4 上拉加载更多

PaginatorBuilder 也支持上拉加载更多功能。当用户滚动到列表底部时,插件会自动加载更多数据。

PaginatorBuilder<MyDataModel>(
  controller: controller,
  itemBuilder: (context, item, index) {
    return ListTile(
      title: Text(item.title),
      subtitle: Text(item.description),
    );
  },
  paginationWidget: (context, state) {
    if (state == PaginationState.loading) {
      return Center(child: CircularProgressIndicator());
    } else if (state == PaginationState.error) {
      return Center(child: Text('Failed to load more data'));
    } else {
      return SizedBox.shrink();
    }
  },
);

3. 自定义分页逻辑

你可以通过继承 PaginatorController 来自定义分页逻辑。例如,你可以根据不同的条件来加载数据,或者处理分页错误。

class MyCustomPaginatorController extends PaginatorController<MyDataModel> {
  [@override](/user/override)
  Future<List<MyDataModel>> fetchPage(int page) async {
    try {
      final response = await myApi.getData(page: page);
      return response.data;
    } catch (e) {
      // 处理错误
      throw e;
    }
  }
}

4. 其他功能

x_paginator 还支持其他一些功能,例如:

  • 手动刷新:你可以通过调用 controller.refresh() 来手动刷新数据。
  • 重置分页:你可以通过调用 controller.reset() 来重置分页状态。
  • 自定义分页状态:你可以通过 PaginationState 来自定义分页状态的 UI。

5. 示例代码

以下是一个完整的示例代码:

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

class MyDataModel {
  final String title;
  final String description;

  MyDataModel({required this.title, required this.description});
}

class MyPaginatorController extends PaginatorController<MyDataModel> {
  [@override](/user/override)
  Future<List<MyDataModel>> fetchPage(int page) async {
    // 模拟从 API 获取数据
    await Future.delayed(Duration(seconds: 2));
    return List.generate(10, (index) => MyDataModel(title: 'Item ${page * 10 + index}', description: 'Description ${page * 10 + index}'));
  }
}

class MyPaginatedList extends StatelessWidget {
  final MyPaginatorController controller = MyPaginatorController();

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Paginated List')),
      body: PaginatorBuilder<MyDataModel>(
        controller: controller,
        onRefresh: () async {
          await controller.refresh();
        },
        itemBuilder: (context, item, index) {
          return ListTile(
            title: Text(item.title),
            subtitle: Text(item.description),
          );
        },
        emptyWidget: Center(child: Text('No data available')),
        errorWidget: (error) => Center(child: Text('Error: $error')),
        loadingWidget: Center(child: CircularProgressIndicator()),
        paginationWidget: (context, state) {
          if (state == PaginationState.loading) {
            return Center(child: CircularProgressIndicator());
          } else if (state == PaginationState.error) {
            return Center(child: Text('Failed to load more data'));
          } else {
            return SizedBox.shrink();
          }
        },
      ),
    );
  }
}

void main() {
  runApp(MaterialApp(
    home: MyPaginatedList(),
  ));
}
回到顶部