Flutter无限滚动分页动画插件animated_infinite_scroll_pagination的使用
Flutter无限滚动分页动画插件animated_infinite_scroll_pagination的使用
1. 概述
animated_infinite_scroll_pagination
是一个用于实现无限滚动分页效果的Flutter插件,它可以帮助开发者在用户向下滚动屏幕时,加载并显示小块的数据。通过这个插件,可以轻松实现带有动画效果的无限滚动分页功能。
2. 使用步骤
2.1 创建数据仓库(Repository)
首先,我们需要创建一个数据仓库(Repository),用于从后端获取数据。以下是一个示例代码:
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
class UserRepository {
Future<UsersList?> getUsersList(int page) async {
/// 从服务器获取数据
final api = "https://example.com/api/users?page=$page&limit=10"; // 示例API
final http.Response response = await http.get(Uri.parse(api));
final responseData = UserResponse.fromJson(jsonDecode(response.body));
/// responseData.usersList -> json: { "users": [], "total": 100 }
return responseData.usersList;
}
}
2.2 创建分页控制器(PaginationController)
接下来,我们需要创建一个分页控制器(PaginationController),它将作为用户界面和数据模型之间的桥梁。以下是分页控制器的示例代码:
import 'package:animated_infinite_scroll_pagination/animated_infinite_scroll_pagination.dart';
import '../../models/data/user.dart';
class UsersPaginationController with AnimatedInfinitePaginationController<User> {
final repository = UserRepository();
/// 判断两个对象是否表示同一个项目
[@override](/user/override)
bool areItemsTheSame(User a, User b) {
return a.id == b.id;
}
/// 从仓库中获取数据并更新状态
///
/// 设置总项目数 -> 当最后一页数据加载完成后停止加载
///
/// 使用 [emitState] 将新的分页状态反映到UI
[@override](/user/override)
Future<void> fetchData(int page) async {
// 发出加载状态
emitState(const PaginationLoadingState());
try {
// 从服务器获取数据
final data = await repository.getUsersList(page);
if (data?.total != null && data?.users != null) {
// 发出获取到的数据
// 当发出远程数据时,如果缓存中有相同页的数据,控制器会用远程数据替换缓存数据
emitState(PaginationSuccessState(data!.users!, cached: false));
// 告诉控制器总项目数,
// 这样当最后一页数据加载完成后会停止加载更多数据。
setTotal(data.total!);
}
} catch (error) {
if (kDebugMode) print(error);
// 发出错误状态
emitState(const PaginationErrorState());
}
}
/// 从用户列表中移除一个项目
void remove(User user) {
// 找到用户在items列表中的索引
final index = items.value.indexWhere((element) => element.item.id == user.id);
// `deleteItem` 是 `AnimatedInfinitePaginationDelegate` 中声明的方法
// 它期望一个整数值,即项目的索引
if (index != -1) deleteItem(index);
}
}
2.3 创建UI
现在我们可以在UI中使用分页控制器来展示数据。以下是完整的UI代码示例:
import 'package:flutter/material.dart';
import '../controllers/users_pagination_controller.dart';
import 'package:animated_infinite_scroll_pagination/animated_infinite_scroll_pagination.dart';
class UsersListView extends StatefulWidget {
const UsersListView({super.key});
[@override](/user/override)
State<UsersListView> createState() => _UsersListViewState();
}
class _UsersListViewState extends State<UsersListView> {
final controller = UsersPaginationController();
[@override](/user/override)
void initState() {
super.initState();
controller.fetchNewChunk(); // 从服务器获取第一页数据
}
/// 从列表中移除用户
void deleteUser(User user) {
controller.remove(user);
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("用户列表"),
),
body: AnimatedInfiniteScrollView<User>(
controller: controller,
options: AnimatedInfinitePaginationOptions(
// 自定义加载指示器
loadingWidget: const Center(child: CircularProgressIndicator()),
// 自定义分页加载指示器
footerLoadingWidget: const Padding(
padding: EdgeInsets.all(8.0),
child: CircularProgressIndicator(),
),
// 自定义错误提示
errorWidget: TextButton(
onPressed: controller.fetchNewChunk, // 重新获取数据
child: Text(
"加载失败,点击重试",
style: TextStyle(fontSize: 18, color: Theme.of(context).primaryColor),
),
),
// 当第一次加载数据为空时显示的提示
noItemsWidget: Center(
child: Text("没有找到任何数据"),
),
// 项目构建器
itemBuilder: (BuildContext context, User item, int index) {
return ListTile(
title: Text(item.name),
subtitle: Text(item.email),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () => deleteUser(item),
),
);
},
/// 包装 [ScrollView] 在 [RefreshIndicator] 中
refreshIndicator: true,
// 处理下拉刷新事件
onRefresh: () async {
await controller.refresh();
},
),
),
);
}
}
3. 其他功能
3.1 GridView 分页
如果你想在 GridView
中启用分页,只需在 AnimatedInfiniteScrollView
的 options
中添加 gridDelegate
即可。例如:
[@override](/user/override)
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return AnimatedInfiniteScrollView<User>(
controller: controller,
options: AnimatedInfinitePaginationOptions(
// 添加 gridDelegate 以在 GridView 中显示分页列表
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
),
// 项目构建器
itemBuilder: (BuildContext context, User item, int index) {
return Card(
child: Column(
children: [
Image.network(item.avatarUrl),
Text(item.name),
],
),
);
},
),
);
}
3.2 水平滚动
如果你希望分页列表支持水平滚动,可以通过设置 scrollDirection
来实现。例如:
[@override](/user/override)
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return AnimatedInfiniteScrollView<User>(
controller: controller,
options: AnimatedInfinitePaginationOptions(
// 设置滚动方向为水平
scrollDirection: Axis.horizontal,
// 当滚动方向为水平时,子项的宽度是必需的
itemBuilder: (context, item, index) => SizedBox(
width: size.width * 0.8,
child: Card(
child: Column(
children: [
Image.network(item.avatarUrl),
Text(item.name),
],
),
),
),
),
);
}
3.3 添加顶部组件
你还可以在 AnimatedInfiniteScrollView
中添加顶部组件。例如:
[@override](/user/override)
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return AnimatedInfiniteScrollView<User>(
controller: controller,
options: AnimatedInfinitePaginationOptions(
// 添加顶部组件
topWidgets: [
// 非Sliver组件
SliverCustomWidget(
isSliver: false,
child: Container(
color: Colors.blue,
height: size.height / 6,
child: const Center(
child: Text(
"分页列表",
style: TextStyle(fontSize: 30, color: Colors.white, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
),
),
),
// Sliver组件
SliverCustomWidget(
child: const SliverAppBar(
backgroundColor: Colors.white,
floating: false,
pinned: true,
expandedHeight: 100.0,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
title: Text(
"SliverList 组件",
style: TextStyle(color: Colors.black87, fontSize: 20.0, fontWeight: FontWeight.bold),
),
),
),
),
],
// 项目构建器
itemBuilder: (BuildContext context, User item, int index) {
return ListTile(
title: Text(item.name),
subtitle: Text(item.email),
);
},
),
);
}
更多关于Flutter无限滚动分页动画插件animated_infinite_scroll_pagination的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter无限滚动分页动画插件animated_infinite_scroll_pagination的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用animated_infinite_scroll_pagination
插件来实现无限滚动分页动画的一个基本示例。这个插件允许你以动画的形式加载更多数据,非常适合实现如社交媒体信息流等场景。
首先,确保你已经在pubspec.yaml
文件中添加了animated_infinite_scroll_pagination
依赖:
dependencies:
flutter:
sdk: flutter
animated_infinite_scroll_pagination: ^x.y.z # 请替换为最新版本号
然后运行flutter pub get
来安装依赖。
接下来是一个简单的示例,演示如何使用该插件:
import 'package:flutter/material.dart';
import 'package:animated_infinite_scroll_pagination/animated_infinite_scroll_pagination.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: InfiniteScrollPage<int>(
// 初始数据加载
initialData: fetchInitialData(),
// 加载更多数据的函数
pageFuture: (pageKey) async {
// 模拟网络请求延迟
await Future.delayed(Duration(seconds: 1));
// 返回分页数据
return fetchMoreData(pageKey);
},
// 构建每一项数据的Widget
itemBuilder: (context, item, index) {
return ListTile(
title: Text('Item $item'),
);
},
// 分页加载完成后的回调
onLoadMore: () {
print('Loading more...');
},
// 错误处理
onError: (error) {
return Center(
child: Text('Error: $error'),
);
},
// 空状态处理
onEmpty: () {
return Center(
child: Text('No more data'),
);
},
// 初始加载时的占位符
placeholder: Center(
child: CircularProgressIndicator(),
),
// 列表末尾的加载更多按钮(可选)
loadMoreFooter: const LoadMoreFooter(
onLoadMore: () {},
retry: () {},
),
),
);
}
// 模拟初始数据加载
List<int> fetchInitialData() {
return List.generate(10, (index) => index + 1);
}
// 模拟分页数据加载
Future<List<int>> fetchMoreData(int pageKey) async {
final start = pageKey * 10 + 1;
final end = start + 9;
return List.generate(10, (index) => start + index);
}
}
// LoadMoreFooter 是一个简单的加载更多按钮组件,你可以根据需要自定义
class LoadMoreFooter extends StatelessWidget {
final VoidCallback onLoadMore;
final VoidCallback retry;
const LoadMoreFooter({
Key? key,
required this.onLoadMore,
required this.retry,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: onLoadMore,
child: Text('Load More'),
),
const SizedBox(width: 16),
ElevatedButton(
onPressed: retry,
child: Text('Retry'),
),
],
),
);
}
}
解释
-
依赖添加:在
pubspec.yaml
中添加animated_infinite_scroll_pagination
依赖。 -
初始化和配置:
- 使用
InfiniteScrollPage
作为主页面。 initialData
:初始数据加载函数。pageFuture
:加载更多数据的异步函数,根据pageKey
(页码)返回数据。itemBuilder
:构建每个数据项的Widget。onLoadMore
:加载更多数据时的回调。onError
:错误处理。onEmpty
:当没有更多数据时显示的Widget。placeholder
:初始加载时的占位符。loadMoreFooter
:加载更多按钮的Widget(可选)。
- 使用
-
模拟数据加载:
fetchInitialData
:模拟初始数据加载。fetchMoreData
:模拟分页数据加载。
-
自定义加载更多按钮:
LoadMoreFooter
是一个简单的加载更多按钮组件,包含“Load More”和“Retry”按钮。
这个示例演示了如何使用animated_infinite_scroll_pagination
插件实现基本的无限滚动分页动画。你可以根据实际需求进一步定制和扩展这个示例。