This commit is contained in:
jantunesmessias 2025-02-17 14:15:46 -03:00
parent 2487801ee1
commit 07f55bbceb
6 changed files with 282 additions and 443 deletions

View File

@ -6,22 +6,22 @@ part of 'index.dart';
class DocumentPageBloc extends Bloc<DocumentPageEvent, DocumentPageState> { class DocumentPageBloc extends Bloc<DocumentPageEvent, DocumentPageState> {
final DocumentPageModel model; final DocumentPageModel model;
static DocumentPageBloc? _singleton;
DocumentPageBloc._(this.model, DocumentPageState initialState) factory DocumentPageBloc(DocumentPageModel model) {
: super(initialState) { _singleton ??= DocumentPageBloc._internal(model);
return _singleton!;
}
DocumentPageBloc._internal(this.model) : super(DocumentPageState()) {
on<SelectDocumentEvent>(_selectDocument); on<SelectDocumentEvent>(_selectDocument);
on<UnselectDocumentEvent>(_unselectDocument); on<UnselectDocumentEvent>(_unselectDocument);
on<FilterCategoryEvent>(_filterCategoryEvent); on<FilterCategoryEvent>(_filterCategoryEvent);
} }
static DocumentPageBloc create(DocumentPageModel model) {
final initialState = DocumentPageState();
return DocumentPageBloc._(model, initialState);
}
Future<void> _filterCategoryEvent( Future<void> _filterCategoryEvent(
FilterCategoryEvent event, Emitter<DocumentPageState> emit) async { FilterCategoryEvent event, Emitter<DocumentPageState> emit) async {
log('generateDocuments -> ${state.isCategorySelected}'); _selectCategory(event, emit);
state.isCategorySelected state.isCategorySelected
? _unselectCategory(event, emit) ? _unselectCategory(event, emit)
: _selectCategory(event, emit); : _selectCategory(event, emit);
@ -29,16 +29,14 @@ class DocumentPageBloc extends Bloc<DocumentPageEvent, DocumentPageState> {
Future<void> _selectCategory( Future<void> _selectCategory(
FilterCategoryEvent event, Emitter<DocumentPageState> emit) async { FilterCategoryEvent event, Emitter<DocumentPageState> emit) async {
log('filterItems A: ${event.query}');
emit(state.copyWith( emit(state.copyWith(
isCategorySelected: true, isCategorySelected: true,
)); ));
final listViewState = model.enhancedListViewKey.currentState!; final listViewState = model.vihicleScreenManager.currentState!;
listViewState.safeSetState(() async { listViewState.widget.bodyItems = (await model.generateBodyItems(
return await listViewState.filterItems(event.query); 1, 10, event.query)) as BodyItemsBuilder<Document>;
});
// state.isCategorySelected ? s.filter(event.query) : s.filter(event.query);
} }
Future<void> _unselectCategory( Future<void> _unselectCategory(
@ -47,10 +45,9 @@ class DocumentPageBloc extends Bloc<DocumentPageEvent, DocumentPageState> {
isCategorySelected: false, isCategorySelected: false,
)); ));
final listViewState = model.enhancedListViewKey.currentState!; final listViewState = model.vihicleScreenManager.currentState!;
listViewState.safeSetState(() async { listViewState.widget.bodyItems = (await model.generateBodyItems(
return await listViewState.filterItems(null); 1, 10, null)) as BodyItemsBuilder<Document>;
});
} }
Future<void> _selectDocument( Future<void> _selectDocument(
@ -69,15 +66,10 @@ class DocumentPageBloc extends Bloc<DocumentPageEvent, DocumentPageState> {
Future<void> _unselectDocument( Future<void> _unselectDocument(
UnselectDocumentEvent event, Emitter<DocumentPageState> emit) async { UnselectDocumentEvent event, Emitter<DocumentPageState> emit) async {
// final docs = await model.generateDocuments(state.page, state.query);
// final cats = await model.generateCategories();
emit( emit(
state.copyWith( state.copyWith(
currentDocument: null, currentDocument: null,
isDocumentSelected: false, isDocumentSelected: false,
// documents: docs.$2,
// categories: cats,
), ),
); );
} }

View File

@ -1,21 +1,25 @@
part of 'index.dart'; part of 'index.dart';
class DocumentPageModel extends FlutterFlowModel<DocumentPage> { class DocumentPageModel extends FlutterFlowModel<DocumentPage> {
DocumentPageModel(); DocumentPageModel._privateConstructor();
late final GlobalKey<State<FutureBuilder>> pageKey; static final DocumentPageModel _instance =
late final EnhancedRemoteListViewKey<Document, Category, Null> DocumentPageModel._privateConstructor();
enhancedListViewKey;
late final DocumentKey viewerKey; factory DocumentPageModel() {
late final PagingController<int, Document> _pagingController; return _instance;
}
late EnhancedListViewKey<Document, Category, Null> vihicleScreenManager;
late DocumentKey vehicleScreenViewer;
late PagingController<int, Document> _pagingController;
/// ------------ /// ------------
@override @override
void initState(BuildContext context) { void initState(BuildContext context) {
pageKey = GlobalKey<State<FutureBuilder>>(); vihicleScreenManager = EnhancedListViewKey<Document, Category, Null>();
enhancedListViewKey = EnhancedRemoteListViewKey<Document, Category, Null>(); vehicleScreenViewer = DocumentKey();
viewerKey = DocumentKey();
_pagingController = PagingController<int, Document>(firstPageKey: 1); _pagingController = PagingController<int, Document>(firstPageKey: 1);
} }
@ -23,8 +27,8 @@ class DocumentPageModel extends FlutterFlowModel<DocumentPage> {
@override @override
void dispose() { void dispose() {
_pagingController.dispose(); _pagingController.dispose();
// isCategorySelected = false; vihicleScreenManager.currentState?.dispose();
// isDocumentSelected = false; vehicleScreenViewer.currentState?.dispose();
} }
/// ------------ /// ------------
@ -77,7 +81,7 @@ class DocumentPageModel extends FlutterFlowModel<DocumentPage> {
}); });
/// [generateBodyItems] /// [generateBodyItems]
Future<List<T?>> generateBodyItems<T extends Document>( Future<List<T?>> generateBodyItems<T>(
int pageKey, int pageSize, dynamic query) async { int pageKey, int pageSize, dynamic query) async {
log('generateDocuments: $query'); log('generateDocuments: $query');
@ -86,54 +90,73 @@ class DocumentPageModel extends FlutterFlowModel<DocumentPage> {
final GetDocuments getDocuments = FreAccessWSGlobal.getDocuments; final GetDocuments getDocuments = FreAccessWSGlobal.getDocuments;
final ApiCallResponse newItems = await getDocuments.call(pageKey, query); final ApiCallResponse newItems = await getDocuments.call(pageKey, query);
if (newItems.jsonBody == null || newItems.jsonBody['error'] == true) { if (newItems.jsonBody == null) return error;
return error; if (newItems.jsonBody['error'] == true) return error;
}
final List<dynamic> list = newItems.jsonBody['value']['list']; final List<dynamic> list = newItems.jsonBody['value']['list'];
final List<Document> docs = list.map((item) {
late final List<Document> docs = [];
for (var item in list) {
log('-> generateDocuments: $item'); log('-> generateDocuments: $item');
return Document( final String description = item['description'];
id: item['id'], final String type = item['type'];
description: item['description'], final String category = item['category']['description'];
type: item['type'], 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( category: Category(
id: item['category']['id'], id: categoryId,
color: item['category']['color'].toColor(), color: color.toColor(),
title: item['category']['description'], title: category,
), ),
person: item['person'] ?? '', person: person,
property: item['property'] ?? '', property: property,
createdAt: item['createdAt'], createdAt: createdAt,
updatedAt: item['updatedAt'], updatedAt: updatedAt,
); );
}).toList();
docs.add(doc);
}
return docs as List<T?>; return docs as List<T?>;
} }
/// [generateHeaderItems] /// [generateHeaderItems]
Future<List<T?>> generateHeaderItems<T extends Category>() async { Future<List<T?>> generateHeaderItems<T>() async {
log('generateCategories: ');
final List<T?> error = [null]; final List<T?> error = [null];
final GetCategories getCategories = FreAccessWSGlobal.getCategories; final GetCategories getCategories = FreAccessWSGlobal.getCategories;
final ApiCallResponse newItems = await getCategories.call(); final ApiCallResponse newItems = await getCategories.call();
if (newItems.jsonBody == null || newItems.jsonBody['error'] == true) { if (newItems.jsonBody['error'] == true) return error;
return error; if (newItems.jsonBody == null) return error;
} final list = newItems.jsonBody['value'] as List<dynamic>;
late final List<Category> cats = [];
for (var item in list) {
final String color = item['color'];
final String title = item['description'];
final int id = item['id'];
final List<dynamic> list = newItems.jsonBody['value']; final cat = Category(
final List<Category> categories = list.map((item) { id: id,
return Category( color: color.toColor(),
id: item['id'], title: title,
color: item['color'].toColor(),
title: item['description'],
); );
}).toList(); cats.add(cat);
}
log('categories: $categories'); log('cats: $cats');
return categories as List<T?>; return cats as List<T?>;
} }
/// [filter] /// [filter]
@ -142,26 +165,10 @@ class DocumentPageModel extends FlutterFlowModel<DocumentPage> {
.read<DocumentPageBloc>() .read<DocumentPageBloc>()
.add(FilterCategoryEvent(query as Archive?)); .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] /// [onFetchError]
void onFetchError(Object e, StackTrace s) { void onFetchError(Object e, StackTrace s) {
DialogUtil.errorDefault(viewerKey.currentContext!); DialogUtil.errorDefault(vehicleScreenViewer.currentContext!);
LogUtil.requestAPIFailed( LogUtil.requestAPIFailed(
"proccessRequest.php", "", "Consulta de Veículo", e, s); "proccessRequest.php", "", "Consulta de Veículo", e, s);
} }

View File

@ -23,7 +23,7 @@ class FREDocumentPageState<T extends DocumentPage>
Widget buildBody(BuildContext context) { Widget buildBody(BuildContext context) {
return BlocProvider<DocumentPageBloc>( return BlocProvider<DocumentPageBloc>(
create: (context) => DocumentPageBloc.create(model), create: (context) => DocumentPageBloc(model),
child: BlocBuilder<DocumentPageBloc, DocumentPageState>( child: BlocBuilder<DocumentPageBloc, DocumentPageState>(
builder: (context, state) { builder: (context, state) {
print('Bloc -> ${state.isCategorySelected}'); print('Bloc -> ${state.isCategorySelected}');

View File

@ -31,8 +31,8 @@ class DocumentManagerScreen extends StatelessScreen {
return Column( return Column(
children: [ children: [
Expanded( Expanded(
child: EnhancedListView.remote<Document, Category, Null>( child: EnhancedListView<Document, Category, Null>(
key: model.enhancedListViewKey, key: model.vihicleScreenManager,
headerBuilder: model.itemHeaderBuilder<Category>, headerBuilder: model.itemHeaderBuilder<Category>,
headerItems: model.generateHeaderItems<Category>, headerItems: model.generateHeaderItems<Category>,
bodyBuilder: model.itemBodyBuilder<Document>, bodyBuilder: model.itemBodyBuilder<Document>,

View File

@ -1,140 +1,140 @@
import 'package:flutter/material.dart'; // import 'package:flutter/material.dart';
import 'package:hub/flutter_flow/index.dart'; // import 'package:hub/flutter_flow/index.dart';
import 'package:hub/shared/utils/limited_text_size.dart'; // import 'package:hub/shared/utils/limited_text_size.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; // import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
extension PagedListViewExtension<PageKeyType, ItemType> // extension PagedListViewExtension<PageKeyType, ItemType>
on PagedSliverList<PageKeyType, ItemType> {} // on PagedSliverList<PageKeyType, ItemType> {}
typedef PaginatedListViewHeaderBuilder<T> = Widget Function<T>( // typedef PaginatedListViewHeaderBuilder<T> = Widget Function<T>(
Future<List<T?>> Function() gen); // Future<List<T?>> Function() gen);
typedef PaginatedListViewBodyBuilder<T> = Widget Function(BuildContext, T, int); // typedef PaginatedListViewBodyBuilder<T> = Widget Function(BuildContext, T, int);
mixin Pageable<T extends StatefulWidget> on State<T> { // mixin Pageable<T extends StatefulWidget> on State<T> {
Expanded buildPaginatedListView<PageKeyType, BodyType, HeaderType>( // Expanded buildPaginatedListView<PageKeyType, BodyType, HeaderType>(
String noDataFound, // String noDataFound,
PagingController<PageKeyType, BodyType> pg, // PagingController<PageKeyType, BodyType> pg,
Future<List<HeaderType?>> Function() headerItems, // Future<List<HeaderType?>> Function() headerItems,
PaginatedListViewHeaderBuilder<BodyType> headerBuilder, // PaginatedListViewHeaderBuilder<BodyType> headerBuilder,
PaginatedListViewBodyBuilder<BodyType> bodyBuilder) { // PaginatedListViewBodyBuilder<BodyType> bodyBuilder) {
final theme = FlutterFlowTheme.of(context); // final theme = FlutterFlowTheme.of(context);
return Expanded( // return Expanded(
child: RefreshIndicator( // child: RefreshIndicator(
backgroundColor: theme.primaryBackground, // backgroundColor: theme.primaryBackground,
color: theme.primary, // color: theme.primary,
onRefresh: () async => pg.refresh(), // onRefresh: () async => pg.refresh(),
child: PagedListView<PageKeyType, BodyType>( // child: PagedListView<PageKeyType, BodyType>(
pagingController: pg, // pagingController: pg,
builderDelegate: PagedChildBuilderDelegate<BodyType>( // builderDelegate: PagedChildBuilderDelegate<BodyType>(
animateTransitions: true, // animateTransitions: true,
itemBuilder: (context, item, int index) { // itemBuilder: (context, item, int index) {
return Column(children: [ // return Column(children: [
if (index == 0) headerBuilder(headerItems), // if (index == 0) headerBuilder(headerItems),
bodyBuilder(context, item, index), // bodyBuilder(context, item, index),
]); // ]);
}, // },
newPageProgressIndicatorBuilder: (context) => // newPageProgressIndicatorBuilder: (context) =>
buildLoadingIndicator(context), // buildLoadingIndicator(context),
firstPageProgressIndicatorBuilder: (context) => // firstPageProgressIndicatorBuilder: (context) =>
buildLoadingIndicator(context), // buildLoadingIndicator(context),
noItemsFoundIndicatorBuilder: (context) => // noItemsFoundIndicatorBuilder: (context) =>
buildNoDataFound<HeaderType>( // buildNoDataFound<HeaderType>(
context, noDataFound, headerItems, headerBuilder), // context, noDataFound, headerItems, headerBuilder),
firstPageErrorIndicatorBuilder: (context) => const Placeholder(), // firstPageErrorIndicatorBuilder: (context) => const Placeholder(),
newPageErrorIndicatorBuilder: (context) => const Placeholder(), // newPageErrorIndicatorBuilder: (context) => const Placeholder(),
), // ),
), // ),
), // ),
); // );
} // }
Future<void> showError(PagingStatus status, PagingController pg) async { // Future<void> showError(PagingStatus status, PagingController pg) async {
if (status == PagingStatus.subsequentPageError) { // if (status == PagingStatus.subsequentPageError) {
final message = FFLocalizations.of(context).getVariableText( // final message = FFLocalizations.of(context).getVariableText(
enText: 'Something went wrong while fetching a new page.', // enText: 'Something went wrong while fetching a new page.',
ptText: 'Algo deu errado ao buscar uma nova página.', // ptText: 'Algo deu errado ao buscar uma nova página.',
); // );
final retry = FFLocalizations.of(context).getVariableText( // final retry = FFLocalizations.of(context).getVariableText(
enText: 'Retry', // enText: 'Retry',
ptText: 'Recarregar', // ptText: 'Recarregar',
); // );
ScaffoldMessenger.of(context).showSnackBar( // ScaffoldMessenger.of(context).showSnackBar(
SnackBar( // SnackBar(
content: Text(message), // content: Text(message),
action: SnackBarAction( // action: SnackBarAction(
label: retry, // label: retry,
onPressed: () => pg.retryLastFailedRequest(), // onPressed: () => pg.retryLastFailedRequest(),
), // ),
), // ),
); // );
} // }
} // }
Future<void> fetchPage({ // Future<void> fetchPage({
required Future<(bool, dynamic)> Function() dataProvider, // required Future<(bool, dynamic)> Function() dataProvider,
required void Function(dynamic data) onDataAvailable, // required void Function(dynamic data) onDataAvailable,
required void Function() onDataUnavailable, // required void Function() onDataUnavailable,
required void Function(Object error, StackTrace stackTrace) onFetchError, // required void Function(Object error, StackTrace stackTrace) onFetchError,
}) async { // }) async {
try { // try {
final (bool isDataAvailable, dynamic data) = await dataProvider(); // final (bool isDataAvailable, dynamic data) = await dataProvider();
if (isDataAvailable) { // if (isDataAvailable) {
onDataAvailable(data); // onDataAvailable(data);
} else { // } else {
onDataUnavailable(); // onDataUnavailable();
} // }
} catch (error, stackTrace) { // } catch (error, stackTrace) {
onFetchError(error, stackTrace); // onFetchError(error, stackTrace);
} // }
} // }
void showNoMoreDataSnackBar(BuildContext context) { // void showNoMoreDataSnackBar(BuildContext context) {
final message = FFLocalizations.of(context).getVariableText( // final message = FFLocalizations.of(context).getVariableText(
ptText: "Não há mais dados.", // ptText: "Não há mais dados.",
enText: "No more data.", // enText: "No more data.",
); // );
showSnackbar(context, message, true); // showSnackbar(context, message, true);
} // }
Widget buildNoDataFound<T>( // Widget buildNoDataFound<T>(
BuildContext context, // BuildContext context,
String title, // String title,
Future<List<T?>> Function() items, // Future<List<T?>> Function() items,
Widget Function<T>(Future<List<T?>> Function() items) headerBuilder, // Widget Function<T>(Future<List<T?>> Function() items) headerBuilder,
) { // ) {
final headerFontSize = LimitedFontSizeUtil.getHeaderFontSize(context); // final headerFontSize = LimitedFontSizeUtil.getHeaderFontSize(context);
// final bodyFontSize = LimitedFontSizeUtil.getBodyFontSize(context); // // final bodyFontSize = LimitedFontSizeUtil.getBodyFontSize(context);
return Column( // return Column(
children: [ // children: [
headerBuilder(items), // headerBuilder(items),
Expanded( // Expanded(
child: Center( // child: Center(
child: Text( // child: Text(
title, // title,
style: TextStyle( // style: TextStyle(
fontFamily: 'Nunito', // fontFamily: 'Nunito',
fontSize: headerFontSize, // fontSize: headerFontSize,
), // ),
), // ),
), // ),
), // ),
], // ],
); // );
} // }
Widget buildLoadingIndicator(BuildContext context) { // Widget buildLoadingIndicator(BuildContext context) {
print('Loading'); // print('Loading');
return Container( // return Container(
padding: const EdgeInsets.symmetric(vertical: 15), // padding: const EdgeInsets.symmetric(vertical: 15),
child: Center( // child: Center(
child: CircularProgressIndicator( // child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>( // valueColor: AlwaysStoppedAnimation<Color>(
FlutterFlowTheme.of(context).primary, // FlutterFlowTheme.of(context).primary,
), // ),
), // ),
), // ),
); // );
} // }
} // }

View File

@ -2,10 +2,8 @@ part of 'widgets.dart';
/// [TypeDefs] /// [TypeDefs]
typedef EnhancedRemoteListViewKey<B, H, F> typedef EnhancedListViewKey<B, H, F>
= GlobalKey<EnhancedRemoteListViewState<B, H, F>>; = GlobalKey<EnhancedListViewState<B, H, F>>;
typedef EnhancedLocalListViewKey<B, H, F>
= GlobalKey<EnhancedLocalListViewState<B, H, F>>;
typedef PaginatedListViewHeaderBuilder<H> = Widget Function( typedef PaginatedListViewHeaderBuilder<H> = Widget Function(
Future<List<H?>> Function() headerItems); Future<List<H?>> Function() headerItems);
@ -154,176 +152,17 @@ abstract interface class EnhancedListViewBase<T, H, F> extends StatefulWidget {
abstract interface class EnhancedListViewBaseState<T> abstract interface class EnhancedListViewBaseState<T>
extends State<EnhancedListViewBase> {} extends State<EnhancedListViewBase> {}
class EnhancedListView { // ignore: must_be_immutable
static EnhancedRemoteListView<BodyType, HeaderType, FooterType> class EnhancedListView<BodyType, HeaderType, FooterType>
remote<BodyType, HeaderType, FooterType>({
required Key? key,
required BodyItemsBuilder<BodyType> bodyItems,
required PaginatedListViewBodyBuilder<BodyType> bodyBuilder,
HeaderItemsBuilder<HeaderType>? headerItems,
PaginatedListViewHeaderBuilder<HeaderType>? headerBuilder,
FooterItemsBuilder<FooterType>? footerItems,
PaginatedListViewFooterBuilder<FooterType>? footerBuilder,
}) {
return EnhancedRemoteListView<BodyType, HeaderType, FooterType>(
key: key,
bodyItems: bodyItems,
bodyBuilder: bodyBuilder,
headerItems: headerItems,
headerBuilder: headerBuilder,
footerItems: footerItems,
footerBuilder: footerBuilder,
);
}
static EnhancedLocalListView<T, H, F> local<T, H, F>({
required Key? key,
required List<T> list,
required Widget Function(T) itemBuilder,
required bool Function(T, String) filter,
List<T> Function(String)? onSearch,
Widget? header,
}) {
return EnhancedLocalListView<T, H, F>(
key: key,
list: list,
itemBuilder: itemBuilder,
filter: filter,
onSearch: onSearch,
header: header,
);
}
}
/// [EnhancedLocalListView]
class EnhancedLocalListView<T, H, F> extends EnhancedListViewBase<T, H, F> {
final List<T> list;
final Widget Function(T) itemBuilder;
final bool Function(T, String) filter;
final Widget header;
final List<T> Function(String)? onSearch;
EnhancedLocalListView({
Key? key,
required this.list,
required this.itemBuilder,
required this.filter,
List<T> 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<T, H, F> createState() =>
EnhancedLocalListViewState<T, H, F>();
}
class EnhancedLocalListViewState<T, H, F>
extends State<EnhancedLocalListView<T, H, F>> {
TextEditingController editingController = TextEditingController();
late List<T> 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: <Widget>[
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<BodyType, HeaderType, FooterType>
extends EnhancedListViewBase<BodyType, HeaderType, FooterType> { extends EnhancedListViewBase<BodyType, HeaderType, FooterType> {
final BodyItemsBuilder<BodyType> bodyItems; BodyItemsBuilder<BodyType> bodyItems;
final PaginatedListViewBodyBuilder<BodyType> bodyBuilder; PaginatedListViewBodyBuilder<BodyType> bodyBuilder;
final HeaderItemsBuilder<HeaderType>? headerItems; HeaderItemsBuilder<HeaderType>? headerItems;
final PaginatedListViewHeaderBuilder<HeaderType>? headerBuilder; PaginatedListViewHeaderBuilder<HeaderType>? headerBuilder;
final FooterItemsBuilder<FooterType>? footerItems; FooterItemsBuilder<FooterType>? footerItems;
final PaginatedListViewFooterBuilder<FooterType>? footerBuilder; PaginatedListViewFooterBuilder<FooterType>? footerBuilder;
const EnhancedRemoteListView({ EnhancedListView({
Key? key, Key? key,
required this.bodyItems, required this.bodyItems,
required this.bodyBuilder, required this.bodyBuilder,
@ -334,62 +173,88 @@ class EnhancedRemoteListView<BodyType, HeaderType, FooterType>
}) : super(key: key); }) : super(key: key);
@override @override
EnhancedRemoteListViewState<BodyType, HeaderType, FooterType> createState() => EnhancedListViewState<BodyType, HeaderType, FooterType> createState() =>
EnhancedRemoteListViewState<BodyType, HeaderType, FooterType>(); EnhancedListViewState<BodyType, HeaderType, FooterType>();
} }
class EnhancedRemoteListViewState<ItemType, HeaderType, FooterType> class EnhancedListViewState<ItemType, HeaderType, FooterType>
extends State<EnhancedRemoteListView<ItemType, HeaderType, FooterType>> { extends State<EnhancedListView<ItemType, HeaderType, FooterType>> {
final ScrollController _scrollController = ScrollController(); final ScrollController _scrollController = ScrollController();
bool _isLoadingMore = false; bool _isLoadingMore = false;
List<ItemType?> _items = []; List<ItemType?> items = [];
int _currentPage = 1; List<HeaderType?> headerItems = [];
List<FooterType?> footerItems = [];
int currentPage = 1;
Query<ItemType> query; Query<ItemType> query;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_scrollController.addListener(_onScroll); _scrollController.addListener(_onScroll);
_loadInitialItems(); _loadBodyItems();
_loadHeaderItems();
_loadFooterItems();
} }
void _onScroll() { void _onScroll() {
if (_scrollController.position.pixels == if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent && _scrollController.position.maxScrollExtent &&
!_isLoadingMore) { !_isLoadingMore) {
_loadMoreItems(); _loadMoreBodyItems();
} }
} }
Future<void> _loadInitialItems() async { Future<void> _loadBodyItems() async {
log('loadInitialItems');
final newItems = await widget.bodyItems(1, 10, query); final newItems = await widget.bodyItems(1, 10, query);
setState(() { setState(() {
_items = newItems; items = newItems;
}); });
} }
Future<void> _loadMoreItems() async { Future<void> _loadMoreBodyItems() async {
log('loadMoreItems');
setState(() { setState(() {
_isLoadingMore = true; _isLoadingMore = true;
}); });
final newItems = await widget.bodyItems(_currentPage + 1, 10, query); final newItems = await widget.bodyItems(currentPage + 1, 10, query);
setState(() { setState(() {
_isLoadingMore = false; _isLoadingMore = false;
if (newItems.isNotEmpty) { if (newItems.isNotEmpty) {
_items.addAll(newItems); items.addAll(newItems);
_currentPage++; currentPage++;
} }
}); });
} }
Future<void> filterItems(Query<ItemType> newQuery) async { Future<void> _loadHeaderItems() async {
log('filterItems: $newQuery'); if (widget.headerItems != null) {
log('loadHeaderItems');
final newHeaderItems = await widget.headerItems!();
setState(() {
headerItems = newHeaderItems;
});
}
}
Future<void> _loadFooterItems() async {
if (widget.footerItems != null) {
log('loadFooterItems');
final newFooterItems = await widget.footerItems!();
setState(() {
footerItems = newFooterItems;
});
}
}
Future<void> filterBodyItems(Query<ItemType> newQuery) async {
log('filterItems B: ${newQuery.toString()}');
setState(() { setState(() {
query = newQuery; query = newQuery;
_items = []; items = [];
_currentPage = 1; currentPage = 1;
}); });
await _loadInitialItems(); await _loadBodyItems();
} }
@override @override
@ -397,51 +262,26 @@ class EnhancedRemoteListViewState<ItemType, HeaderType, FooterType>
log('key: ${widget.key}'); log('key: ${widget.key}');
return ListView.builder( return ListView.builder(
controller: _scrollController, controller: _scrollController,
itemCount: _items.length + itemCount: items.length +
(widget.headerItems != null ? 1 : 0) + (headerItems.isNotEmpty ? 1 : 0) +
(widget.footerItems != null ? 1 : 0) + (footerItems.isNotEmpty ? 1 : 0) +
(_isLoadingMore ? 1 : 0), (_isLoadingMore ? 1 : 0),
itemBuilder: (context, index) { itemBuilder: (context, index) {
if (widget.headerItems != null && index == 0) { if (headerItems.isNotEmpty && index == 0) {
return FutureBuilder<List<HeaderType?>>( return widget.headerBuilder!(() => Future.value(headerItems));
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 (footerItems.isNotEmpty &&
); index == items.length + (headerItems.isNotEmpty ? 1 : 0)) {
} return widget.footerBuilder!(() => Future.value(footerItems));
if (widget.footerItems != null &&
index == _items.length + (widget.headerItems != null ? 1 : 0)) {
return FutureBuilder<List<FooterType?>>(
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 (_isLoadingMore && if (_isLoadingMore &&
index == index ==
_items.length + items.length +
(widget.headerItems != null ? 1 : 0) + (headerItems.isNotEmpty ? 1 : 0) +
(widget.footerItems != null ? 1 : 0)) { (footerItems.isNotEmpty ? 1 : 0)) {
return const EnhancedProgressIndicator(); 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); return widget.bodyBuilder(context, item as ItemType, index);
}, },
); );