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
更多关于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(),
));
}