Flutter分页构建插件paginated_builder的使用
Flutter分页构建插件paginated_builder的使用
1. 插件概述
paginated_builder
是一个用于分页加载数据的Flutter插件,支持从任何数据源(如API、本地数据)进行分页。它提供了以下功能:
- 自定义列表组件:可以选择自己的列表组件(如
ListView.builder
、AnimatableIndexedWidgetBuilder
或自定义组件)。 - 自定义项组件:可以为每个项使用自己的组件。
- 基于数据项操作:直接操作数据项,而不是索引。
- 轻松集成API或本地数据分页逻辑。
- 事件通知:当列表重建或接收到新项时会触发通知。
- 可替换的默认组件:提供空状态、加载状态和错误状态的默认组件,但建议实现自定义组件。
- 自动加载更多:在加载新数据时自动显示加载动画。
- 调试信息:提供可选的调试打印语句。
- 分块限制:可以指定每次请求的数据量。
- 阈值设置:可以设置何时请求新的数据块。
- 双向插入:可以在列表的两端插入新项。
2. 分页机制
分页是通过数据分块来实现的。你可以提供一个 limit
参数来控制每次加载的数据量,并通过 cursor
参数来标记当前加载到的位置。每次加载新数据时,插件会根据 cursor
和 limit
来获取下一批数据。
3. 示例代码
3.1 使用API进行分页
假设我们有一个API接口,可以从 jsonplaceholder.typicode.com
获取帖子数据。我们将使用 PaginatedBuilder
来实现分页加载。
3.1.1 API集成
首先,我们需要编写一个方法来从API获取数据。这个方法将根据 cursor
和 limit
参数来获取下一批数据。
import 'dart:convert';
import 'package:http/http.dart' as http;
class Post {
final int id;
final String title;
final String body;
Post({required this.id, required this.title, required this.body});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
id: json['id'],
title: json['title'],
body: json['body'],
);
}
}
Future<List<Post>> fetchPosts(int? cursor, int limit) async {
final startIndex = cursor ?? 0;
final response = await http.get(
Uri.https(
'jsonplaceholder.typicode.com',
'/posts',
{'_start': '$startIndex', '_limit': '$limit'},
),
);
if (response.statusCode == 200) {
final body = json.decode(response.body) as List;
return body.map((json) {
final map = json as Map<String, dynamic>;
return Post.fromJson(map);
}).toList();
}
throw Exception('error fetching posts');
}
3.1.2 Widget代码
接下来,我们使用 PaginatedBuilder
来构建分页列表。我们将 fetchPosts
方法传递给 dataChunker
参数,并提供其他必要的配置。
import 'package:flutter/material.dart';
import 'package:paginated_builder/paginated_builder.dart';
class PostsList extends StatelessWidget {
const PostsList({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return PaginatedBuilder<Post, int>(
// 当使用 `ListView.builder` 时,必须设置为 true,否则新项不会显示
rebuildListWhenChunkIsCached: true,
// 必需参数
listBuilder: (initialItemCount, paginatedItemBuilder) {
return ListView.builder(
itemBuilder: paginatedItemBuilder,
itemCount: initialItemCount,
);
},
itemBuilder: (context, data, [animation]) {
return ListTile(
title: Text(data.item.title),
subtitle: Text(data.item.body),
);
},
dataChunker: fetchPosts, // 调用上面定义的 fetchPosts 方法
// 可选参数
emptyWidget: const Center(child: Text('没有帖子')),
cursorSelector: (Post post) => post.id,
);
}
}
3.2 使用本地数据进行分页
如果你已经有了所有数据,但仍然希望从设计上实现分页效果,可以使用 PaginatedComparator
来实现。我们将生成一些虚拟数据,并模拟分页加载。
3.2.1 生成本地数据
首先,我们生成一些虚拟的帖子数据。
class Post {
final int id;
final String title;
final String body;
Post({required this.id, required this.title, required this.body});
}
// 生成虚拟帖子数据
final allPosts = List.generate(100, (index) {
final location = index + 1;
return Post(
id: location,
title: '帖子 $location',
body: '这是帖子的内容',
);
});
// 模拟分页加载
Future<List<Post>> _handleGetNext(Post? cursor, int limit) async {
// 如果 cursor 为 null,表示这是第一次加载
final isFirstRun = cursor == null;
final data = isFirstRun
? allPosts.take(limit)
: allPosts
.skipWhile((post) => post != cursor)
.skip(1) // 从上一次的游标之后开始
.take(limit);
// 模拟网络延迟
return Future.delayed(const Duration(seconds: 1), () => data.toList());
}
3.2.2 Widget代码
接下来,我们使用 PaginatedComparator
来构建分页列表。我们将 handleGetNext
方法传递给 dataChunker
参数,并提供其他必要的配置。
import 'package:flutter/material.dart';
import 'package:paginated_builder/paginated_builder.dart';
class LocalPostsList extends StatelessWidget {
const LocalPostsList({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return PaginatedComparator<Post, Post>(
// 当使用 `ListView.builder` 时,必须设置为 true,否则新项不会显示
rebuildListWhenChunkIsCached: true,
dataChunker: _handleGetNext, // 调用上面定义的 _handleGetNext 方法
listBuilder: (initialItemCount, paginatedItemBuilder) {
return ListView.builder(
itemCount: initialItemCount,
itemBuilder: paginatedItemBuilder,
);
},
itemBuilder: (context, comparator, [animation]) {
return Card(
margin: const EdgeInsets.all(12),
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
children: [
Text(
'比较器 ${comparator.currentItem.index + 1}',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 24),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildColumn(comparator.previous, '上一个'),
_buildColumn(comparator.current, '当前'),
_buildColumn(comparator.next, '下一个'),
],
),
],
),
),
);
},
);
}
Widget _buildColumn(Post? post, String position) {
if (post == null) return const SizedBox.shrink();
return Column(
mainAxisSize: MainAxisSize.min,
key: Key('${post.id}_$position'),
children: [
Text(
position,
style: Theme.of(context).textTheme.bodySmall,
),
Text(
post.title,
style: Theme.of(context).textTheme.titleLarge,
)
],
);
}
}
更多关于Flutter分页构建插件paginated_builder的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter分页构建插件paginated_builder的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何在Flutter中使用paginated_builder
插件来实现分页加载的示例代码。paginated_builder
是一个非常有用的插件,可以帮助你轻松实现分页功能。
首先,确保你已经在pubspec.yaml
文件中添加了paginated_builder
依赖:
dependencies:
flutter:
sdk: flutter
paginated_builder: ^0.0.1 # 请检查最新版本号
然后,运行flutter pub get
来安装依赖。
以下是一个完整的示例代码,展示如何使用PaginatedBuilder
来构建分页列表:
import 'package:flutter/material.dart';
import 'package:paginated_builder/paginated_builder.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Paginated Builder Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final List<String> items = List.generate(100, (index) => "Item $index");
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Paginated Builder Demo'),
),
body: PaginatedBuilder<String>(
itemCount: () async {
// 这里返回总的项数,可以根据需求从服务器获取
return items.length;
},
itemBuilder: (_, index, item) {
return ListTile(
title: Text(item),
);
},
pageLoadBuilder: (_, pageKey) async {
// 这里模拟从服务器加载数据
// 实际使用时,你可能会发送网络请求来获取数据
final start = (pageKey - 1) * 20;
final end = start + 20;
return items.sublist(start, min(end, items.length));
},
pageSize: 20, // 每页显示的项目数
),
);
}
}
在这个示例中:
itemCount
是一个异步函数,返回总的项目数。在实际应用中,这通常是从服务器获取的。itemBuilder
用于构建每个项目的UI。在这个例子中,我们简单地显示了一个ListTile
。pageLoadBuilder
是一个异步函数,用于加载特定页的数据。这里我们模拟了从本地列表加载数据,但你可以替换为网络请求。pageSize
指定了每页显示的项目数。
这个示例假设你已经有了所有数据(在items
列表中),但在实际应用中,你可能会根据用户的滚动事件动态地从服务器加载数据。
请根据你的实际需求调整上述代码,例如添加网络请求、错误处理、加载指示器等。