Flutter分页加载插件bloc_pagination的使用

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

Flutter分页加载插件bloc_pagination的使用

在本文中,我们将详细介绍如何使用bloc_pagination插件来实现Flutter应用中的分页加载功能。此插件通过BLoC模式(Business Logic Component)帮助开发者更方便地处理分页逻辑。

安装

首先,在你的pubspec.yaml文件中添加依赖:

dependencies:
  bloc_pagination: // 添加最新版本

然后导入该包:

import 'package:bloc_pagination/bloc_pagination.dart';

基本用法

以下是一个基本的使用示例:

class MyBloc extends PaginationBloc {
  [@override](/user/override)
  Future<ListResponse<PaginationModel>> findAll(int page,
      {AbstractQueryParameters? queryParameters}) async {
    /// 你的数据源
    return readRepository.findAll(page, params: queryParameters);
  }
}

class MyWidget extends StatelessWidget {
  final MyBloc _bloc = MyBloc();

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      body: BlocPagination<TempModel, ErrorWrapper>(
        bloc: _bloc,
        blocListener: (context, state) {},
        firstPageErrorBuilder: (context, error) {
          return Center(
            child: Text(error.message.toString()),
          );
        },
        bannerPinned: true,
        banner: SliverAppBarDelegate(
          maxHeight: 100,
          minHeight: 100,
          child: Container(
            color: Colors.green,
            padding: const EdgeInsets.symmetric(vertical: 10),
            child: Text('header widget'),
          ),
        ),
        footerPinned: true,
        footer: Container(
          color: Colors.green,
          height: 100,
          width: double.infinity,
          child: Text('footer widget'),
        ),
        itemsBuilder: (context, item, index) => InkWell(
          onTap: () => _bloc.add(EditListTypePaginationEvent(
              listType: _bloc.state.listType.isListed
                  ? ListType.gridView
                  : ListType.listView)),
          child: Container(
            color: Colors.green,
            margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 20),
            height: 200,
            child: Text('index  ${item.id}'),
          ),
        ),
      ),
    );
  }
}

示例代码

以下是完整的示例代码:

import 'package:bloc_pagination/bloc_pagination.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  [@override](/user/override)
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final _bloc = MyBloc();

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          backgroundColor: Theme.of(context).colorScheme.inversePrimary,
          title: const Text('Pagination page'),
        ),
        body: BlocPagination<TempModel, ErrorWrapper>(
          bloc: _bloc,
          firstPageErrorBuilder: (context, error) {
            return Center(
              child: Text(error.message.toString()),
            );
          },
          bannerPinned: true,
          banner: SliverAppBarDelegate(
            maxHeight: 100,
            minHeight: 100,
            child: Container(
              color: Colors.greenAccent,
              padding: const EdgeInsets.symmetric(vertical: 10),
              child: Text('header'),
            ),
          ),
          footerPinned: true,
          footer: Container(
            color: Colors.greenAccent,
            height: 100,
            width: MediaQuery.of(context).size.width,
            child: Text('footer'),
          ),
          itemsBuilder: (context, item, index) => InkWell(
            onTap: () => _bloc.add(EditListTypePaginationEvent(
                listType: _bloc.state.listType.isListed
                    ? ListType.gridView
                    : ListType.listView)),
            child: Container(
              color: Colors.green,
              margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 20),
              height: 200,
              child: Text('index  ${item.id}'),
            ),
          ),
        ));
  }
}

class MyBloc extends PaginationBloc<TempModel> {
  MyBloc() : super(initialType: ListType.listView);

  [@override](/user/override)
  Future<ListResponse<TempModel>> findAll(int page,
      {AbstractQueryParameters? queryParameters}) async {
    await Future.delayed(const Duration(seconds: 1));

    // 抛出异常作为 ErrorWrapper() 或其他异常
    // throw ErrorWrapper(message: 'error');
    return ListedData(
        data: [for (int i = 0; i < 10; i++) TempModel(i)], total: 20);
  }
}

class TempModel {
  final int id;
  TempModel(this.id);
}

class ListedData with ListResponse<TempModel> {
  [@override](/user/override)
  List<TempModel> data;
  [@override](/user/override)
  int? total;
  ListedData({required this.data, this.total});
}

class ErrorWrapper {
  String? message;
  int? statusCode;
  ErrorWrapper({this.message, this.statusCode});
}

更多关于Flutter分页加载插件bloc_pagination的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter分页加载插件bloc_pagination的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter中使用bloc_pagination插件实现分页加载的示例代码。bloc_pagination是一个非常流行的Flutter包,用于处理分页数据的加载和状态管理。

首先,确保你已经在pubspec.yaml文件中添加了blocbloc_pagination的依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_bloc: ^8.0.0  # 确保版本是最新的
  bloc_pagination: ^0.2.0  # 确保版本是最新的

然后运行flutter pub get来获取这些依赖。

接下来,让我们编写一个简单的分页加载示例。假设我们有一个API返回分页的用户列表。

1. 创建用户模型

class User {
  final String id;
  final String name;

  User({required this.id, required this.name});

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      id: json['id'] as String,
      name: json['name'] as String,
    );
  }
}

2. 创建用户事件、状态和Bloc

用户事件

import 'package:equatable/equatable.dart';

abstract class UserEvent extends Equatable {
  const UserEvent();
}

class LoadUsersEvent extends UserEvent {
  final int page;

  const LoadUsersEvent({required this.page});

  @override
  List<Object?> get props => [page];
}

用户状态

import 'package:bloc_pagination/bloc_pagination.dart';
import 'package:meta/meta.dart';

class UserState extends PaginatedState<User> {
  UserState({
    required List<User> items,
    required PaginationController<User> paginationController,
  }) : super(items: items, paginationController: paginationController);

  @override
  List<Object?> get props => [items, paginationController];
}

用户Bloc

import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:bloc_pagination/bloc_pagination.dart';
import 'package:meta/meta.dart';

class UserBloc extends Bloc<UserEvent, UserState> with PaginationMixin<User> {
  UserBloc({required PaginationRepository<User> repository})
      : super(UserState(
            items: [],
            paginationController: PaginationController(
              initialPage: 1,
              pageOptions: PageOptions(limit: 10),
            ),
          )) {
    on<LoadUsersEvent>((event, emitter) async {
      await fetchPage(
        event.page,
        emitter,
        repository: repository,
      );
    });
  }

  @override
  Future<void> fetchPage(
    int page,
    Emitter<UserState> emitter, {
    required PaginationRepository<User> repository,
  }) async {
    try {
      final List<User> newItems = await repository.fetchPage(
        page: page,
        params: {}, // 可以在这里传递额外的查询参数
      );
      final List<User> items = [...state.items, ...newItems];
      emitter.emit(
        state.copyWith(
          items: items,
          paginationController: state.paginationController.copyWith(
            page: page,
            itemCount: items.length,
          ),
        ),
      );
    } catch (_) {
      // 处理错误
    }
  }
}

3. 创建分页存储库

import 'dart:convert';
import 'package:bloc_pagination/bloc_pagination.dart';
import 'package:http/http.dart' as http;

class UserRepository implements PaginationRepository<User> {
  @override
  Future<List<User>> fetchPage(
    int page, {
    Map<String, dynamic>? params,
  }) async {
    final response = await http.get(
      Uri.parse('https://jsonplaceholder.typicode.com/users'),
      // 注意:这里的API不支持分页,这里只是示例,实际项目中需要传递分页参数
    );
    final List<dynamic> body = jsonDecode(response.body);
    // 模拟分页,只返回前page*limit个用户
    return body
        .map((dynamic item) => User.fromJson(item as Map<String, dynamic>))
        .take(page * 10)
        .skip((page - 1) * 10)
        .toList();
  }
}

4. 使用BlocProvider和PaginationView

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:bloc_pagination/bloc_pagination.dart';
import 'user_bloc.dart';
import 'user_repository.dart';

void main() {
  runApp(
    BlocProvider<UserBloc>(
      create: (context) => UserBloc(repository: UserRepository()),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('User List')),
        body: BlocConsumer<UserBloc, UserState>(
          listener: (context, state) {},
          builder: (context, state) {
            return PaginationView<User>(
              paginationController: state.paginationController,
              builderDelegate: PaginationViewBuilderDelegate<User>(
                itemBuilder: (context, user, index) {
                  return ListTile(title: Text(user.name));
                },
                emptyBuilder: (context) {
                  return Center(child: Text('No users found'));
                },
                errorBuilder: (context, error) {
                  return Center(child: Text('Error: $error'));
                },
              ),
            );
          },
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            context.read<UserBloc>().add(LoadUsersEvent(page: state.paginationController.page + 1));
          },
          tooltip: 'Load More',
          child: Icon(Icons.add),
        ),
      ),
    );
  }
}

注意事项

  1. 上述示例中,API调用并没有真正分页,因为示例API不支持分页。在实际项目中,你需要根据API文档传递分页参数(如pagelimit)。
  2. 错误处理和加载状态管理在实际项目中需要更详细的处理。
  3. 示例中的PaginationViewPaginationViewBuilderDelegatebloc_pagination包提供的便捷组件,用于处理分页视图的构建。

希望这个示例代码对你有所帮助!

回到顶部