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