Bloc模式下使用infinite_scroll_pagination实现无限滚动的方法
场景说明
我目前正在将代码迁移为BLoC模式,此前使用flutter_pagewise ^1.2.3结合Future<>实现无限滚动,但不清楚如何在BLoC中使用该库,也不确定二者是否兼容。
因此我尝试切换为infinite_scroll_pagination: ^2.3.0,其官方文档说明支持BLoC,但我看不懂文档中针对BLoC的示例代码,希望得到一个二者搭配使用的简单示例,我当前使用的flutter_bloc版本为^6.1.3。
原始BLoC代码
class TimeslotViewBloc extends Bloc<TimeslotViewEvent, TimeslotViewState> { final GetTimeslotView gettimeslotView; TimeslotViewBloc({this.gettimeslotView}) : super(TimeslotViewInitialState()); @override Stream<TimeslotViewState> mapEventToState( TimeslotViewEvent event, ) async* { if (event is GetTimeslotViewEvent) { yield TimeslotViewLoadingState(); final failureOrSuccess = await gettimeslotView(Params( id: event.id, date: event.date, offset: event.offset, limit: event.limit, )); yield* _eitherLoadedOrErrorState(failureOrSuccess); } } Stream<TimeslotViewState> _eitherLoadedOrErrorState( Either<Failure, List<TimeslotViewEntity>> failureOrTrivia, ) async* { yield failureOrTrivia.fold( (failure) => TimeslotViewErrorState( message: _mapFailureToMessage(failure), failure: failure), (result) => TimeslotViewLoadedState(result), ); } } // BLoC事件定义 abstract class TimeslotViewEvent extends Equatable { const TimeslotViewEvent(); @override List<Object> get props => []; } class GetTimeslotViewEvent extends TimeslotViewEvent { final String id; final String date; final int offset; final int limit; GetTimeslotViewEvent({ required this.id, required this.date, required this.offset, required this.limit }); @override List<Object> get props => [id, date, offset, limit]; } // BLoC状态定义 abstract class TimeslotViewState extends Equatable { const TimeslotViewState(); @override List<Object> get props => []; } class TimeslotViewInitialState extends TimeslotViewState {} class TimeslotViewLoadingState extends TimeslotViewState {} class TimeslotViewLoadedState extends TimeslotViewState { final List<TimeslotViewEntity> records; TimeslotViewLoadedState(this.records); @override List<Object> get props => [records]; } class TimeslotViewErrorState extends TimeslotViewState { final String message; final Failure failure; TimeslotViewErrorState({required this.message, required this.failure}); @override List<Object> get props => [message, failure]; }
可正常运行的UI层实现代码
class TimeslotPage extends StatefulWidget { const TimeslotPage({super.key}); @override State<TimeslotPage> createState() => _TimeslotPageState(); } class _TimeslotPageState extends State<TimeslotPage> { // 分页大小按实际接口规则调整 static const int PAGE_SIZE = 10; final PagingController<int, TimeslotViewEntity> _pagingController = PagingController(firstPageKey: 0); late TimeslotViewBloc _timeLotBloc; int totalRecordCount = 0; @override void initState() { super.initState(); _timeLotBloc = TimeslotViewBloc(gettimeslotView: GetTimeslotView()); // 监听分页控制器的翻页请求,触发BLoC拉取数据 _pagingController.addPageRequestListener((pageKey) { _timeLotBloc.add(GetTimeslotViewEvent( id: "替换为实际业务id", date: "替换为实际日期参数", offset: pageKey, limit: PAGE_SIZE, )); }); } @override Widget build(BuildContext context) { return BlocProvider( create: (context) => _timeLotBloc, child: BlocListener<TimeslotViewBloc, TimeslotViewState>( listener: (context, state) { if (state is TimeslotViewLoadedState) { // 仅统计累计条数,无需在本地维护全量列表 totalRecordCount += state.records.length; final nextPageKey = totalRecordCount; final isLastPage = state.records.length < PAGE_SIZE; if (isLastPage) { _pagingController.appendLastPage(state.records); } else { _pagingController.appendPage(state.records, nextPageKey); } } if (state is TimeslotViewErrorState) { _pagingController.error = state.failure; } }, // 不要把PagedListView放在BlocBuilder中,避免不必要的全局重绘 child: PagedListView<int, TimeslotViewEntity>( pagingController: _pagingController, builderDelegate: PagedChildBuilderDelegate<TimeslotViewEntity>( itemBuilder: (context, time, index) => TimeslotViewEntityListItem( character: time, ), ), ), ), ); } @override void dispose() { _pagingController.dispose(); _timeLotBloc.close(); super.dispose(); } }
核心实现逻辑
- 分页相关状态全部由
infinite_scroll_pagination的PagingController统一管理,无需在BLoC中维护分页偏移量、全量数据列表,大幅降低BLoC的复杂度 - 仅使用
BlocListener监听BLoC的状态变更,触发分页控制器的状态更新,不需要用BlocBuilder包裹列表组件,避免列表全局重绘 - 分页控制器的
addPageRequestListener会在用户滚动到待加载区域时自动触发,直接携带下一页的偏移量调用BLoC拉取数据即可
内容的提问来源于stack exchange,提问作者unice
相关产品推荐
相关产品推荐

