WIP
This commit is contained in:
parent
2487801ee1
commit
07f55bbceb
|
@ -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,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}');
|
||||||
|
|
|
@ -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>,
|
||||||
|
|
|
@ -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,
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
|
@ -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 (widget.footerItems != null &&
|
if (footerItems.isNotEmpty &&
|
||||||
index == _items.length + (widget.headerItems != null ? 1 : 0)) {
|
index == items.length + (headerItems.isNotEmpty ? 1 : 0)) {
|
||||||
return FutureBuilder<List<FooterType?>>(
|
return widget.footerBuilder!(() => Future.value(footerItems));
|
||||||
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);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue