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

View File

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