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