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