diff --git a/lib/features/documents/document_page_bloc.dart b/lib/features/documents/document_page_bloc.dart index a0d75b62..81406d99 100644 --- a/lib/features/documents/document_page_bloc.dart +++ b/lib/features/documents/document_page_bloc.dart @@ -6,22 +6,22 @@ part of 'index.dart'; class DocumentPageBloc extends Bloc { final DocumentPageModel model; + static DocumentPageBloc? _singleton; - DocumentPageBloc._(this.model, DocumentPageState initialState) - : super(initialState) { + factory DocumentPageBloc(DocumentPageModel model) { + _singleton ??= DocumentPageBloc._internal(model); + return _singleton!; + } + + DocumentPageBloc._internal(this.model) : super(DocumentPageState()) { on(_selectDocument); on(_unselectDocument); on(_filterCategoryEvent); } - static DocumentPageBloc create(DocumentPageModel model) { - final initialState = DocumentPageState(); - return DocumentPageBloc._(model, initialState); - } - Future _filterCategoryEvent( FilterCategoryEvent event, Emitter emit) async { - log('generateDocuments -> ${state.isCategorySelected}'); + _selectCategory(event, emit); state.isCategorySelected ? _unselectCategory(event, emit) : _selectCategory(event, emit); @@ -29,16 +29,14 @@ class DocumentPageBloc extends Bloc { Future _selectCategory( FilterCategoryEvent event, Emitter emit) async { + log('filterItems A: ${event.query}'); emit(state.copyWith( isCategorySelected: true, )); - final listViewState = model.enhancedListViewKey.currentState!; - listViewState.safeSetState(() async { - return await listViewState.filterItems(event.query); - }); - - // state.isCategorySelected ? s.filter(event.query) : s.filter(event.query); + final listViewState = model.vihicleScreenManager.currentState!; + listViewState.widget.bodyItems = (await model.generateBodyItems( + 1, 10, event.query)) as BodyItemsBuilder; } Future _unselectCategory( @@ -47,10 +45,9 @@ class DocumentPageBloc extends Bloc { isCategorySelected: false, )); - final listViewState = model.enhancedListViewKey.currentState!; - listViewState.safeSetState(() async { - return await listViewState.filterItems(null); - }); + final listViewState = model.vihicleScreenManager.currentState!; + listViewState.widget.bodyItems = (await model.generateBodyItems( + 1, 10, null)) as BodyItemsBuilder; } Future _selectDocument( @@ -69,15 +66,10 @@ class DocumentPageBloc extends Bloc { Future _unselectDocument( UnselectDocumentEvent event, Emitter emit) async { - // final docs = await model.generateDocuments(state.page, state.query); - // final cats = await model.generateCategories(); - emit( state.copyWith( currentDocument: null, isDocumentSelected: false, - // documents: docs.$2, - // categories: cats, ), ); } diff --git a/lib/features/documents/document_page_model.dart b/lib/features/documents/document_page_model.dart index 8501e39b..78b696ae 100644 --- a/lib/features/documents/document_page_model.dart +++ b/lib/features/documents/document_page_model.dart @@ -1,21 +1,25 @@ part of 'index.dart'; class DocumentPageModel extends FlutterFlowModel { - DocumentPageModel(); + DocumentPageModel._privateConstructor(); - late final GlobalKey> pageKey; - late final EnhancedRemoteListViewKey - enhancedListViewKey; - late final DocumentKey viewerKey; - late final PagingController _pagingController; + static final DocumentPageModel _instance = + DocumentPageModel._privateConstructor(); + + factory DocumentPageModel() { + return _instance; + } + + late EnhancedListViewKey vihicleScreenManager; + late DocumentKey vehicleScreenViewer; + late PagingController _pagingController; /// ------------ @override void initState(BuildContext context) { - pageKey = GlobalKey>(); - enhancedListViewKey = EnhancedRemoteListViewKey(); - viewerKey = DocumentKey(); + vihicleScreenManager = EnhancedListViewKey(); + vehicleScreenViewer = DocumentKey(); _pagingController = PagingController(firstPageKey: 1); } @@ -23,8 +27,8 @@ class DocumentPageModel extends FlutterFlowModel { @override void dispose() { _pagingController.dispose(); - // isCategorySelected = false; - // isDocumentSelected = false; + vihicleScreenManager.currentState?.dispose(); + vehicleScreenViewer.currentState?.dispose(); } /// ------------ @@ -77,7 +81,7 @@ class DocumentPageModel extends FlutterFlowModel { }); /// [generateBodyItems] - Future> generateBodyItems( + Future> generateBodyItems( int pageKey, int pageSize, dynamic query) async { log('generateDocuments: $query'); @@ -86,54 +90,73 @@ class DocumentPageModel extends FlutterFlowModel { final GetDocuments getDocuments = FreAccessWSGlobal.getDocuments; final ApiCallResponse newItems = await getDocuments.call(pageKey, query); - if (newItems.jsonBody == null || newItems.jsonBody['error'] == true) { - return error; - } + if (newItems.jsonBody == null) return error; + if (newItems.jsonBody['error'] == true) return error; final List list = newItems.jsonBody['value']['list']; - final List docs = list.map((item) { + + late final List docs = []; + + for (var item in list) { log('-> generateDocuments: $item'); - return Document( - id: item['id'], - description: item['description'], - type: item['type'], + final String description = item['description']; + final String type = item['type']; + final String category = item['category']['description']; + final String color = item['category']['color']; + final String person = item['person'] ?? ''; + final String property = item['property'] ?? ''; + final String createdAt = item['createdAt']; + final String updatedAt = item['updatedAt']; + final int categoryId = item['category']['id']; + final int documentId = item['id']; + + final doc = Document( + id: documentId, + description: description, + type: type, category: Category( - id: item['category']['id'], - color: item['category']['color'].toColor(), - title: item['category']['description'], + id: categoryId, + color: color.toColor(), + title: category, ), - person: item['person'] ?? '', - property: item['property'] ?? '', - createdAt: item['createdAt'], - updatedAt: item['updatedAt'], + person: person, + property: property, + createdAt: createdAt, + updatedAt: updatedAt, ); - }).toList(); + + docs.add(doc); + } return docs as List; } /// [generateHeaderItems] - Future> generateHeaderItems() async { + Future> generateHeaderItems() async { + log('generateCategories: '); final List error = [null]; final GetCategories getCategories = FreAccessWSGlobal.getCategories; final ApiCallResponse newItems = await getCategories.call(); - if (newItems.jsonBody == null || newItems.jsonBody['error'] == true) { - return error; - } + if (newItems.jsonBody['error'] == true) return error; + if (newItems.jsonBody == null) return error; + final list = newItems.jsonBody['value'] as List; + late final List cats = []; + for (var item in list) { + final String color = item['color']; + final String title = item['description']; + final int id = item['id']; - final List list = newItems.jsonBody['value']; - final List categories = list.map((item) { - return Category( - id: item['id'], - color: item['color'].toColor(), - title: item['description'], + final cat = Category( + id: id, + color: color.toColor(), + title: title, ); - }).toList(); - - log('categories: $categories'); - return categories as List; + cats.add(cat); + } + log('cats: $cats'); + return cats as List; } /// [filter] @@ -142,26 +165,10 @@ class DocumentPageModel extends FlutterFlowModel { .read() .add(FilterCategoryEvent(query as Archive?)); } - // { - // log('filterByCategories: '); - // final state = managerKey.currentState; - - // if (state != null) { - // // safeSetState(() { - // // if (isCategorySelected) { - // // filter(null); - // // isCategorySelected = false; - // // } else { - // // filter(query); - // // isCategorySelected = true; - // // } - // // }); - // } - // } /// [onFetchError] void onFetchError(Object e, StackTrace s) { - DialogUtil.errorDefault(viewerKey.currentContext!); + DialogUtil.errorDefault(vehicleScreenViewer.currentContext!); LogUtil.requestAPIFailed( "proccessRequest.php", "", "Consulta de Veículo", e, s); } diff --git a/lib/features/documents/document_page_widget.dart b/lib/features/documents/document_page_widget.dart index ae7b00e2..815d23bd 100644 --- a/lib/features/documents/document_page_widget.dart +++ b/lib/features/documents/document_page_widget.dart @@ -23,7 +23,7 @@ class FREDocumentPageState Widget buildBody(BuildContext context) { return BlocProvider( - create: (context) => DocumentPageBloc.create(model), + create: (context) => DocumentPageBloc(model), child: BlocBuilder( builder: (context, state) { print('Bloc -> ${state.isCategorySelected}'); diff --git a/lib/features/documents/document_screen_manager.dart b/lib/features/documents/document_screen_manager.dart index 1db102dd..d2b52f3c 100644 --- a/lib/features/documents/document_screen_manager.dart +++ b/lib/features/documents/document_screen_manager.dart @@ -31,8 +31,8 @@ class DocumentManagerScreen extends StatelessScreen { return Column( children: [ Expanded( - child: EnhancedListView.remote( - key: model.enhancedListViewKey, + child: EnhancedListView( + key: model.vihicleScreenManager, headerBuilder: model.itemHeaderBuilder, headerItems: model.generateHeaderItems, bodyBuilder: model.itemBodyBuilder, diff --git a/lib/shared/mixins/pegeable_mixin.dart b/lib/shared/mixins/pegeable_mixin.dart index 95b60b71..20a3cfdb 100644 --- a/lib/shared/mixins/pegeable_mixin.dart +++ b/lib/shared/mixins/pegeable_mixin.dart @@ -1,140 +1,140 @@ -import 'package:flutter/material.dart'; -import 'package:hub/flutter_flow/index.dart'; -import 'package:hub/shared/utils/limited_text_size.dart'; -import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; +// import 'package:flutter/material.dart'; +// import 'package:hub/flutter_flow/index.dart'; +// import 'package:hub/shared/utils/limited_text_size.dart'; +// import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; -extension PagedListViewExtension - on PagedSliverList {} +// extension PagedListViewExtension +// on PagedSliverList {} -typedef PaginatedListViewHeaderBuilder = Widget Function( - Future> Function() gen); -typedef PaginatedListViewBodyBuilder = Widget Function(BuildContext, T, int); +// typedef PaginatedListViewHeaderBuilder = Widget Function( +// Future> Function() gen); +// typedef PaginatedListViewBodyBuilder = Widget Function(BuildContext, T, int); -mixin Pageable on State { - Expanded buildPaginatedListView( - String noDataFound, - PagingController pg, - Future> Function() headerItems, - PaginatedListViewHeaderBuilder headerBuilder, - PaginatedListViewBodyBuilder bodyBuilder) { - final theme = FlutterFlowTheme.of(context); - return Expanded( - child: RefreshIndicator( - backgroundColor: theme.primaryBackground, - color: theme.primary, - onRefresh: () async => pg.refresh(), - child: PagedListView( - pagingController: pg, - builderDelegate: PagedChildBuilderDelegate( - animateTransitions: true, - itemBuilder: (context, item, int index) { - return Column(children: [ - if (index == 0) headerBuilder(headerItems), - bodyBuilder(context, item, index), - ]); - }, - newPageProgressIndicatorBuilder: (context) => - buildLoadingIndicator(context), - firstPageProgressIndicatorBuilder: (context) => - buildLoadingIndicator(context), - noItemsFoundIndicatorBuilder: (context) => - buildNoDataFound( - context, noDataFound, headerItems, headerBuilder), - firstPageErrorIndicatorBuilder: (context) => const Placeholder(), - newPageErrorIndicatorBuilder: (context) => const Placeholder(), - ), - ), - ), - ); - } +// mixin Pageable on State { +// Expanded buildPaginatedListView( +// String noDataFound, +// PagingController pg, +// Future> Function() headerItems, +// PaginatedListViewHeaderBuilder headerBuilder, +// PaginatedListViewBodyBuilder bodyBuilder) { +// final theme = FlutterFlowTheme.of(context); +// return Expanded( +// child: RefreshIndicator( +// backgroundColor: theme.primaryBackground, +// color: theme.primary, +// onRefresh: () async => pg.refresh(), +// child: PagedListView( +// pagingController: pg, +// builderDelegate: PagedChildBuilderDelegate( +// animateTransitions: true, +// itemBuilder: (context, item, int index) { +// return Column(children: [ +// if (index == 0) headerBuilder(headerItems), +// bodyBuilder(context, item, index), +// ]); +// }, +// newPageProgressIndicatorBuilder: (context) => +// buildLoadingIndicator(context), +// firstPageProgressIndicatorBuilder: (context) => +// buildLoadingIndicator(context), +// noItemsFoundIndicatorBuilder: (context) => +// buildNoDataFound( +// context, noDataFound, headerItems, headerBuilder), +// firstPageErrorIndicatorBuilder: (context) => const Placeholder(), +// newPageErrorIndicatorBuilder: (context) => const Placeholder(), +// ), +// ), +// ), +// ); +// } - Future showError(PagingStatus status, PagingController pg) async { - if (status == PagingStatus.subsequentPageError) { - final message = FFLocalizations.of(context).getVariableText( - enText: 'Something went wrong while fetching a new page.', - ptText: 'Algo deu errado ao buscar uma nova página.', - ); - final retry = FFLocalizations.of(context).getVariableText( - enText: 'Retry', - ptText: 'Recarregar', - ); +// Future showError(PagingStatus status, PagingController pg) async { +// if (status == PagingStatus.subsequentPageError) { +// final message = FFLocalizations.of(context).getVariableText( +// enText: 'Something went wrong while fetching a new page.', +// ptText: 'Algo deu errado ao buscar uma nova página.', +// ); +// final retry = FFLocalizations.of(context).getVariableText( +// enText: 'Retry', +// ptText: 'Recarregar', +// ); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(message), - action: SnackBarAction( - label: retry, - onPressed: () => pg.retryLastFailedRequest(), - ), - ), - ); - } - } +// ScaffoldMessenger.of(context).showSnackBar( +// SnackBar( +// content: Text(message), +// action: SnackBarAction( +// label: retry, +// onPressed: () => pg.retryLastFailedRequest(), +// ), +// ), +// ); +// } +// } - Future fetchPage({ - required Future<(bool, dynamic)> Function() dataProvider, - required void Function(dynamic data) onDataAvailable, - required void Function() onDataUnavailable, - required void Function(Object error, StackTrace stackTrace) onFetchError, - }) async { - try { - final (bool isDataAvailable, dynamic data) = await dataProvider(); - if (isDataAvailable) { - onDataAvailable(data); - } else { - onDataUnavailable(); - } - } catch (error, stackTrace) { - onFetchError(error, stackTrace); - } - } +// Future fetchPage({ +// required Future<(bool, dynamic)> Function() dataProvider, +// required void Function(dynamic data) onDataAvailable, +// required void Function() onDataUnavailable, +// required void Function(Object error, StackTrace stackTrace) onFetchError, +// }) async { +// try { +// final (bool isDataAvailable, dynamic data) = await dataProvider(); +// if (isDataAvailable) { +// onDataAvailable(data); +// } else { +// onDataUnavailable(); +// } +// } catch (error, stackTrace) { +// onFetchError(error, stackTrace); +// } +// } - void showNoMoreDataSnackBar(BuildContext context) { - final message = FFLocalizations.of(context).getVariableText( - ptText: "Não há mais dados.", - enText: "No more data.", - ); +// void showNoMoreDataSnackBar(BuildContext context) { +// final message = FFLocalizations.of(context).getVariableText( +// ptText: "Não há mais dados.", +// enText: "No more data.", +// ); - showSnackbar(context, message, true); - } +// showSnackbar(context, message, true); +// } - Widget buildNoDataFound( - BuildContext context, - String title, - Future> Function() items, - Widget Function(Future> Function() items) headerBuilder, - ) { - final headerFontSize = LimitedFontSizeUtil.getHeaderFontSize(context); - // final bodyFontSize = LimitedFontSizeUtil.getBodyFontSize(context); - return Column( - children: [ - headerBuilder(items), - Expanded( - child: Center( - child: Text( - title, - style: TextStyle( - fontFamily: 'Nunito', - fontSize: headerFontSize, - ), - ), - ), - ), - ], - ); - } +// Widget buildNoDataFound( +// BuildContext context, +// String title, +// Future> Function() items, +// Widget Function(Future> Function() items) headerBuilder, +// ) { +// final headerFontSize = LimitedFontSizeUtil.getHeaderFontSize(context); +// // final bodyFontSize = LimitedFontSizeUtil.getBodyFontSize(context); +// return Column( +// children: [ +// headerBuilder(items), +// Expanded( +// child: Center( +// child: Text( +// title, +// style: TextStyle( +// fontFamily: 'Nunito', +// fontSize: headerFontSize, +// ), +// ), +// ), +// ), +// ], +// ); +// } - Widget buildLoadingIndicator(BuildContext context) { - print('Loading'); - return Container( - padding: const EdgeInsets.symmetric(vertical: 15), - child: Center( - child: CircularProgressIndicator( - valueColor: AlwaysStoppedAnimation( - FlutterFlowTheme.of(context).primary, - ), - ), - ), - ); - } -} +// Widget buildLoadingIndicator(BuildContext context) { +// print('Loading'); +// return Container( +// padding: const EdgeInsets.symmetric(vertical: 15), +// child: Center( +// child: CircularProgressIndicator( +// valueColor: AlwaysStoppedAnimation( +// FlutterFlowTheme.of(context).primary, +// ), +// ), +// ), +// ); +// } +// } diff --git a/lib/shared/widgets/enhanced_list_view.dart b/lib/shared/widgets/enhanced_list_view.dart index 5ba1828e..eb2ec5a1 100644 --- a/lib/shared/widgets/enhanced_list_view.dart +++ b/lib/shared/widgets/enhanced_list_view.dart @@ -2,10 +2,8 @@ part of 'widgets.dart'; /// [TypeDefs] -typedef EnhancedRemoteListViewKey - = GlobalKey>; -typedef EnhancedLocalListViewKey - = GlobalKey>; +typedef EnhancedListViewKey + = GlobalKey>; typedef PaginatedListViewHeaderBuilder = Widget Function( Future> Function() headerItems); @@ -154,176 +152,17 @@ abstract interface class EnhancedListViewBase extends StatefulWidget { abstract interface class EnhancedListViewBaseState extends State {} -class EnhancedListView { - static EnhancedRemoteListView - remote({ - required Key? key, - required BodyItemsBuilder bodyItems, - required PaginatedListViewBodyBuilder bodyBuilder, - HeaderItemsBuilder? headerItems, - PaginatedListViewHeaderBuilder? headerBuilder, - FooterItemsBuilder? footerItems, - PaginatedListViewFooterBuilder? footerBuilder, - }) { - return EnhancedRemoteListView( - key: key, - bodyItems: bodyItems, - bodyBuilder: bodyBuilder, - headerItems: headerItems, - headerBuilder: headerBuilder, - footerItems: footerItems, - footerBuilder: footerBuilder, - ); - } - - static EnhancedLocalListView local({ - required Key? key, - required List list, - required Widget Function(T) itemBuilder, - required bool Function(T, String) filter, - List Function(String)? onSearch, - Widget? header, - }) { - return EnhancedLocalListView( - key: key, - list: list, - itemBuilder: itemBuilder, - filter: filter, - onSearch: onSearch, - header: header, - ); - } -} - -/// [EnhancedLocalListView] - -class EnhancedLocalListView extends EnhancedListViewBase { - final List list; - final Widget Function(T) itemBuilder; - final bool Function(T, String) filter; - final Widget header; - final List Function(String)? onSearch; - - EnhancedLocalListView({ - Key? key, - required this.list, - required this.itemBuilder, - required this.filter, - List Function(String)? onSearch, - Widget? header, - }) : header = header ?? const SizedBox.shrink(), - onSearch = onSearch ?? - ((String query) => - list.where((documents) => filter(documents, query)).toList()), - super(key: key); - - @override - EnhancedLocalListViewState createState() => - EnhancedLocalListViewState(); -} - -class EnhancedLocalListViewState - extends State> { - TextEditingController editingController = TextEditingController(); - late List filteredItems; - - @override - void initState() { - filteredItems = widget.list; - super.initState(); - } - - @override - Widget build(BuildContext context) { - void filter(value) { - setState(() { - filteredItems = widget.onSearch!(value); - }); - } - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.max, - children: [ - Expanded( - child: ListView.builder( - shrinkWrap: true, - itemCount: filteredItems.length + 1, - itemBuilder: (context, index) { - if (index == 0) return widget.header; - return widget.itemBuilder(filteredItems[index - 1]); - }, - ), - ), - Padding( - padding: const EdgeInsets.all(30.0), - child: TextFormField( - controller: editingController, - onChanged: filter, - cursorColor: Colors.black, - cursorWidth: 2.0, - cursorRadius: Radius.circular(2.0), - style: TextStyle( - color: Colors.black, - fontSize: 16.0, - ), - keyboardType: TextInputType.text, - textInputAction: TextInputAction.search, - autocorrect: true, - textCapitalization: TextCapitalization.sentences, - decoration: InputDecoration( - prefixIcon: Icon(Icons.search, color: Colors.black), - labelText: 'Pesquisar', - labelStyle: TextStyle( - color: Colors.black, - fontSize: 16.0, - ), - hintText: 'Digite sua pesquisa', - hintStyle: TextStyle( - color: Colors.grey, - fontSize: 14.0, - ), - filled: true, - fillColor: Colors.white, - contentPadding: - EdgeInsets.symmetric(vertical: 10.0, horizontal: 15.0), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(15.0)), - borderSide: BorderSide(color: Colors.black), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(15.0)), - borderSide: BorderSide(color: Colors.blue), - ), - errorBorder: OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(15.0)), - borderSide: BorderSide(color: Colors.red), - ), - focusedErrorBorder: OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(15.0)), - borderSide: BorderSide(color: Colors.red, width: 2.0), - ), - ), - ), - ), - ], - ); - } -} - -/// [EnhancedRemoteListView] - -class EnhancedRemoteListView +// ignore: must_be_immutable +class EnhancedListView extends EnhancedListViewBase { - final BodyItemsBuilder bodyItems; - final PaginatedListViewBodyBuilder bodyBuilder; - final HeaderItemsBuilder? headerItems; - final PaginatedListViewHeaderBuilder? headerBuilder; - final FooterItemsBuilder? footerItems; - final PaginatedListViewFooterBuilder? footerBuilder; + BodyItemsBuilder bodyItems; + PaginatedListViewBodyBuilder bodyBuilder; + HeaderItemsBuilder? headerItems; + PaginatedListViewHeaderBuilder? headerBuilder; + FooterItemsBuilder? footerItems; + PaginatedListViewFooterBuilder? footerBuilder; - const EnhancedRemoteListView({ + EnhancedListView({ Key? key, required this.bodyItems, required this.bodyBuilder, @@ -334,62 +173,88 @@ class EnhancedRemoteListView }) : super(key: key); @override - EnhancedRemoteListViewState createState() => - EnhancedRemoteListViewState(); + EnhancedListViewState createState() => + EnhancedListViewState(); } -class EnhancedRemoteListViewState - extends State> { +class EnhancedListViewState + extends State> { final ScrollController _scrollController = ScrollController(); bool _isLoadingMore = false; - List _items = []; - int _currentPage = 1; + List items = []; + List headerItems = []; + List footerItems = []; + int currentPage = 1; Query query; @override void initState() { super.initState(); _scrollController.addListener(_onScroll); - _loadInitialItems(); + _loadBodyItems(); + _loadHeaderItems(); + _loadFooterItems(); } void _onScroll() { if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent && !_isLoadingMore) { - _loadMoreItems(); + _loadMoreBodyItems(); } } - Future _loadInitialItems() async { + Future _loadBodyItems() async { + log('loadInitialItems'); final newItems = await widget.bodyItems(1, 10, query); setState(() { - _items = newItems; + items = newItems; }); } - Future _loadMoreItems() async { + Future _loadMoreBodyItems() async { + log('loadMoreItems'); setState(() { _isLoadingMore = true; }); - final newItems = await widget.bodyItems(_currentPage + 1, 10, query); + final newItems = await widget.bodyItems(currentPage + 1, 10, query); setState(() { _isLoadingMore = false; if (newItems.isNotEmpty) { - _items.addAll(newItems); - _currentPage++; + items.addAll(newItems); + currentPage++; } }); } - Future filterItems(Query newQuery) async { - log('filterItems: $newQuery'); + Future _loadHeaderItems() async { + if (widget.headerItems != null) { + log('loadHeaderItems'); + final newHeaderItems = await widget.headerItems!(); + setState(() { + headerItems = newHeaderItems; + }); + } + } + + Future _loadFooterItems() async { + if (widget.footerItems != null) { + log('loadFooterItems'); + final newFooterItems = await widget.footerItems!(); + setState(() { + footerItems = newFooterItems; + }); + } + } + + Future filterBodyItems(Query newQuery) async { + log('filterItems B: ${newQuery.toString()}'); setState(() { query = newQuery; - _items = []; - _currentPage = 1; + items = []; + currentPage = 1; }); - await _loadInitialItems(); + await _loadBodyItems(); } @override @@ -397,51 +262,26 @@ class EnhancedRemoteListViewState log('key: ${widget.key}'); return ListView.builder( controller: _scrollController, - itemCount: _items.length + - (widget.headerItems != null ? 1 : 0) + - (widget.footerItems != null ? 1 : 0) + + itemCount: items.length + + (headerItems.isNotEmpty ? 1 : 0) + + (footerItems.isNotEmpty ? 1 : 0) + (_isLoadingMore ? 1 : 0), itemBuilder: (context, index) { - if (widget.headerItems != null && index == 0) { - return FutureBuilder>( - future: widget.headerItems!(), - builder: (context, headerSnapshot) { - if (headerSnapshot.connectionState == ConnectionState.waiting) { - return const EnhancedProgressIndicator(); - } else if (headerSnapshot.hasError) { - return EnhancedErrorWidget(error: headerSnapshot.error); - } else { - return widget - .headerBuilder!(() => Future.value(headerSnapshot.data)); - } - }, - ); + if (headerItems.isNotEmpty && index == 0) { + return widget.headerBuilder!(() => Future.value(headerItems)); } - if (widget.footerItems != null && - index == _items.length + (widget.headerItems != null ? 1 : 0)) { - return FutureBuilder>( - future: widget.footerItems!(), - builder: (context, footerSnapshot) { - if (footerSnapshot.connectionState == ConnectionState.waiting) { - return const EnhancedProgressIndicator(); - } else if (footerSnapshot.hasError) { - return EnhancedErrorWidget( - error: footerSnapshot.error as Exception); - } else { - return widget - .footerBuilder!(() => Future.value(footerSnapshot.data)); - } - }, - ); + if (footerItems.isNotEmpty && + index == items.length + (headerItems.isNotEmpty ? 1 : 0)) { + return widget.footerBuilder!(() => Future.value(footerItems)); } if (_isLoadingMore && index == - _items.length + - (widget.headerItems != null ? 1 : 0) + - (widget.footerItems != null ? 1 : 0)) { + items.length + + (headerItems.isNotEmpty ? 1 : 0) + + (footerItems.isNotEmpty ? 1 : 0)) { return const EnhancedProgressIndicator(); } - final item = _items[index - (widget.headerItems != null ? 1 : 0)]; + final item = items[index - (headerItems.isNotEmpty ? 1 : 0)]; return widget.bodyBuilder(context, item as ItemType, index); }, );