From 1dc611004ac07bf4f8e3385c67abfe8c6e2d33ca Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Wed, 12 Feb 2025 17:49:55 -0300 Subject: [PATCH] WIP --- .vscode/launch.json | 28 ++ .../card_item_template_component_widget.dart | 1 + .../backend/api_requests/api_calls.dart | 59 +++- .../documents/document_item_component.dart | 72 ++++- .../documents/document_manager_screen.dart | 99 +----- .../documents/document_page_model.dart | 155 ++++++++++ .../documents/document_page_widget.dart | 71 ++--- .../documents/document_viewer_screen.dart | 36 ++- lib/features/documents/index.dart | 9 + lib/flutter_flow/flutter_flow_theme.dart | 4 +- lib/flutter_flow/nav/nav.dart | 14 +- lib/main.dart | 69 ++--- lib/pages/pets_page/pets_page_model.dart | 7 +- lib/shared/extensions/string_extensions.dart | 17 +- lib/shared/mixins/index.dart | 5 +- lib/shared/mixins/pegeable_mixin.dart | 51 ++-- lib/shared/utils/validator_util.dart | 16 +- lib/shared/widgets/view/carousel_view.dart | 38 +-- lib/shared/widgets/view/search_view.dart | 285 +++++++++++------- lib/shared/widgets/viewer/viewer.dart | 30 +- lib/shared/widgets/widgets.dart | 3 +- pubspec.yaml | 2 +- 22 files changed, 698 insertions(+), 373 deletions(-) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..fb1433dc --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,28 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "flutter-freaccesss-hub", + "request": "launch", + "type": "dart", + "args": [ + "--no-enable-impeller" + ] + }, + { + "name": "flutter-freaccesss-hub (profile mode)", + "request": "launch", + "type": "dart", + "flutterMode": "profile" + }, + { + "name": "flutter-freaccesss-hub (release mode)", + "request": "launch", + "type": "dart", + "flutterMode": "release" + } + ] +} \ No newline at end of file diff --git a/lib/components/templates_components/card_item_template_component/card_item_template_component_widget.dart b/lib/components/templates_components/card_item_template_component/card_item_template_component_widget.dart index a5527a22..9e968770 100644 --- a/lib/components/templates_components/card_item_template_component/card_item_template_component_widget.dart +++ b/lib/components/templates_components/card_item_template_component/card_item_template_component_widget.dart @@ -101,6 +101,7 @@ class _CardItemTemplateComponentWidgetState } Widget _generateImage() { + print('img: ${widget.imagePath ?? ''}'); // CachedNetworkImage.evictFromCache(widget.imagePath ?? ''); return ClipRRect( borderRadius: BorderRadius.circular(20), diff --git a/lib/features/backend/api_requests/api_calls.dart b/lib/features/backend/api_requests/api_calls.dart index d9fce908..ded72f97 100644 --- a/lib/features/backend/api_requests/api_calls.dart +++ b/lib/features/backend/api_requests/api_calls.dart @@ -5,11 +5,14 @@ import 'dart:convert'; import 'dart:developer'; import 'package:flutter/foundation.dart'; +import 'package:hub/features/documents/index.dart' as doc; import 'package:hub/features/notification/index.dart'; import 'package:hub/features/storage/index.dart'; +import 'package:hub/shared/extensions/index.dart'; import 'package:hub/shared/utils/log_util.dart'; import 'package:hub/shared/utils/validator_util.dart'; +import 'package:hub/shared/widgets/widgets.dart'; import '/flutter_flow/flutter_flow_util.dart'; import 'api_manager.dart'; @@ -24,7 +27,7 @@ abstract class Api { GetLicense getLicense = GetLicense(); } -abstract class Endpoint {} +abstract interface class Endpoint {} class FreAccessWSGlobal extends Api { static String getBaseUrl() => 'https://freaccess.com.br/freaccess'; @@ -70,6 +73,7 @@ class FreAccessWSGlobal extends Api { static GetResidentsByProperty getResidentsByProperty = GetResidentsByProperty(); static GetOpenedVisits getOpenedVisits = GetOpenedVisits(); + @override GetLicense getLicense = GetLicense(); static GetProvSchedules getProvSchedules = GetProvSchedules(); static GetCategories getCategories = GetCategories(); @@ -77,6 +81,7 @@ class FreAccessWSGlobal extends Api { } class GetCategories extends Endpoint { + @override Future call() async { final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = @@ -113,7 +118,11 @@ class GetCategories extends Endpoint { } class GetDocuments extends Endpoint { - Future call(final String page) async { + @override + Future call( + final dynamic page, + final Query query, + ) async { final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; @@ -124,6 +133,8 @@ class GetDocuments extends Endpoint { const String atividade = 'listaDocumentos'; const String pageSize = '10'; // final bool isFiltered = filter != '' && filter != '.*'; + // final isCategory = !category; + // final isDescription = !desc.isNullOrEmpty; return await ApiManager.instance.makeApiCall( callName: 'listaDocumentos', apiUrl: '$baseUrl/processRequest.php', @@ -131,12 +142,15 @@ class GetDocuments extends Endpoint { headers: {'Content-Type': 'application/x-www-form-urlencoded'}, params: { // if (isFiltered) 'filter': filter, + 'devUUID': devUUID, 'userUUID': userUUID, 'cliID': cliID, 'atividade': atividade, - 'page': page, + 'page': page.toString(), 'pageSize': pageSize, + if (query is doc.Category) 'categoryId': query.id, + if (query is doc.Document) 'searh': query.description, }, bodyType: BodyType.X_WWW_FORM_URL_ENCODED, returnBody: true, @@ -150,6 +164,7 @@ class GetDocuments extends Endpoint { } class GetProvSchedules extends Endpoint { + @override Future call(final String page, final String status) async { final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = @@ -188,6 +203,7 @@ class GetProvSchedules extends Endpoint { } class GetOpenedVisits extends Endpoint { + @override Future call(final String page) async { final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = @@ -223,6 +239,7 @@ class GetOpenedVisits extends Endpoint { } class GetResidentsByProperty extends Endpoint { + @override Future call(final String page) async { final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = @@ -257,6 +274,7 @@ class GetResidentsByProperty extends Endpoint { } class GetVehiclesByProperty extends Endpoint { + @override Future call(final String page) async { final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = @@ -293,6 +311,7 @@ class GetVehiclesByProperty extends Endpoint { } class GetLicense extends Endpoint { + @override Future call() async { final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = @@ -327,6 +346,7 @@ class GetLicense extends Endpoint { } class UnregisterDevice extends Endpoint { + @override Future call() async { final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = @@ -357,6 +377,7 @@ class UnregisterDevice extends Endpoint { } class DeletePet extends Endpoint { + @override Future call({final int? petID = 0}) async { final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = @@ -393,6 +414,7 @@ class DeletePet extends Endpoint { } class UpdatePet extends Endpoint { + @override Future call({ final int? petID, final String? image, @@ -452,6 +474,7 @@ class UpdatePet extends Endpoint { } class GetPets extends Endpoint { + @override Future call({ final int? page, final int? pageSize, @@ -492,6 +515,7 @@ class GetPets extends Endpoint { } class GetPetPhoto extends Endpoint { + @override Future call({final int? petId}) async { final String baseUrl = FreAccessWSGlobal.getBaseUrl(); @@ -528,6 +552,7 @@ class GetPetPhoto extends Endpoint { } class RegisterPet extends Endpoint { + @override Future call({ final String? image, final String? name, @@ -585,6 +610,7 @@ class RegisterPet extends Endpoint { } class BuscaEnconcomendas extends Endpoint { + @override Future call({ final String? page, final String? pageSize, @@ -629,6 +655,7 @@ class BuscaEnconcomendas extends Endpoint { } class CancelaVisita extends Endpoint { + @override Future call({ final int? idDestino, final int? idVisita, @@ -674,6 +701,7 @@ class CancelaVisita extends Endpoint { } class DeleteAccount extends Endpoint { + @override Future call() async { final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; @@ -704,6 +732,7 @@ class DeleteAccount extends Endpoint { } class ChangePanic extends Endpoint { + @override Future call({ final String? newSenhaPanico, }) async { @@ -743,6 +772,7 @@ class ChangePanic extends Endpoint { } class ChangePass extends Endpoint { + @override Future call({ final String? newSenha, }) async { @@ -782,6 +812,7 @@ class ChangePass extends Endpoint { } class RespondeVinculo extends Endpoint { + @override Future call({ final String? tarefa, }) async { @@ -819,6 +850,7 @@ class RespondeVinculo extends Endpoint { } class ChangeNotifica extends Endpoint { + @override Future call({ final String? notifica, }) async { @@ -858,6 +890,7 @@ class ChangeNotifica extends Endpoint { } class UpdateIDE extends Endpoint { + @override Future call() async { final String baseUrl = FreAccessWSGlobal.getBaseUrl(); @@ -897,6 +930,7 @@ class UpdateIDE extends Endpoint { } class UpdToken extends Endpoint { + @override Future call() async { final String baseUrl = FreAccessWSGlobal.getBaseUrl(); @@ -930,6 +964,7 @@ class UpdToken extends Endpoint { } class LoginCall extends Endpoint { + @override Future call() async { final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = @@ -980,6 +1015,7 @@ class LoginCall extends Endpoint { } class RegisterCall extends Endpoint { + @override Future call({ required final String name, required final String email, @@ -1019,6 +1055,7 @@ class RegisterCall extends Endpoint { } class ChangePasswordCall extends Endpoint { + @override Future call({ required final String email, required final String token, @@ -1059,6 +1096,7 @@ class ChangePasswordCall extends Endpoint { } class ForgotPasswordCall extends Endpoint { + @override Future call({ final String? email, }) async { @@ -1086,6 +1124,7 @@ class ForgotPasswordCall extends Endpoint { } class GetLocalsCall extends Endpoint { + @override Future call() async { final String baseUrl = FreAccessWSGlobal.getBaseUrl(); @@ -1125,6 +1164,7 @@ class GetLocalsCall extends Endpoint { } class PostScheduleVisitorCall extends Endpoint { + @override Future call({ required final String documento, required final String nome, @@ -1179,6 +1219,7 @@ class PostScheduleVisitorCall extends Endpoint { } class PostScheduleVisitCall extends Endpoint { + @override Future call({ final String? devDesc, final String? idVisitante, @@ -1241,6 +1282,7 @@ class PostScheduleVisitCall extends Endpoint { } class GetScheduleVisitCall extends Endpoint { + @override Future call({ final int? pageSize, final int? pageNumber, @@ -1522,6 +1564,7 @@ class GetScheduleVisitCall extends Endpoint { } class GetDadosCall extends Endpoint { + @override Future call() async { final String baseUrl = FreAccessWSGlobal.getBaseUrl(); @@ -1768,6 +1811,7 @@ class GetDadosCall extends Endpoint { } class GetVisitorByDocCall extends Endpoint { + @override Future call({ final String? documento, }) async { @@ -1824,6 +1868,7 @@ class GetVisitorByDocCall extends Endpoint { } class GetFotoVisitanteCall extends Endpoint { + @override Future call({ final String? documento, final String? tipo, @@ -1864,6 +1909,7 @@ class GetFotoVisitanteCall extends Endpoint { } class PostProvVisitSchedulingCall extends Endpoint { + @override Future call({ final String? data, final String? motivo, @@ -1918,6 +1964,7 @@ class PostProvVisitSchedulingCall extends Endpoint { } class GetVisitsCall extends Endpoint { + @override Future call({ final int? pageSize, final int? pageNumber, @@ -2185,6 +2232,7 @@ class GetVisitsCall extends Endpoint { } class DeleteVisitCall extends Endpoint { + @override Future call({ final String? idVisita, }) async { @@ -2233,6 +2281,7 @@ class DeleteVisitCall extends Endpoint { } class GetPessoasLocalCall extends Endpoint { + @override Future call() async { final String baseUrl = FreAccessWSGlobal.getBaseUrl(); @@ -2298,6 +2347,7 @@ class GetPessoasLocalCall extends Endpoint { } class RespondeSolicitacaoCall extends Endpoint { + @override Future call({ final String? referencia, final String? tarefa, @@ -2352,6 +2402,7 @@ class RespondeSolicitacaoCall extends Endpoint { } class GetAccessCall extends Endpoint { + @override Future call({ final String? pageSize, final String? pageNumber, @@ -2606,6 +2657,7 @@ class GetAccessCall extends Endpoint { } class GetLiberationsCall extends Endpoint { + @override Stream call() { final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final StreamController controller = StreamController(); @@ -2799,6 +2851,7 @@ class GetLiberationsCall extends Endpoint { } class GetMessagesCall extends Endpoint { + @override Future call({ final String? pageSize, final String? pageNumber, diff --git a/lib/features/documents/document_item_component.dart b/lib/features/documents/document_item_component.dart index 963ada8f..0228c90f 100644 --- a/lib/features/documents/document_item_component.dart +++ b/lib/features/documents/document_item_component.dart @@ -1,31 +1,60 @@ part of 'index.dart'; -interface class Category extends Entity { +abstract interface class DocumentEntity extends Entity {} + +interface class Category extends DocumentEntity { + final int id; final Color color; final String title; Category({ + required this.id, required this.color, required this.title, }); + + factory Category.fromDesc(String desc) { + return Category( + id: 0, + color: Colors.transparent, + title: desc, + ); + } + + static Color isSelected() => Colors.black; } -interface class Document extends Entity { - final String title; +interface class Document extends DocumentEntity { + final int id; + final String description; + final String type; final Category category; - final String to; - final String from; - final String createdAt; - final String updatedAt; + final String person; + final String property; + String createdAt; + String updatedAt; Document({ + required this.id, + required this.description, + required this.type, + required this.category, + required this.person, + required this.property, required this.createdAt, required this.updatedAt, - required this.category, - required this.to, - required this.from, - required this.title, }); + + factory Document.fromDesc(String desc) => Document( + id: 0, + description: desc, + type: '', + category: Category.fromDesc(''), + person: '', + property: '', + createdAt: '', + updatedAt: '', + ); } class DocumentItem extends StatelessWidget { @@ -77,6 +106,16 @@ class DocumentItem extends StatelessWidget { fontWeight: FontWeight.normal, fontStyle: FontStyle.italic, ); + final Map extra = { + 'document': document, + kTransitionInfoKey: const TransitionInfo( + hasTransition: true, + transitionType: PageTransitionType.rightToLeft, + alignment: Alignment.bottomCenter, + ), + }; + Future onTap() => + context.push('/documentViewerScreen', extra: extra); return Padding( padding: const EdgeInsets.all(8), @@ -87,9 +126,9 @@ class DocumentItem extends StatelessWidget { : MediaQuery.of(context).size.height * 2; return InkWell( - onTap: () => print('Click'), + onTap: onTap, enableFeedback: true, - overlayColor: MaterialStateProperty.all(primaryColor), + overlayColor: WidgetStateProperty.all(primaryColor), borderRadius: BorderRadius.circular(10), child: SizedBox( height: boxHeight, @@ -105,15 +144,16 @@ class DocumentItem extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ Tooltip( - message: document.title, + message: document.description, child: AutoText( - document.title, + document.description, style: textStyleMajor, overflow: TextOverflow.ellipsis, ), ), AutoText( - document.updatedAt, + ValidatorUtil.toLocalDateTime( + 'yyyy-MM-dd', document.updatedAt), style: textStyleMinor, overflow: TextOverflow.ellipsis, ), diff --git a/lib/features/documents/document_manager_screen.dart b/lib/features/documents/document_manager_screen.dart index 0608e17e..354a0825 100644 --- a/lib/features/documents/document_manager_screen.dart +++ b/lib/features/documents/document_manager_screen.dart @@ -1,102 +1,27 @@ part of 'index.dart'; class DocumentManagerScreen extends StatelessScreen { - DocumentManagerScreen({ - super.key, - required this.documents, - required this.categories, - }); + final DocumentPageModel model; - List documents; - final List categories; + const DocumentManagerScreen({ + super.key, + required this.model, + }); @override Widget build(BuildContext context) { - final GlobalKey _listViewKey = - GlobalKey(); - - bool filter(document, query) { - final lowerQuery = query.toLowerCase(); - return document.title.toLowerCase().contains(lowerQuery) || - document.description.toLowerCase().contains(lowerQuery) || - document.category.title.toLowerCase().contains(lowerQuery) || - document.to.toLowerCase().contains(lowerQuery) || - document.from.toLowerCase().contains(lowerQuery) || - document.createdAt.toLowerCase().contains(lowerQuery) || - document.updatedAt.toLowerCase().contains(lowerQuery); - } - - DocumentItem itemBuilder(dynamic item) { - log('item: $item'); - - final doc = Document( - createdAt: '00/00/00', - updatedAt: '00/00/00', - category: Category(color: Colors.black, title: item['category']), - to: item['person'], - from: '', - title: item['description'], - ); - final docItem = DocumentItem(document: doc); - return docItem; - } - - void filterByCategory(Category query) { - print('Test'); - final state = _listViewKey.currentState; - - if (state != null) { - state.safeSetState(() { - state.filteredItems = documents - .where((documents) => filter(documents, query.title)) - .toList(); - }); - } - } - - void unfilter(Category) { - final state = _listViewKey.currentState; - if (state != null) { - state.safeSetState(() { - state.filteredItems = documents; - }); - } - } - - final header = Column( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(15, 0, 50, 0), - child: Text('Últimos Documentos'), - ), - CategoryCarousel( - categories: categories, - onSelect: filterByCategory, - onUnselect: unfilter, - ), - ], - ); - List filterByString(String query) { - return documents.where((documents) => filter(documents, query)).toList(); - } - final SizedBox space = SizedBox(height: 30); return Column( children: [ Expanded( - child: RemoteSearchListView( - key: _listViewKey, - header: header, - onSearch: filterByString, - list: documents, - itemBuilder: itemBuilder, - filter: filter, - title: '', - // fetchItems: (String ) { },, + child: RemoteSearchView( + key: model.searchKey, + pagingController: model._pagingController, + headerBuilder: model.listHeaderBuilder, + bodyBuilder: model.listBodyBuilder, + dataProvider: model.generateDocuments, + onFetchError: model.onFetchError, ), ), ] // diff --git a/lib/features/documents/document_page_model.dart b/lib/features/documents/document_page_model.dart index 9ec58ac2..4439deae 100644 --- a/lib/features/documents/document_page_model.dart +++ b/lib/features/documents/document_page_model.dart @@ -1 +1,156 @@ part of 'index.dart'; + +class DocumentPageModel extends FlutterFlowModel { + @override + void dispose() {} + + @override + void initState(BuildContext context) {} + + final SearchKey searchKey = SearchKey(); + final DocumentKey docKey = DocumentKey(); + + final PagingController _pagingController = + PagingController(firstPageKey: 1); + int count = 0; + final dynamic page = 1; + + Query query = Document.fromDesc(''); + + late Document currentDocument; + bool isCategorySelected = false; + late Category currentCategory; + + List documents = []; + List categories = []; + + /// [listBodyBuilder] + Widget listBodyBuilder(BuildContext context, Document item, int index) { + return DocumentItem(document: item); + } + + /// [listHeaderBuilder] + Widget listHeaderBuilder(BuildContext context) => Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(15, 0, 50, 0), + child: Text( + 'Últimos Documentos', + style: TextStyle( + color: FlutterFlowTheme.of(context).primaryText, + fontSize: LimitedFontSizeUtil.getHeaderFontSize(context), + ), + ), + ), + CategoryCarousel( + categories: categories, + filter: filterByCategory, + ), + ], + ); + + /// [generateDocuments] + Future<(bool, List)> generateDocuments( + pageKey, Query query) async { + final List error = [null]; + print('Query: ${query is Document}'); + final GetDocuments getDocuments = FreAccessWSGlobal.getDocuments; + final ApiCallResponse newItems = await getDocuments.call(pageKey, query); + + if (newItems.jsonBody == null) return (false, error); + if (newItems.jsonBody['error'] == true) return (false, error); + + final List list = newItems.jsonBody['value']['list']; + + late final List docs = []; + + for (var item in list) { + log('-> generateDocuments: $item'); + final String description = item['description']; + final String type = item['type']; + final String category = item['category']['description']; + final String color = item['category']['color']; + final String person = item['person'] ?? ''; + final String property = item['property'] ?? ''; + final String createdAt = item['createdAt']; + final String updatedAt = item['updatedAt']; + final int categoryId = item['category']['id']; + final int documentId = item['id']; + + final doc = Document( + id: documentId, + description: description, + type: type, + category: Category( + id: categoryId, + color: color.toColor(), + title: category, + ), + person: person, + property: property, + createdAt: createdAt, + updatedAt: updatedAt, + ); + + docs.add(doc); + } + + return (true, docs); + // listViewKey.currentState!.count = newItems.jsonBody['value']['count'] ?? 0; + } + + /// [generateCategories] + Future> generateCategories(List documents) async { + final List error = [null]; + if (documents == []) return error; + + final GetCategories getCategories = FreAccessWSGlobal.getCategories; + final ApiCallResponse newItems = await getCategories.call(); + + if (newItems.jsonBody['error'] == true) return error; + if (newItems.jsonBody == null) return error; + final list = newItems.jsonBody['value'] as List; + late final List cats = []; + for (var item in list) { + final String color = item['color']; + final String title = item['description']; + final int id = item['id']; + + final cat = Category( + id: id, + color: color.toColor(), + title: title, + ); + cats.add(cat); + } + log('cats: $cats'); + return cats; + } + + void filterByCategory(Category query) { + final state = searchKey.currentState; + + if (state != null) { + log('filterByCategories: '); + + state.safeSetState(() { + if (isCategorySelected) { + state.filter(null); + isCategorySelected = false; + } else { + state.filter(query); + isCategorySelected = true; + } + }); + } + } + + void onFetchError(Object e, StackTrace s) { + DialogUtil.errorDefault(docKey.currentContext!); + LogUtil.requestAPIFailed( + "proccessRequest.php", "", "Consulta de Veículo", e, s); + } +} diff --git a/lib/features/documents/document_page_widget.dart b/lib/features/documents/document_page_widget.dart index a70f2775..ec558fda 100644 --- a/lib/features/documents/document_page_widget.dart +++ b/lib/features/documents/document_page_widget.dart @@ -1,69 +1,54 @@ part of 'index.dart'; -List generateDocuments(int count) { - String str() => randomString(8, 8, true, true, true); - Color color() => randomColor(); +typedef DocumentKey = GlobalKey; - return List.generate( - count, - (index) => Document( - title: 'Lorem Ipsum et Cetera $index', - category: Category(color: color(), title: str()), - to: str(), - from: str(), - createdAt: '00/00/0000', - updatedAt: '00/00/0000', - ), - ); -} - -List generateCategories(List documents) { - final Map categoryMap = {}; - - for (var document in documents) { - final category = document.category; - if (!categoryMap.containsKey(category.title)) { - categoryMap[category.title] = category; - } - } - - return categoryMap.values.toList(); -} - -class FREDocumentPage extends StatefulPage { - const FREDocumentPage({super.key}); +class DocumentPage extends StatefulPage { + const DocumentPage({super.key}); @override - State createState() => _FREDocumentPageState(); + State createState() => FREDocumentPageState(); } -class _FREDocumentPageState extends PageState { +class FREDocumentPageState + extends PageState { + DocumentPageModel model = DocumentPageModel(); + @override Widget build(BuildContext context) { - final String title = FFLocalizations.of(context) - .getVariableText(enText: 'Documents', ptText: 'Documentos'); + final String title = FFLocalizations.of(context).getVariableText( + enText: 'Documents', + ptText: 'Documentos', + ); + final theme = FlutterFlowTheme.of(context); return Scaffold( + backgroundColor: theme.primaryBackground, appBar: buildAppBar(title, context), body: buildBody(context), ); } - late List documents; - late List categories; - @override void initState() { super.initState(); - documents = generateDocuments(20); - categories = generateCategories(documents); } Widget buildBody(BuildContext context) { - return DocumentManagerScreen( - documents: documents, - categories: categories, + return FutureBuilder( + future: initAsync(), + builder: (context, snapshot) { + return DocumentManagerScreen(model: model); + }, ); // return DocumentViewScreen(document: documents.first); } + + Future initAsync() async { + final documents = await model.generateDocuments(model.page, model.query); + final categories = await model.generateCategories(model.documents); + model.documents = documents.$2; + model.categories = categories; + log('-> generateDocuments: $documents'); + log('-> generateCategories: $categories'); + } } diff --git a/lib/features/documents/document_viewer_screen.dart b/lib/features/documents/document_viewer_screen.dart index 9c4a0c63..7b9b704e 100644 --- a/lib/features/documents/document_viewer_screen.dart +++ b/lib/features/documents/document_viewer_screen.dart @@ -13,33 +13,45 @@ class DocumentViewScreen extends StatefulScreen { } class _DocumentViewScreenState extends State { - final PDFViewerState _viewerKey = PDFViewerState(); + final PDFViewerKey _viewerKey = PDFViewerKey(); @override Widget build(BuildContext context) { + final Uri url = Uri.parse( + 'https://cdn.syncfusion.com/content/PDFViewer/flutter-succinctly.pdf'); + + void onPressed() async { + final response = await http.get(url); + if (response.statusCode == 200) { + final XFile xfile = XFile.fromData(response.bodyBytes, + name: + '${widget.document.description}_${widget.document.category.title}.pdf', + mimeType: 'application/pdf'); + await Share.shareXFiles([xfile], text: 'Confira este PDF!'); + } else { + print('Erro ao baixar o arquivo: ${response.statusCode}'); + } + } + return Stack( children: [ Padding( padding: EdgeInsets.all(10), child: FREViewerPDF( - key: _viewerKey, - url: - 'https://cdn.syncfusion.com/content/PDFViewer/flutter-succinctly.pdf', + search: _viewerKey, + src: url.toString(), ), ), Positioned( bottom: 10, right: 10, child: IconButton( - icon: Icon(Icons.share, color: Colors.black), + icon: Icon( + Icons.share, + color: Colors.black, + ), color: Colors.black, - onPressed: () { - _viewerKey.currentState?.openBookmarkView(); - // Share.share(FFLocalizations.of(context).getVariableText( - // ptText: '', - // enText: '', - // )); - }, + onPressed: onPressed, ), ), ], diff --git a/lib/features/documents/index.dart b/lib/features/documents/index.dart index c32958c7..864743e1 100644 --- a/lib/features/documents/index.dart +++ b/lib/features/documents/index.dart @@ -1,9 +1,18 @@ import 'dart:developer'; +import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:http/http.dart' as http; +import 'package:hub/features/backend/index.dart'; import 'package:hub/flutter_flow/index.dart'; +import 'package:hub/shared/extensions/index.dart'; +import 'package:hub/shared/mixins/pegeable_mixin.dart'; import 'package:hub/shared/utils/index.dart'; import 'package:hub/shared/widgets/widgets.dart'; +import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; +import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart'; +import 'package:share_plus/share_plus.dart'; part 'document_manager_screen.dart'; part 'document_page_widget.dart'; diff --git a/lib/flutter_flow/flutter_flow_theme.dart b/lib/flutter_flow/flutter_flow_theme.dart index d178b517..b331757e 100644 --- a/lib/flutter_flow/flutter_flow_theme.dart +++ b/lib/flutter_flow/flutter_flow_theme.dart @@ -158,7 +158,7 @@ class LightModeTheme extends FlutterFlowTheme { late Color primary = const Color(0xFF1AAB5F); late Color secondary = const Color(0xFFB59E9E); - late Color tertiary = const Color(0xFF984BB6); + late Color tertiary = const Color(0xFF000000); late Color alternate = const Color(0xFFF2F2F2); late Color alternate2 = const Color(0xFF232323); late Color primaryText = const Color(0xFF000000); @@ -192,7 +192,7 @@ class DarkModeTheme extends FlutterFlowTheme { late Color primary = const Color(0xFF1AAB5F); late Color secondary = const Color(0xFF18AA99); - late Color tertiary = const Color(0xFF984BB6); + late Color tertiary = const Color(0xFF000000); late Color alternate = const Color(0xFF232323); late Color alternate2 = const Color(0xFF171717); late Color primaryText = const Color(0xFFFFFFFF); diff --git a/lib/flutter_flow/nav/nav.dart b/lib/flutter_flow/nav/nav.dart index 0da33c1e..357346f8 100644 --- a/lib/flutter_flow/nav/nav.dart +++ b/lib/flutter_flow/nav/nav.dart @@ -303,7 +303,19 @@ GoRouter createRouter(AppStateNotifier appStateNotifier) { name: 'documentPage', path: '/documentPage', builder: (context, params) { - return FREDocumentPage(); + return DocumentPage(); + }, + ), + FFRoute( + name: 'documentViewerScreen', + path: '/documentViewerScreen', + builder: (context, params) { + final Document document = + params.getParam('document', ParamType.Function); + return DocumentViewScreen( + key: UniqueKey(), + document: document, + ); }, ), // FFRoute(name: 'settingsPage', path: '/settingsPage', builder: (context, params) => params.isEmpty ? const NavBarPage(initialPage: 'settingsPage') : const SettingsPageWidget()) diff --git a/lib/main.dart b/lib/main.dart index 34d1a25f..e5e2cf09 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -74,38 +74,6 @@ class _AppState extends State { late GoRouter _router; bool displaySplashImage = true; - final ThemeData _darkTheme = ThemeData( - brightness: Brightness.dark, - scrollbarTheme: ScrollbarThemeData( - thumbVisibility: WidgetStateProperty.all(false), - interactive: false, - thumbColor: WidgetStateProperty.resolveWith((states) { - if (states.contains(WidgetState.dragged)) { - return const Color(0xff1aab5f); - } - if (states.contains(WidgetState.hovered)) { - return const Color(0xff1aab5f); - } - return const Color(0xff1aab5f); - }), - ), - ); - final ThemeData _theme = ThemeData( - brightness: Brightness.light, - scrollbarTheme: ScrollbarThemeData( - thumbVisibility: WidgetStateProperty.all(false), - interactive: false, - thumbColor: WidgetStateProperty.resolveWith((states) { - if (states.contains(WidgetState.dragged)) { - return const Color(0xff1aab5f); - } - if (states.contains(WidgetState.hovered)) { - return const Color(0xff1aab5f); - } - return const Color(0xff1aab5f); - }), - ), - ); final Iterable>? localizationsDelegates = const [ FFLocalizationsDelegate(), @@ -201,6 +169,39 @@ class _AppState extends State { @override Widget build(BuildContext context) { + final themeSchema = FlutterFlowTheme.of(context); + + final ThemeData darkTheme = ThemeData( + brightness: Brightness.dark, + scrollbarTheme: ScrollbarThemeData( + thumbVisibility: WidgetStateProperty.all(false), + interactive: false, + thumbColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.dragged)) return themeSchema.primary; + if (states.contains(WidgetState.hovered)) return themeSchema.primary; + return themeSchema.primary; + }), + ), + ); + final ThemeData lightTheme = ThemeData( + brightness: Brightness.light, + textSelectionTheme: TextSelectionThemeData( + cursorColor: themeSchema.primaryText, // Cor do cursor + selectionColor: themeSchema.accent2, // Cor da seleção de texto + selectionHandleColor: + themeSchema.primaryText, // Cor do manipulador de seleção + ), + scrollbarTheme: ScrollbarThemeData( + thumbVisibility: WidgetStateProperty.all(false), + interactive: false, + thumbColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.dragged)) return themeSchema.primary; + if (states.contains(WidgetState.hovered)) return themeSchema.primary; + return themeSchema.primary; + }), + ), + ); + return MaterialApp.router( scrollBehavior: CustomScrollBehavior(), key: navigatorKey, @@ -209,8 +210,8 @@ class _AppState extends State { localizationsDelegates: localizationsDelegates, locale: _locale, supportedLocales: supportedLocales, - theme: _theme, - darkTheme: _darkTheme, + theme: lightTheme, + darkTheme: darkTheme, themeMode: _themeMode, routerConfig: _router, ); diff --git a/lib/pages/pets_page/pets_page_model.dart b/lib/pages/pets_page/pets_page_model.dart index d9e96e61..753455fc 100644 --- a/lib/pages/pets_page/pets_page_model.dart +++ b/lib/pages/pets_page/pets_page_model.dart @@ -163,8 +163,10 @@ class PetsPageModel extends FlutterFlowModel { // updateImage!(); (() async { - Response response = await get(Uri.parse( - 'https://freaccess.com.br/freaccess/getImage.php?devUUID=$devUUID&userUUID=$userUUID&cliID=$cliUUID&atividade=consultaFotoPet&petId=$petId')); + final String url = + 'https://freaccess.com.br/freaccess/getImage.php?devUUID=$devUUID&userUUID=$userUUID&cliID=$cliUUID&atividade=consultaFotoPet&petId=$petId'; + print('img: $url'); + Response response = await get(Uri.parse(url)); String base64 = base64Encode(response.bodyBytes); uploadedTempFile = await ImageUtils.convertToUploadFile(base64); updateImage?.call(); @@ -280,6 +282,7 @@ class PetsPageModel extends FlutterFlowModel { img = "base64;jpeg,$img"; final url = 'https://freaccess.com.br/freaccess/getImage.php?devUUID=$devUUID&userUUID=$userUUID&cliID=$cliUUID&atividade=consultaFotoPet&petId=$petId'; + print('img: $url'); final response = await FreAccessWSGlobal.updatePet.call( petID: petId, image: img, diff --git a/lib/shared/extensions/string_extensions.dart b/lib/shared/extensions/string_extensions.dart index dc0b62c6..8381f55b 100644 --- a/lib/shared/extensions/string_extensions.dart +++ b/lib/shared/extensions/string_extensions.dart @@ -1,10 +1,12 @@ +import 'dart:ui'; + extension StringNullableExtensions on String? { bool toBoolean() { if (this == null) return false; return this!.toLowerCase() == 'true'; } - bool isNullOrEmpty() { + bool get isNullOrEmpty { if (this == null) return true; if (this == '') return true; return false; @@ -18,3 +20,16 @@ extension StringExtensions on String { } extension StringExtension on String? {} + +extension HexColor on String { + Color toColor() { + final hexCode = replaceAll('#', ''); + final buffer = StringBuffer(); + if (hexCode.length == 6) { + buffer + .write('ff'); // Adiciona opacidade total caso não esteja especificada + } + buffer.write(hexCode); + return Color(int.parse(buffer.toString(), radix: 16)); + } +} diff --git a/lib/shared/mixins/index.dart b/lib/shared/mixins/index.dart index f41f8c59..974783fd 100644 --- a/lib/shared/mixins/index.dart +++ b/lib/shared/mixins/index.dart @@ -1,2 +1,3 @@ -export 'status_mixin.dart'; -export 'switcher_mixin.dart'; +export 'pegeable_mixin.dart'; +export 'status_mixin.dart'; +export 'switcher_mixin.dart'; diff --git a/lib/shared/mixins/pegeable_mixin.dart b/lib/shared/mixins/pegeable_mixin.dart index 1752244a..e54db8f5 100644 --- a/lib/shared/mixins/pegeable_mixin.dart +++ b/lib/shared/mixins/pegeable_mixin.dart @@ -7,30 +7,33 @@ extension PagedListViewExtension on PagedSliverList {} mixin Pageable on State { - Expanded buildPaginatedListView( + Expanded buildPaginatedListView( String noDataFound, - PagingController pg, - Widget Function(BuildContext, Y, int) itemBuilder) { + PagingController pg, + Widget Function(BuildContext) headerBuilder, + Widget Function(BuildContext, ItemType, int) bodyBuilder) { final theme = FlutterFlowTheme.of(context); - return Expanded( child: RefreshIndicator( backgroundColor: theme.primaryBackground, color: theme.primary, onRefresh: () async => pg.refresh(), - child: PagedListView( + child: PagedListView( pagingController: pg, - builderDelegate: PagedChildBuilderDelegate( + builderDelegate: PagedChildBuilderDelegate( animateTransitions: true, - itemBuilder: (context, item, index) => - itemBuilder(context, item, index), - // noMoreItemsIndicatorBuilder: , + itemBuilder: (context, item, int index) { + return Column(children: [ + if (index == 0) headerBuilder(context), + bodyBuilder(context, item, index), + ]); + }, newPageProgressIndicatorBuilder: (context) => buildLoadingIndicator(context), firstPageProgressIndicatorBuilder: (context) => buildLoadingIndicator(context), noItemsFoundIndicatorBuilder: (context) => - buildNoDataFound(context, noDataFound), + buildNoDataFound(context, noDataFound, headerBuilder), firstPageErrorIndicatorBuilder: (context) => const Placeholder(), newPageErrorIndicatorBuilder: (context) => const Placeholder(), ), @@ -89,23 +92,33 @@ mixin Pageable on State { showSnackbar(context, message, true); } - Widget buildNoDataFound(BuildContext context, String title) { + Widget buildNoDataFound( + BuildContext context, + String title, + Widget Function(BuildContext) headerBuilder, + ) { final headerFontSize = LimitedFontSizeUtil.getHeaderFontSize(context); // final bodyFontSize = LimitedFontSizeUtil.getBodyFontSize(context); - return Expanded( - child: Center( - child: Text( - title, - style: TextStyle( - fontFamily: 'Nunito', - fontSize: headerFontSize, + return Column( + children: [ + headerBuilder(context), + Expanded( + child: Center( + child: Text( + title, + style: TextStyle( + fontFamily: 'Nunito', + fontSize: headerFontSize, + ), + ), ), ), - ), + ], ); } Widget buildLoadingIndicator(BuildContext context) { + print('Loading'); return Container( padding: const EdgeInsets.symmetric(vertical: 15), child: Center( diff --git a/lib/shared/utils/validator_util.dart b/lib/shared/utils/validator_util.dart index 51abce74..a8eef8db 100644 --- a/lib/shared/utils/validator_util.dart +++ b/lib/shared/utils/validator_util.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:intl/intl.dart'; class ValidatorUtil { @@ -34,10 +36,16 @@ class ValidatorUtil { } static String toLocalDateTime(String format, String value) { - DateFormat dateFormat = DateFormat(format); - DateTime dateTime = dateFormat.parse(value); - - return DateFormat('dd/MM/yyyy HH:mm:ss').format(dateTime); + try { + if (value.isEmpty) return ''; + DateFormat dateFormat = DateFormat(format); + DateTime? dateTime = dateFormat.tryParse(value); + if (dateTime == null) return ''; + return dateFormat.format(dateTime); + } catch (e, s) { + log(e.toString(), stackTrace: s); + return ''; + } } static String formatDateTimePicker(String dateTime) { diff --git a/lib/shared/widgets/view/carousel_view.dart b/lib/shared/widgets/view/carousel_view.dart index fd215ce6..e00f3067 100644 --- a/lib/shared/widgets/view/carousel_view.dart +++ b/lib/shared/widgets/view/carousel_view.dart @@ -1,46 +1,28 @@ part of '../widgets.dart'; -class CategoryCarousel extends StatelessWidget { - final List categories; - final void Function(Category) onSelect; - final void Function(Category) onUnselect; +class CategoryCarousel extends StatelessWidget { + final List categories; + final void Function(T) filter; const CategoryCarousel({ super.key, required this.categories, - required this.onSelect, - required this.onUnselect, + required this.filter, }); @override Widget build(BuildContext context) { final backgroundTheme = FlutterFlowTheme.of(context).primaryBackground; - bool isSelected = false; - Category? current = null; - - return Container( + return SizedBox( height: 120, - decoration: BoxDecoration( - color: backgroundTheme, - borderRadius: BorderRadius.circular(10), - ), child: CarouselView( itemExtent: 100, - onTap: (index) { - if (isSelected && current == categories[index]) { - onUnselect(categories[index]); - isSelected = false; - current = null; - } else { - onSelect(categories[index]); - isSelected = true; - current = categories[index]; - } - }, + onTap: (index) => filter(categories[index]!), children: categories.map((category) { - return GestureDetector( - onTap: () {}, + category as Category?; + return ColoredBox( + color: backgroundTheme, child: Padding( padding: const EdgeInsets.all(8.0), child: Column( @@ -48,7 +30,7 @@ class CategoryCarousel extends StatelessWidget { Container( padding: const EdgeInsets.all(8.0), decoration: BoxDecoration( - color: category.color, + color: category!.color, shape: BoxShape.circle, ), child: Icon( diff --git a/lib/shared/widgets/view/search_view.dart b/lib/shared/widgets/view/search_view.dart index 37a6b20e..b22eb7fe 100644 --- a/lib/shared/widgets/view/search_view.dart +++ b/lib/shared/widgets/view/search_view.dart @@ -1,7 +1,12 @@ part of '../widgets.dart'; +typedef SearchKey = GlobalKey; + +typedef Query = X?; + /// ----------------------------------------------- /// [SearchView] +/// ----------------------------------------------- class SearchView extends StatefulComponent { const SearchView({super.key}); @@ -17,6 +22,10 @@ class _SearchViewState extends State { } } +/// ----------------------------------------------- +/// [LocalSearchView] +/// ----------------------------------------------- + class LocalSearchView extends SearchView { final List list; final Widget Function(T) itemBuilder; @@ -77,109 +86,104 @@ class LocalSearchViewState extends State> { ), ), Padding( - padding: const EdgeInsets.all(30.0), - child: TextField( - onChanged: filter, - controller: editingController, - cursorColor: Colors.black, - decoration: InputDecoration( - prefixIcon: Icon(Icons.search), - focusColor: Colors.black, - hoverColor: Colors.black, - fillColor: Colors.black, - iconColor: Colors.black, - contentPadding: - EdgeInsets.symmetric(vertical: 10.0, horizontal: 10.0), - border: OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(15.0)), - borderSide: - BorderSide(color: Colors.black), // Set border color here + 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, ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(15.0)), - borderSide: BorderSide( - color: Colors.black), // Set focused border color here + 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), + ), ), - ), - ), - ), + )), ], ); } } -class RemoteSearchListView extends SearchView { - final List list; - final String title; - final Widget Function(T) itemBuilder; - // final Future> Function(String) fetchItems; - final bool Function(T, String) filter; - final Widget header; - final List Function(String)? onSearch; +/// ----------------------------------------------- +/// [RemoteSearchView] +/// ----------------------------------------------- - RemoteSearchListView({ +class RemoteSearchView extends SearchView { + final Widget Function(BuildContext, T, int) bodyBuilder; + Widget Function(BuildContext) headerBuilder; + final PagingController pagingController; + final Future<(bool, List)> Function(int pageKey, Query query) + dataProvider; + + final void Function(Object, StackTrace) onFetchError; + + RemoteSearchView({ Key? key, // required this.fetchItems, - required this.title, - required this.list, - required this.itemBuilder, - required this.filter, - List Function(String)? onSearch, - Widget? header, - }) : header = header ?? const SizedBox.shrink(), - onSearch = onSearch ?? - ((String query) => - list.where((documents) => filter(documents, query)).toList()), - super(key: key); + required this.bodyBuilder, + required this.headerBuilder, + required this.pagingController, + required this.dataProvider, + required this.onFetchError, + }) : super(key: key); @override - _RemoteSearchViewState createState() => _RemoteSearchViewState(); + RemoteSearchViewState createState() => RemoteSearchViewState(); } -class _RemoteSearchViewState extends State> +class RemoteSearchViewState extends State> with Pageable { TextEditingController editingController = TextEditingController(); - late List filteredItems; bool isLoading = false; - final apiCall = FreAccessWSGlobal.getDocuments; - int count = 0; - final PagingController _pagingController = - PagingController(firstPageKey: 1); + Query query = Document.fromDesc(''); @override void initState() { - filteredItems = widget.list; - _pagingController.addPageRequestListener( - (dynamic pageKey) => fetchPage( - dataProvider: () async { - final newItems = await apiCall.call(pageKey.toString()); - if (newItems.jsonBody == null) return (false, null); - final List docs = - (newItems.jsonBody['value']['list'] as List?) ?? []; - _pagingController.nextPageKey = pageKey + 1; - - safeSetState(() { - count = newItems.jsonBody['value']['count'] ?? 0; - }); - return (docs.isNotEmpty, docs); - }, - onDataUnavailable: () { - setState(() {}); - showNoMoreDataSnackBar(context); - }, - onDataAvailable: (vehicles) { - setState(() {}); - _pagingController.appendLastPage(vehicles); - }, - onFetchError: (e, s) { - DialogUtil.errorDefault(context); - LogUtil.requestAPIFailed( - "proccessRequest.php", "", "Consulta de Veículo", e, s); - setState(() {}); - }, - ), + widget.pagingController.addPageRequestListener( + (page) => fetchPage( + dataProvider: () async => await widget.dataProvider(page, query), + onDataUnavailable: () => showNoMoreDataSnackBar(context), + onDataAvailable: (data) => + widget.pagingController.appendLastPage(data), + onFetchError: (e, s) => widget.onFetchError), ); - _pagingController.addStatusListener(_showError); + widget.pagingController.addStatusListener(_showError); super.initState(); } @@ -200,57 +204,122 @@ class _RemoteSearchViewState extends State> content: Text(message), action: SnackBarAction( label: retry, - onPressed: () => _pagingController.retryLastFailedRequest(), + onPressed: () => widget.pagingController.retryLastFailedRequest(), ), ), ); } } - void filterSearchResults(String query) async { - setState(() { - isLoading = true; - }); - // final results = await widget.fetchItems(query); - // setState(() { - // filteredItems = results; - // isLoading = false; - // }); + void filter(Query data) async { + if (data is Category) { + safeSetState(() => query = Category( + id: data.id, + color: data.color, + title: data.title, + )); + widget.pagingController.refresh(); + } else if (data is Document) { + log('filter: ${data.description}'); + + safeSetState(() => query = data); + widget.pagingController.refresh(); + } else { + safeSetState(() { + query = Document.fromDesc(''); + }); + widget.pagingController.refresh(); + } } @override Widget build(BuildContext context) { final noDataFound = FFLocalizations.of(context).getVariableText( - ptText: "Nenhum veículo encontrado!", - enText: "No vehicle found", + ptText: "Nenhum item encontrado!", + enText: "No item found", ); + final theme = FlutterFlowTheme.of(context); + final locale = FFLocalizations.of(context); + return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.max, children: [ + buildPaginatedListView( + noDataFound, + widget.pagingController, + widget.headerBuilder, + widget.bodyBuilder, + ), Padding( padding: const EdgeInsets.all(8.0), - child: TextField( - onChanged: (value) => filterSearchResults(value), + child: TextFormField( controller: editingController, + onChanged: (value) => EasyDebounce.debounce( + '_model.keyTextFieldTextController', + const Duration(milliseconds: 500), + () => filter(Document.fromDesc(value)), + ), + 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( - labelText: "Search", - hintText: "Search", - prefixIcon: Icon(Icons.search), - border: OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(25.0)), + 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), ), ), ), ), - widget.header, - buildPaginatedListView( - noDataFound, - _pagingController, - (BuildContext context, dynamic item, int index) => - widget.itemBuilder(item), - ), ], ); } diff --git a/lib/shared/widgets/viewer/viewer.dart b/lib/shared/widgets/viewer/viewer.dart index 2e5f01c9..21be7ccc 100644 --- a/lib/shared/widgets/viewer/viewer.dart +++ b/lib/shared/widgets/viewer/viewer.dart @@ -1,9 +1,12 @@ part of '../widgets.dart'; -typedef PDFViewerState = GlobalKey; +typedef PDFViewerKey = GlobalKey; -abstract class Viewer extends StatelessComponent { - const Viewer({super.key, required this.src}); +abstract interface class Viewer extends StatelessComponent { + const Viewer({ + super.key, + required this.src, + }); final String src; @override @@ -14,13 +17,24 @@ abstract class Viewer extends StatelessComponent { Widget buildViewer(BuildContext context); } -class FREViewerPDF extends Viewer { - const FREViewerPDF({required Key key, required this.url}) - : super(key: key as PDFViewerState, src: url); - final String url; +class FREViewerPDF extends StatelessComponent { + final String src; + final PDFViewerKey search; + + const FREViewerPDF({ + required this.search, + required this.src, + }); @override + Widget build(BuildContext context) { + return buildViewer(context); + } + Widget buildViewer(BuildContext context) { - return SfPdfViewer.network(src, key: key as PDFViewerState); + return SfPdfViewer.network( + src, + key: search, + ); } } diff --git a/lib/shared/widgets/widgets.dart b/lib/shared/widgets/widgets.dart index 1ba34d40..c930bc37 100644 --- a/lib/shared/widgets/widgets.dart +++ b/lib/shared/widgets/widgets.dart @@ -1,13 +1,12 @@ import 'dart:developer'; import 'package:auto_size_text/auto_size_text.dart'; +import 'package:easy_debounce/easy_debounce.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; -import 'package:hub/features/backend/api_requests/api_calls.dart'; import 'package:hub/features/documents/index.dart'; import 'package:hub/flutter_flow/index.dart'; import 'package:hub/shared/mixins/pegeable_mixin.dart'; -import 'package:hub/shared/utils/index.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index 455814dc..d6098524 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -36,7 +36,7 @@ dependencies: flutter_animate: ^4.5.2 # flutter_cache_manager: ^3.4.1 # flutter_plugin_android_lifecycle: ^2.0.23 - share_plus: ^10.0.0 + share_plus: ^10.1.4 # connectivity_plus: ^6.0.5 flutter_secure_storage: ^10.0.0-beta.2 flutter_secure_storage_linux: ^2.0.0