Line data Source code
1 : import 'dart:collection'; 2 : 3 : import 'package:bloc/bloc.dart'; 4 : import 'package:flutter_bloc_patterns/base_list.dart'; 5 : import 'package:flutter_bloc_patterns/src/common/view_state.dart'; 6 : import 'package:flutter_bloc_patterns/src/list/paged/page.dart'; 7 : import 'package:flutter_bloc_patterns/src/list/paged/paged_list.dart'; 8 : import 'package:flutter_bloc_patterns/src/list/paged/paged_list_events.dart'; 9 : import 'package:flutter_bloc_patterns/src/list/paged/paged_repository.dart'; 10 : 11 : /// A list BLoC with pagination but without filtering. 12 : /// 13 : /// Designed to collaborate with [ViewStateBuilder] for displaying data. 14 : /// 15 : /// Call [loadFirstPage] to fetch first page of data 16 : /// Call [loadNextPage] to fetch next page of data. 17 : /// 18 : /// [T] - the type of list elements. 19 : class PagedListBloc<T> extends Bloc<PagedListEvent, ViewState> { 20 : static const _defaultPageSize = 10; 21 : final PagedRepository<T> _pagedRepository; 22 : 23 1 : PagedListBloc(PagedRepository<T> pagedRepository) 24 0 : : assert(pagedRepository != null), 25 : this._pagedRepository = pagedRepository; 26 : 27 1 : @override 28 1 : ViewState get initialState => Initial(); 29 : 30 1 : List<T> get _currentElements => 31 6 : (state is Success) ? (state as Success).data.elements : []; 32 : 33 : Page _page; 34 : 35 : /// Loads first page with given size. When no size is given [_defaultPageSize] 36 : /// is used. 37 1 : void loadFirstPage({int pageSize = _defaultPageSize}) { 38 2 : _page = Page.first(size: pageSize); 39 3 : add(LoadPage(_page)); 40 : } 41 : 42 : /// Loads next page. When no page has been loaded before the first one is 43 : /// loaded with the default page size [_defaultPageSize]. 44 1 : void loadNextPage() { 45 3 : _page = _page?.next() ?? Page.first(size: _defaultPageSize); 46 3 : add(LoadPage(_page)); 47 : } 48 : 49 : @override 50 1 : Stream<ViewState> mapEventToState(PagedListEvent event) async* { 51 1 : if (event is LoadPage) { 52 3 : yield* _mapLoadPage(event.page); 53 : } 54 : } 55 : 56 1 : Stream<ViewState> _mapLoadPage(Page page) async* { 57 : try { 58 2 : yield* _emitLoadingWhenFirstPage(page); 59 3 : final List<T> pageElements = await _pagedRepository.getAll(page); 60 1 : if (pageElements.isEmpty) { 61 2 : yield* _emitEmptyPageLoaded(page); 62 : } else { 63 2 : yield* _emitNextPageLoaded(pageElements); 64 : } 65 1 : } on PageNotFoundException catch (_) { 66 2 : yield* _emitEmptyPageLoaded(page); 67 : } catch (e) { 68 2 : yield Failure(e); 69 : } 70 : } 71 : 72 1 : Stream<ViewState> _emitLoadingWhenFirstPage(Page page) async* { 73 1 : if (page.isFirst) { 74 2 : yield Loading(); 75 : } 76 : } 77 : 78 1 : Stream<ViewState> _emitEmptyPageLoaded(Page page) async* { 79 2 : yield (_isFirst(page)) 80 1 : ? Empty() 81 1 : : Success( 82 1 : PagedList<T>( 83 2 : UnmodifiableListView(_currentElements), 84 : hasReachedMax: true, 85 : ), 86 : ); 87 : } 88 : 89 3 : bool _isFirst(Page page) => page.number == 0; 90 : 91 1 : Stream<ViewState> _emitNextPageLoaded(List<T> pageElements) async* { 92 2 : final List<T> allElements = _currentElements + pageElements; 93 4 : yield Success(PagedList<T>(UnmodifiableListView(allElements))); 94 : } 95 : }