Flutter分页构建插件paginated_builder的使用

Flutter分页构建插件paginated_builder的使用

1. 插件概述

paginated_builder 是一个用于分页加载数据的Flutter插件,支持从任何数据源(如API、本地数据)进行分页。它提供了以下功能:

  • 自定义列表组件:可以选择自己的列表组件(如 ListView.builderAnimatableIndexedWidgetBuilder 或自定义组件)。
  • 自定义项组件:可以为每个项使用自己的组件。
  • 基于数据项操作:直接操作数据项,而不是索引。
  • 轻松集成API或本地数据分页逻辑
  • 事件通知:当列表重建或接收到新项时会触发通知。
  • 可替换的默认组件:提供空状态、加载状态和错误状态的默认组件,但建议实现自定义组件。
  • 自动加载更多:在加载新数据时自动显示加载动画。
  • 调试信息:提供可选的调试打印语句。
  • 分块限制:可以指定每次请求的数据量。
  • 阈值设置:可以设置何时请求新的数据块。
  • 双向插入:可以在列表的两端插入新项。

2. 分页机制

分页是通过数据分块来实现的。你可以提供一个 limit 参数来控制每次加载的数据量,并通过 cursor 参数来标记当前加载到的位置。每次加载新数据时,插件会根据 cursorlimit 来获取下一批数据。

3. 示例代码

3.1 使用API进行分页

假设我们有一个API接口,可以从 jsonplaceholder.typicode.com 获取帖子数据。我们将使用 PaginatedBuilder 来实现分页加载。

3.1.1 API集成

首先,我们需要编写一个方法来从API获取数据。这个方法将根据 cursorlimit 参数来获取下一批数据。

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

1 回复

更多关于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,  // 每页显示的项目数
      ),
    );
  }
}

在这个示例中:

  1. itemCount 是一个异步函数,返回总的项目数。在实际应用中,这通常是从服务器获取的。
  2. itemBuilder 用于构建每个项目的UI。在这个例子中,我们简单地显示了一个ListTile
  3. pageLoadBuilder 是一个异步函数,用于加载特定页的数据。这里我们模拟了从本地列表加载数据,但你可以替换为网络请求。
  4. pageSize 指定了每页显示的项目数。

这个示例假设你已经有了所有数据(在items列表中),但在实际应用中,你可能会根据用户的滚动事件动态地从服务器加载数据。

请根据你的实际需求调整上述代码,例如添加网络请求、错误处理、加载指示器等。

回到顶部