This commit is contained in:
jantunesmessias 2025-02-19 10:43:20 -03:00
parent 23a6b6d1d9
commit 8b8950a992
4 changed files with 177 additions and 64 deletions

Binary file not shown.

View File

@ -118,18 +118,25 @@ class DocumentManagerScreen extends StatelessScreen {
} }
Widget buildBody(BuildContext context) { Widget buildBody(BuildContext context) {
final SizedBox space = SizedBox(height: 30); const SizedBox space = SizedBox(height: 10);
final controller = EnhancedListViewController(
headerBuilder: model.itemHeaderBuilder<SearchField>,
bodyBuilder: model.itemBodyBuilder<Document>,
footerBuilder: model.itemFooterBuilder<Category>,
);
final repository = EnhancedListViewRepository(
fetchHeader: model.generateHeaderItems<SearchField>,
fetchBody: model.generateBodyItems<Document, Query>,
fetchFooter: model.generateFooterItems<Category>,
);
return Column( return Column(
children: [ children: [
Expanded( Expanded(
child: EnhancedListView<Document, Search, Category, Query>( child: EnhancedListView<Document, SearchField, Category, Query>(
key: model.vehicleScreenManager, key: model.vehicleScreenManager,
headerBuilder: model.itemHeaderBuilder<Search>, controller: controller,
headerItems: model.generateHeaderItems<Search>, repository: repository,
bodyBuilder: model.itemBodyBuilder<Document>,
bodyItems: model.generateBodyItems<Document, Query>,
footerBuilder: model.itemFooterBuilder<Category>,
footerItems: model.generateFooterItems<Category>,
), ),
), ),
] // ] //
@ -235,7 +242,7 @@ class DocumentModel extends FlutterFlowModel<DocumentPage> {
final DocumentPageBlocType bloc; final DocumentPageBlocType bloc;
DocumentModel(this.bloc); DocumentModel(this.bloc);
late EnhancedListViewKey<Document, Search, Category, Query> late EnhancedListViewKey<Document, SearchField, Category, Query>
vehicleScreenManager; vehicleScreenManager;
late DocumentKey vehicleScreenViewer; late DocumentKey vehicleScreenViewer;
late PagingController<int, Document> _pagingController; late PagingController<int, Document> _pagingController;
@ -247,7 +254,7 @@ class DocumentModel extends FlutterFlowModel<DocumentPage> {
@override @override
void initState(BuildContext context) { void initState(BuildContext context) {
vehicleScreenManager = vehicleScreenManager =
EnhancedListViewKey<Document, Search, Category, Query>(); EnhancedListViewKey<Document, SearchField, Category, Query>();
vehicleScreenViewer = DocumentKey(); vehicleScreenViewer = DocumentKey();
_pagingController = PagingController<int, Document>(firstPageKey: 1); _pagingController = PagingController<int, Document>(firstPageKey: 1);
@ -388,7 +395,7 @@ class DocumentModel extends FlutterFlowModel<DocumentPage> {
/// [Header] /// [Header]
Widget itemHeaderBuilder<T extends Search>( Widget itemHeaderBuilder<T extends SearchField>(
Future<List<T?>> Function() generateHeaderItems) { Future<List<T?>> Function() generateHeaderItems) {
return Builder(builder: (context) { return Builder(builder: (context) {
final theme = FlutterFlowTheme.of(context); final theme = FlutterFlowTheme.of(context);
@ -462,8 +469,8 @@ class DocumentModel extends FlutterFlowModel<DocumentPage> {
}); });
} }
Future<List<T?>> generateHeaderItems<T extends Search>() async { Future<List<T?>> generateHeaderItems<T extends SearchField>() async {
final Search item = Search(); final SearchField item = SearchField();
return [item] as List<T?>; return [item] as List<T?>;
} }
@ -537,8 +544,8 @@ class DocumentPageBloc extends $DocumentPageBloc {
abstract interface class Archive extends Entity {} abstract interface class Archive extends Entity {}
interface class Search extends Archive { interface class SearchField extends Archive {
Search(); SearchField();
} }
interface class Document extends Archive { interface class Document extends Archive {

View File

@ -5,17 +5,19 @@ part of 'widgets.dart';
typedef EnhancedListViewKey<BodyType, HeaderType, FooterType, QueryType> typedef EnhancedListViewKey<BodyType, HeaderType, FooterType, QueryType>
= GlobalKey< = GlobalKey<
EnhancedListViewState<BodyType, HeaderType, FooterType, QueryType>>; EnhancedListViewState<BodyType, HeaderType, FooterType, QueryType>>;
typedef PaginatedListViewHeaderBuilder<HeaderType> = Widget Function( typedef HeaderTileBuilder<HeaderType> = Widget Function(
Future<List<HeaderType?>> Function() headerItems); Future<List<HeaderType?>> Function() headerItems);
typedef PaginatedListViewBodyBuilder<BodyType> = Widget Function( typedef BodyTileBuilder<BodyType> = Widget Function(
BuildContext context, BodyType item, int index); BuildContext context, BodyType item, int index);
typedef PaginatedListViewFooterBuilder<FooterType> = Widget Function( typedef FooterTileBuilder<FooterType> = Widget Function(
Future<List<FooterType?>> Function() footerItems); Future<List<FooterType?>> Function() footerItems);
typedef Query<QueryType> = QueryType?; typedef Query<QueryType> = QueryType?;
typedef BodyItemsBuilder<BodyType, QueryType> = Future<List<BodyType?>> typedef BodyRetrievalUseCase<BodyType, QueryType> = Future<List<BodyType?>>
Function(int page, int pageSize, QueryType query); Function(int page, int pageSize, QueryType query);
typedef HeaderItemsBuilder<HeaderType> = Future<List<HeaderType?>> Function(); typedef HeaderRetrievalUseCase<HeaderType> = Future<List<HeaderType?>>
typedef FooterItemsBuilder<FooterType> = Future<List<FooterType?>> Function(); Function();
typedef FooterRetrievalUseCase<FooterType> = Future<List<FooterType?>>
Function();
/// [Extensions] ---------------------------------------------------- /// [Extensions] ----------------------------------------------------
extension PaginatedListMergeExtensions<T> extension PaginatedListMergeExtensions<T>
@ -54,20 +56,20 @@ extension StreamStartWithExtension<T> on Stream<T> {
} }
extension QueryBlocStreamExtensions<T> on Stream<bool> { extension QueryBlocStreamExtensions<T> on Stream<bool> {
Stream<Result<EnhancedPaginatedList<T>>> fetchData( // Stream<Result<EnhancedPaginatedList<T>>> fetchData(
EnhancedListViewRepository<T> repository, // EnhancedListViewRepository<T> repository,
PaginatedListViewBodyBuilder<T> builder, // PaginatedListViewBodyBuilder<T> builder,
BehaviorSubject<EnhancedPaginatedList<T>> paginatedList, // BehaviorSubject<EnhancedPaginatedList<T>> paginatedList,
) => // ) =>
switchMap((reset) { // switchMap((reset) {
if (reset) { // if (reset) {
paginatedList.add(paginatedList.value.resetAll()); // paginatedList.add(paginatedList.value.resetAll());
} // }
final nextPage = paginatedList.value.currentPage + 1; // final nextPage = paginatedList.value.currentPage + 1;
return repository // return repository
.fetchPage(nextPage, paginatedList.value.pageSize, builder) // .fetchPage(nextPage, paginatedList.value.pageSize, builder)
.asResultStream(); // .asResultStream();
}); // });
} }
/// [Interfaces] ---------------------------------------------------- /// [Interfaces] ----------------------------------------------------
@ -167,21 +169,15 @@ mixin EnhancedListViewMixin<BodyType, HeaderType, FooterType, QueryType> {
class EnhancedListView<BodyType, HeaderType, FooterType, QueryType> class EnhancedListView<BodyType, HeaderType, FooterType, QueryType>
extends EnhancedListViewBase<BodyType, HeaderType, FooterType, QueryType> { extends EnhancedListViewBase<BodyType, HeaderType, FooterType, QueryType> {
final BodyItemsBuilder<BodyType, QueryType?> bodyItems; final EnhancedListViewRepository<BodyType, HeaderType, FooterType, QueryType>
final PaginatedListViewBodyBuilder<BodyType> bodyBuilder; repository;
final HeaderItemsBuilder<HeaderType>? headerItems; final EnhancedListViewController<BodyType, HeaderType, FooterType, QueryType>
final PaginatedListViewHeaderBuilder<HeaderType>? headerBuilder; controller;
final FooterItemsBuilder<FooterType>? footerItems;
final PaginatedListViewFooterBuilder<FooterType>? footerBuilder;
const EnhancedListView({ const EnhancedListView({
Key? key, Key? key,
required this.bodyItems, required this.repository,
required this.bodyBuilder, required this.controller,
this.headerItems,
this.headerBuilder,
this.footerItems,
this.footerBuilder,
}) : super(key: key); }) : super(key: key);
@override @override
@ -198,9 +194,9 @@ class EnhancedListViewState<ItemType, HeaderType, FooterType, QueryType>
super.initState(); super.initState();
bloc = EnhancedListViewBloc<ItemType, HeaderType, FooterType, QueryType>( bloc = EnhancedListViewBloc<ItemType, HeaderType, FooterType, QueryType>(
bodyItemsBuilder: widget.bodyItems, bodyItemsBuilder: widget.repository.fetchBody,
headerItemsBuilder: widget.headerItems ?? () async => [], headerItemsBuilder: widget.repository.fetchHeader ?? () async => [],
footerItemsBuilder: widget.footerItems ?? () async => [], footerItemsBuilder: widget.repository.fetchFooter ?? () async => [],
); );
bloc.events.loadBodyItems(); bloc.events.loadBodyItems();
bloc.events.loadHeaderItems(); bloc.events.loadHeaderItems();
@ -219,7 +215,8 @@ class EnhancedListViewState<ItemType, HeaderType, FooterType, QueryType>
} else if (!headerSnapshot.hasData || headerSnapshot.data!.isEmpty) { } else if (!headerSnapshot.hasData || headerSnapshot.data!.isEmpty) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} else { } else {
return widget.headerBuilder!(() async => headerSnapshot.data!); return widget
.controller.headerBuilder!(() async => headerSnapshot.data!);
} }
}, },
); );
@ -233,7 +230,8 @@ class EnhancedListViewState<ItemType, HeaderType, FooterType, QueryType>
} else if (!footerSnapshot.hasData || footerSnapshot.data!.isEmpty) { } else if (!footerSnapshot.hasData || footerSnapshot.data!.isEmpty) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} else { } else {
return widget.footerBuilder!(() async => footerSnapshot.data!); return widget
.controller.footerBuilder!(() async => footerSnapshot.data!);
} }
}, },
); );
@ -251,8 +249,8 @@ class EnhancedListViewState<ItemType, HeaderType, FooterType, QueryType>
return ListView.builder( return ListView.builder(
itemCount: bodySnapshot.data?.length ?? 0, itemCount: bodySnapshot.data?.length ?? 0,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return widget.bodyBuilder( return widget.controller
context, bodySnapshot.data![index], index); .bodyBuilder(context, bodySnapshot.data![index], index);
}, },
); );
} }
@ -262,9 +260,9 @@ class EnhancedListViewState<ItemType, HeaderType, FooterType, QueryType>
return Column( return Column(
children: [ children: [
if (widget.headerBuilder != null) header, if (widget.controller.headerBuilder != null) header,
body, body,
if (widget.footerBuilder != null) footer, if (widget.controller.footerBuilder != null) footer,
], ],
); );
} }
@ -340,15 +338,47 @@ class AuthorizationError implements Exception {
AuthorizationError(this.message); AuthorizationError(this.message);
} }
/// [Repositories] ----------------------------------------------------
class EnhancedListViewRepository<BodyType, HeaderType, FooterType, QueryType> {
final BodyRetrievalUseCase<BodyType, QueryType?> fetchBody;
final HeaderRetrievalUseCase<HeaderType>? fetchHeader;
final FooterRetrievalUseCase<FooterType>? fetchFooter;
const EnhancedListViewRepository({
required this.fetchBody,
this.fetchHeader,
this.fetchFooter,
});
}
// class HeaderRepository<HeaderType, QueryType> {}
// class BodyRepository<BodyType, QueryType> {}
// class FooterRepository<FooterType, QueryType> {}
/// [Controllers] ----------------------------------------------------
class EnhancedListViewController<BodyType, HeaderType, FooterType, QueryType> {
final BodyTileBuilder<BodyType> bodyBuilder;
final HeaderTileBuilder<HeaderType>? headerBuilder;
final FooterTileBuilder<FooterType>? footerBuilder;
const EnhancedListViewController({
required this.bodyBuilder,
this.headerBuilder,
this.footerBuilder,
});
}
// class HeaderController<HeaderType, QueryType> {}
// class BodyController<BodyType, QueryType> {}
// class FooterController<FooterType, QueryType> {}
/// [Blocs] ---------------------------------------------------- /// [Blocs] ----------------------------------------------------
Stream<bool> get loadingState => Stream<bool>.empty(); Stream<bool> get loadingState => Stream<bool>.empty();
Stream<Exception> get errorState => Stream<Exception>.empty(); Stream<Exception> get errorState => Stream<Exception>.empty();
abstract class EnhancedListViewRepository<T> {
Future<EnhancedPaginatedList<T>> fetchPage(
int page, int pageSize, PaginatedListViewBodyBuilder<T> builder);
}
abstract class EnhancedListViewEvents<BodyType, HeaderType, FooterType, abstract class EnhancedListViewEvents<BodyType, HeaderType, FooterType,
QueryType> { QueryType> {
void loadBodyItems({bool reset = false, dynamic query = null}); void loadBodyItems({bool reset = false, dynamic query = null});
@ -390,9 +420,9 @@ class EnhancedListViewBloc<BodyType, HeaderType, FooterType, QueryType>
.addTo(_compositeSubscription); .addTo(_compositeSubscription);
} }
final BodyItemsBuilder<BodyType, QueryType?> bodyItemsBuilder; final BodyRetrievalUseCase<BodyType, QueryType?> bodyItemsBuilder;
final HeaderItemsBuilder<HeaderType> headerItemsBuilder; final HeaderRetrievalUseCase<HeaderType> headerItemsBuilder;
final FooterItemsBuilder<FooterType> footerItemsBuilder; final FooterRetrievalUseCase<FooterType> footerItemsBuilder;
final _bodyItems = BehaviorSubject<List<BodyType>>.seeded([]); final _bodyItems = BehaviorSubject<List<BodyType>>.seeded([]);

View File

@ -0,0 +1,76 @@
import 'package:flutter/material.dart';
class EnhancedSearchView extends StatelessWidget {
const EnhancedSearchView({super.key});
@override
Widget build(BuildContext context) {
final theme = FlutterFlowTheme.of(context);
final locale = FFLocalizations.of(context);
TextEditingController editingController = TextEditingController();
return TextFormField(
controller: editingController,
onChanged: (value) => EasyDebounce.debounce(
'_model.keyTextFieldTextController',
const Duration(milliseconds: 500),
() => filterBySearchBar(Document.fromDesc(value), context),
),
cursorColor: theme.primaryText,
showCursor: false,
cursorWidth: 2.0,
cursorRadius: Radius.circular(100),
style: TextStyle(
color: theme.primaryText,
fontSize: 16.0,
decorationColor: Colors.amber,
),
keyboardType: TextInputType.text,
textInputAction: TextInputAction.search,
autocorrect: true,
textCapitalization: TextCapitalization.sentences,
decoration: InputDecoration(
prefixIcon: Icon(Icons.search, color: theme.primary),
labelText: locale.getVariableText(
ptText: 'Pesquisar',
enText: 'Search',
),
labelStyle: TextStyle(
color: theme.primaryText,
fontSize: 16.0,
),
hintText: locale.getVariableText(
ptText: 'Digite sua pesquisa',
enText: 'Enter your search',
),
hintStyle: TextStyle(
color: theme.accent2,
fontSize: 14.0,
),
filled: true,
fillColor: Colors.transparent,
helperStyle: TextStyle(
color: theme.primaryText,
decorationColor: theme.primaryText,
),
focusColor: theme.primaryText,
contentPadding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 15.0),
enabledBorder: UnderlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(15.0)),
borderSide: BorderSide(color: theme.primaryText),
),
focusedBorder: UnderlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(15.0)),
borderSide: BorderSide(color: theme.primaryText),
),
errorBorder: UnderlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(15.0)),
borderSide: BorderSide(color: theme.primaryText),
),
focusedErrorBorder: UnderlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(15.0)),
borderSide: BorderSide(color: theme.primaryText, width: 2.0),
),
),
);
}
}