Flutter无限滚动分页动画插件animated_infinite_scroll_pagination的使用

发布于 1周前 作者 h691938207 来自 Flutter

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 中启用分页,只需在 AnimatedInfiniteScrollViewoptions 中添加 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

1 回复

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

解释

  1. 依赖添加:在pubspec.yaml中添加animated_infinite_scroll_pagination依赖。

  2. 初始化和配置

    • 使用InfiniteScrollPage作为主页面。
    • initialData:初始数据加载函数。
    • pageFuture:加载更多数据的异步函数,根据pageKey(页码)返回数据。
    • itemBuilder:构建每个数据项的Widget。
    • onLoadMore:加载更多数据时的回调。
    • onError:错误处理。
    • onEmpty:当没有更多数据时显示的Widget。
    • placeholder:初始加载时的占位符。
    • loadMoreFooter:加载更多按钮的Widget(可选)。
  3. 模拟数据加载

    • fetchInitialData:模拟初始数据加载。
    • fetchMoreData:模拟分页数据加载。
  4. 自定义加载更多按钮

    • LoadMoreFooter是一个简单的加载更多按钮组件,包含“Load More”和“Retry”按钮。

这个示例演示了如何使用animated_infinite_scroll_pagination插件实现基本的无限滚动分页动画。你可以根据实际需求进一步定制和扩展这个示例。

回到顶部