From 8573d722f5b13fa5b12517e564e5564bbc57b685 Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Thu, 6 Feb 2025 11:23:55 -0300 Subject: [PATCH 01/32] WIP --- assets/files/document.pdf | Bin 0 -> 35523 bytes hx | 0 integration_test/app_test.dart | 7 - integration_test/auth_test.dart | 7 - .../shared_components_atoms/toast.dart | 1 - ..._arrow_linked_locals_component_widget.dart | 1 + .../card_item_template_component_widget.dart | 1 - .../message_notification_widget.dart | 3 +- .../provisional_schedule_template_model.dart | 2 - ...iter_vistor_template_component_widget.dart | 6 - .../sign_up_template_component_widget.dart | 6 +- lib/core/meta/anotations.dart | 2 - .../backend/api_requests/api_calls.dart | 2 + .../backend/schema/structs/device_struct.dart | 2 - .../documents/document_item_component.dart | 154 ++++++++++++++ .../documents/document_manager_screen.dart | 92 ++++++++ .../documents/document_page_model.dart | 1 + .../documents/document_page_widget.dart | 70 ++++++ .../documents/document_viewer_screen.dart | 48 +++++ lib/features/documents/index.dart | 12 ++ .../pages/provisional_history_page.dart | 2 + .../widgets/access_filter_modal.dart | 1 + .../widgets/provisional_filter_modal.dart | 2 - .../home/presentation/pages/about_system.dart | 3 - .../presentation/widgets/drawer_widget.dart | 1 + .../locals_local_data_source.dart | 2 +- .../locals_remote_data_source.dart | 44 ++-- .../repositories/locals_repository_impl.dart | 1 + .../menu/presentation/mappers/menu_entry.dart | 10 + .../widgets/menu_item/menu_item_button.dart | 17 +- .../module/data/models/license_model.dart | 1 - .../module/data/models/module_model.dart | 1 - .../module/domain/entities/base_module.dart | 2 - .../module/domain/entities/license.dart | 10 +- .../notification/deep_link_service.dart | 1 - .../profile_local_data_source.dart | 1 - .../storage/enums/shared_storage_key.dart | 2 - .../repositories/storage_repository_impl.dart | 1 - lib/flutter_flow/flutter_flow_widgets.dart | 1 - lib/flutter_flow/nav/nav.dart | 8 + lib/flutter_flow/nav/serialization_util.dart | 3 - lib/initialization.dart | 24 +-- lib/main.dart | 12 +- .../qr_code_page/qr_code_page_widget.dart | 3 +- .../reception_page/reception_page_widget.dart | 7 +- .../schedule_complete_visit_page_widget.dart | 1 + .../visits_on_the_property_screen.dart | 4 +- lib/shared/widgets/component.dart | 22 ++ lib/shared/widgets/entity.dart | 3 + lib/shared/widgets/model.dart | 11 + lib/shared/widgets/page.dart | 64 ++++++ lib/shared/widgets/screen.dart | 19 ++ lib/shared/widgets/text.dart | 12 ++ lib/shared/widgets/view/carousel_view.dart | 77 +++++++ lib/shared/widgets/view/search_view.dart | 200 ++++++++++++++++++ lib/shared/widgets/viewer/viewer.dart | 26 +++ lib/shared/widgets/widgets.dart | 21 ++ pubspec.yaml | 10 +- 58 files changed, 935 insertions(+), 112 deletions(-) create mode 100644 assets/files/document.pdf create mode 100644 hx create mode 100644 lib/features/documents/document_item_component.dart create mode 100644 lib/features/documents/document_manager_screen.dart create mode 100644 lib/features/documents/document_page_model.dart create mode 100644 lib/features/documents/document_page_widget.dart create mode 100644 lib/features/documents/document_viewer_screen.dart create mode 100644 lib/features/documents/index.dart create mode 100644 lib/shared/widgets/component.dart create mode 100644 lib/shared/widgets/entity.dart create mode 100644 lib/shared/widgets/model.dart create mode 100644 lib/shared/widgets/page.dart create mode 100644 lib/shared/widgets/screen.dart create mode 100644 lib/shared/widgets/text.dart create mode 100644 lib/shared/widgets/view/carousel_view.dart create mode 100644 lib/shared/widgets/view/search_view.dart create mode 100644 lib/shared/widgets/viewer/viewer.dart create mode 100644 lib/shared/widgets/widgets.dart diff --git a/assets/files/document.pdf b/assets/files/document.pdf new file mode 100644 index 0000000000000000000000000000000000000000..9a84c2fc28b6e3e4d6d04277044d9f10eee8d5af GIT binary patch literal 35523 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 literal 0 HcmV?d00001 diff --git a/hx b/hx new file mode 100644 index 00000000..e69de29b diff --git a/integration_test/app_test.dart b/integration_test/app_test.dart index 6267453a..c2b4bbb9 100644 --- a/integration_test/app_test.dart +++ b/integration_test/app_test.dart @@ -1,5 +1,4 @@ import 'dart:collection'; -import 'dart:math'; import 'package:app_tracking_transparency/app_tracking_transparency.dart'; import 'package:firebase_core/firebase_core.dart'; @@ -7,12 +6,9 @@ import 'package:firebase_crashlytics/firebase_crashlytics.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:go_router/go_router.dart'; import 'package:hub/components/molecular_components/throw_exception/throw_exception_widget.dart'; import 'package:hub/components/organism_components/bottom_arrow_linked_locals_component/bottom_arrow_linked_locals_component_widget.dart'; import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart'; -import 'package:hub/components/templates_components/forgot_password_template_component/forgot_password_template_component_widget.dart'; import 'package:hub/features/backend/api_requests/index.dart'; import 'package:hub/features/local/index.dart'; import 'package:hub/features/menu/index.dart'; @@ -22,12 +18,9 @@ import 'package:hub/features/notification/index.dart'; import 'package:hub/features/storage/index.dart'; import 'package:hub/flutter_flow/index.dart' as ff; import 'package:hub/flutter_flow/index.dart'; -import 'package:hub/initialization.dart'; import 'package:hub/main.dart'; -import 'package:hub/pages/forgot_password_page/forgot_password_screen.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:flutter_web_plugins/url_strategy.dart'; -import 'package:patrol/patrol.dart'; import 'app_test.dart'; import 'fuzzer/fuzzer.dart'; diff --git a/integration_test/auth_test.dart b/integration_test/auth_test.dart index 4045cfc3..a60a5b1f 100644 --- a/integration_test/auth_test.dart +++ b/integration_test/auth_test.dart @@ -255,10 +255,3 @@ Future _submit( _navigateBackUsingSystemGesture(); } } - -String _generateRandomString(int length) { - const chars = 'abcdefghijklmnopqrstuvwxyz0123456789'; - final rand = Random(); - return List.generate(length, (index) => chars[rand.nextInt(chars.length)]) - .join(); -} diff --git a/lib/components/atomic_components/shared_components_atoms/toast.dart b/lib/components/atomic_components/shared_components_atoms/toast.dart index 1be2a15e..30cc5332 100644 --- a/lib/components/atomic_components/shared_components_atoms/toast.dart +++ b/lib/components/atomic_components/shared_components_atoms/toast.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; -import 'package:hub/shared/utils/limited_text_size.dart'; class ToastUtil { static void showToast({ diff --git a/lib/components/organism_components/bottom_arrow_linked_locals_component/bottom_arrow_linked_locals_component_widget.dart b/lib/components/organism_components/bottom_arrow_linked_locals_component/bottom_arrow_linked_locals_component_widget.dart index b832653e..00f5922e 100644 --- a/lib/components/organism_components/bottom_arrow_linked_locals_component/bottom_arrow_linked_locals_component_widget.dart +++ b/lib/components/organism_components/bottom_arrow_linked_locals_component/bottom_arrow_linked_locals_component_widget.dart @@ -14,6 +14,7 @@ import 'package:hub/shared/utils/log_util.dart'; import '/flutter_flow/flutter_flow_theme.dart'; +// ignore: must_be_immutable class BottomArrowLinkedLocalsComponentWidget extends StatefulWidget { BottomArrowLinkedLocalsComponentWidget({super.key, required this.response}); ApiCallResponse? response; 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 9c96f384..a5527a22 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 @@ -4,7 +4,6 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:hub/shared/utils/limited_text_size.dart'; -import 'package:material_symbols_icons/symbols.dart'; import '/flutter_flow/flutter_flow_theme.dart'; import '/flutter_flow/flutter_flow_util.dart'; diff --git a/lib/components/templates_components/message_notificaion_modal_template_component/message_notification_widget.dart b/lib/components/templates_components/message_notificaion_modal_template_component/message_notification_widget.dart index 1039a738..733f058e 100644 --- a/lib/components/templates_components/message_notificaion_modal_template_component/message_notification_widget.dart +++ b/lib/components/templates_components/message_notificaion_modal_template_component/message_notification_widget.dart @@ -60,8 +60,7 @@ class _MessageNotificationModalTemplateComponentWidgetState @override Widget build(BuildContext context) { double limitedBodyFontSize = LimitedFontSizeUtil.getBodyFontSize(context); - double limitedSubHeaderFontSize = - LimitedFontSizeUtil.getSubHeaderFontSize(context); + LimitedFontSizeUtil.getSubHeaderFontSize(context); return Container( width: MediaQuery.sizeOf(context).width, diff --git a/lib/components/templates_components/provisional_schedule_template/provisional_schedule_template_model.dart b/lib/components/templates_components/provisional_schedule_template/provisional_schedule_template_model.dart index c1e82767..2985cbba 100644 --- a/lib/components/templates_components/provisional_schedule_template/provisional_schedule_template_model.dart +++ b/lib/components/templates_components/provisional_schedule_template/provisional_schedule_template_model.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:flutter/material.dart'; import 'package:hub/components/templates_components/provisional_schedule_template/provisional_shcedule_template_widget.dart'; import 'package:hub/features/backend/index.dart'; diff --git a/lib/components/templates_components/regisiter_vistor_template_component/regisiter_vistor_template_component_widget.dart b/lib/components/templates_components/regisiter_vistor_template_component/regisiter_vistor_template_component_widget.dart index 4a9c5ebe..e17b808b 100644 --- a/lib/components/templates_components/regisiter_vistor_template_component/regisiter_vistor_template_component_widget.dart +++ b/lib/components/templates_components/regisiter_vistor_template_component/regisiter_vistor_template_component_widget.dart @@ -15,7 +15,6 @@ import '/flutter_flow/flutter_flow_theme.dart'; import '/flutter_flow/flutter_flow_util.dart'; import '/flutter_flow/flutter_flow_widgets.dart'; import '/flutter_flow/form_field_controller.dart'; -import '/flutter_flow/upload_data.dart'; import 'regisiter_vistor_template_component_model.dart'; export 'regisiter_vistor_template_component_model.dart'; @@ -35,7 +34,6 @@ class RegisiterVistorTemplateComponentWidget extends StatefulWidget { class _RegisiterVistorTemplateComponentWidgetState extends State { late RegisiterVistorTemplateComponentModel _model; - bool _isLoading = false; final scaffoldKey = GlobalKey(); bool _isVisitorRegistered = false; @@ -43,10 +41,6 @@ class _RegisiterVistorTemplateComponentWidgetState final _formKey = GlobalKey(); - void _resetForm() { - _formKey.currentState?.reset(); - } - @override void initState() { super.initState(); diff --git a/lib/components/templates_components/sign_up_template_component/sign_up_template_component_widget.dart b/lib/components/templates_components/sign_up_template_component/sign_up_template_component_widget.dart index 3ad2c626..be56144e 100644 --- a/lib/components/templates_components/sign_up_template_component/sign_up_template_component_widget.dart +++ b/lib/components/templates_components/sign_up_template_component/sign_up_template_component_widget.dart @@ -107,9 +107,9 @@ class _SignUpTemplateComponentWidgetState @override Widget build(BuildContext context) { - final MediaQueryData mediaQuery = MediaQuery.of(context); - final double screenWidth = mediaQuery.size.width; - final double screenHeight = mediaQuery.size.height; + // final MediaQueryData mediaQuery = MediaQuery.of(context); + // final double screenWidth = mediaQuery.size.width; + // final double screenHeight = mediaQuery.size.height; double limitedHeaderFontSize = LimitedFontSizeUtil.getHeaderFontSize(context); diff --git a/lib/core/meta/anotations.dart b/lib/core/meta/anotations.dart index af721eaa..842e5064 100644 --- a/lib/core/meta/anotations.dart +++ b/lib/core/meta/anotations.dart @@ -1,5 +1,3 @@ -import 'package:meta/meta.dart'; - class DeadCode { final String? desc; diff --git a/lib/features/backend/api_requests/api_calls.dart b/lib/features/backend/api_requests/api_calls.dart index 72d1a8a2..e4bdd4fc 100644 --- a/lib/features/backend/api_requests/api_calls.dart +++ b/lib/features/backend/api_requests/api_calls.dart @@ -1,3 +1,5 @@ +// ignore_for_file: unused_element + import 'dart:async'; import 'dart:convert'; import 'dart:developer'; diff --git a/lib/features/backend/schema/structs/device_struct.dart b/lib/features/backend/schema/structs/device_struct.dart index db8a8896..0bd580ec 100644 --- a/lib/features/backend/schema/structs/device_struct.dart +++ b/lib/features/backend/schema/structs/device_struct.dart @@ -3,8 +3,6 @@ import 'package:hub/features/backend/index.dart'; import 'package:hub/flutter_flow/nav/nav.dart'; -import 'index.dart'; - class DeviceStruct extends BaseStruct { DeviceStruct({ String? devUUID, diff --git a/lib/features/documents/document_item_component.dart b/lib/features/documents/document_item_component.dart new file mode 100644 index 00000000..3c0ff117 --- /dev/null +++ b/lib/features/documents/document_item_component.dart @@ -0,0 +1,154 @@ +part of 'index.dart'; + +interface class Category extends Entity { + final Color color; + final String title; + + Category({ + required this.color, + required this.title, + }); +} + +interface class Document extends Entity { + final String title; + final String description; + final Category category; + final String to; + final String from; + final String createdAt; + final String updatedAt; + + Document({ + required this.createdAt, + required this.updatedAt, + required this.category, + required this.to, + required this.from, + required this.title, + required this.description, + }); +} + +class DocumentItem extends StatelessWidget { + final Document document; + + const DocumentItem({Key? key, required this.document}) : super(key: key); + + Tooltip _buildTooltip(String text, Color color, BuildContext context, + BoxConstraints constraints) { + final Color textColor = FlutterFlowTheme.of(context).info; + + final double boxHeight = MediaQuery.of(context).size.height * 0.02; + final double boxWidth = MediaQuery.of(context).size.height * 0.1; + + return Tooltip( + message: text, + child: Container( + width: boxWidth, + height: boxHeight, + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(10), + ), + child: Center( + child: AutoText( + text, + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: textColor, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + final Color primaryText = FlutterFlowTheme.of(context).primaryText; + final Color primaryColor = FlutterFlowTheme.of(context).primary; + + final TextStyle textStyleMajor = TextStyle( + color: primaryText, + fontWeight: FontWeight.bold, + ); + final TextStyle textStyleMinor = TextStyle( + color: primaryText, + fontWeight: FontWeight.normal, + fontStyle: FontStyle.italic, + ); + + return Padding( + padding: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (context, constraints) { + final double boxHeight = constraints.maxHeight > 350 + ? MediaQuery.of(context).size.height * 0.07 + : MediaQuery.of(context).size.height * 2; + + return InkWell( + onTap: () => print('Click'), + enableFeedback: true, + overlayColor: MaterialStateProperty.all(primaryColor), + borderRadius: BorderRadius.circular(10), + child: SizedBox( + height: boxHeight, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // const SizedBox(width: 10), + Icon(Icons.description, color: document.category.color), + // const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Tooltip( + message: document.title, + child: AutoText( + document.title, + style: textStyleMajor, + overflow: TextOverflow.ellipsis, + ), + ), + AutoText( + document.updatedAt, + style: textStyleMinor, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + _buildTooltip( + document.category.title, + document.category.color, + context, + constraints, + ), + ], + ), + ), + // const SizedBox(width: 10), + Center( + child: Icon( + Icons.arrow_right, + color: primaryText, + ), + ), + ], + ), + ), + ); + }, + ), + ); + } +} diff --git a/lib/features/documents/document_manager_screen.dart b/lib/features/documents/document_manager_screen.dart new file mode 100644 index 00000000..407a5427 --- /dev/null +++ b/lib/features/documents/document_manager_screen.dart @@ -0,0 +1,92 @@ +part of 'index.dart'; + +class DocumentManagerScreen extends StatelessScreen { + DocumentManagerScreen({ + super.key, + required this.documents, + required this.categories, + }); + + List documents; + final List categories; + + @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(document) => DocumentItem(document: document); + + 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: LocalSearchView( + key: _listViewKey, + header: header, + onSearch: filterByString, + list: documents, + itemBuilder: itemBuilder, + filter: filter, + ), + ), + ] // + .addToStart(space) + .addToEnd(space), + ); + } +} diff --git a/lib/features/documents/document_page_model.dart b/lib/features/documents/document_page_model.dart new file mode 100644 index 00000000..9ec58ac2 --- /dev/null +++ b/lib/features/documents/document_page_model.dart @@ -0,0 +1 @@ +part of 'index.dart'; diff --git a/lib/features/documents/document_page_widget.dart b/lib/features/documents/document_page_widget.dart new file mode 100644 index 00000000..99dc2849 --- /dev/null +++ b/lib/features/documents/document_page_widget.dart @@ -0,0 +1,70 @@ +part of 'index.dart'; + +List generateDocuments(int count) { + String str() => randomString(8, 8, true, true, true); + Color color() => randomColor(); + + return List.generate( + count, + (index) => Document( + title: 'Lorem Ipsum et Cetera $index', + description: 'Description for document $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}); + + @override + State createState() => _FREDocumentPageState(); +} + +class _FREDocumentPageState extends PageState { + @override + Widget build(BuildContext context) { + final String title = FFLocalizations.of(context) + .getVariableText(enText: 'Documents', ptText: 'Documentos'); + + return Scaffold( + 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 DocumentViewScreen(document: documents.first); + } +} diff --git a/lib/features/documents/document_viewer_screen.dart b/lib/features/documents/document_viewer_screen.dart new file mode 100644 index 00000000..9c4a0c63 --- /dev/null +++ b/lib/features/documents/document_viewer_screen.dart @@ -0,0 +1,48 @@ +part of 'index.dart'; + +class DocumentViewScreen extends StatefulScreen { + const DocumentViewScreen({ + super.key, + required this.document, + }); + + final Document document; + + @override + State createState() => _DocumentViewScreenState(); +} + +class _DocumentViewScreenState extends State { + final PDFViewerState _viewerKey = PDFViewerState(); + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Padding( + padding: EdgeInsets.all(10), + child: FREViewerPDF( + key: _viewerKey, + url: + 'https://cdn.syncfusion.com/content/PDFViewer/flutter-succinctly.pdf', + ), + ), + Positioned( + bottom: 10, + right: 10, + child: IconButton( + icon: Icon(Icons.share, color: Colors.black), + color: Colors.black, + onPressed: () { + _viewerKey.currentState?.openBookmarkView(); + // Share.share(FFLocalizations.of(context).getVariableText( + // ptText: '', + // enText: '', + // )); + }, + ), + ), + ], + ); + } +} diff --git a/lib/features/documents/index.dart b/lib/features/documents/index.dart new file mode 100644 index 00000000..c32958c7 --- /dev/null +++ b/lib/features/documents/index.dart @@ -0,0 +1,12 @@ +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:hub/flutter_flow/index.dart'; +import 'package:hub/shared/utils/index.dart'; +import 'package:hub/shared/widgets/widgets.dart'; + +part 'document_manager_screen.dart'; +part 'document_page_widget.dart'; +part 'document_viewer_screen.dart'; +part 'document_page_model.dart'; +part 'document_item_component.dart'; diff --git a/lib/features/history/presentation/pages/provisional_history_page.dart b/lib/features/history/presentation/pages/provisional_history_page.dart index e4bfde25..ae43ad8d 100644 --- a/lib/features/history/presentation/pages/provisional_history_page.dart +++ b/lib/features/history/presentation/pages/provisional_history_page.dart @@ -350,6 +350,7 @@ class ProvisionalHistoryState extends State { ); } + // ignore: unused_element String _imageUrlAtomWidget(String document, String type) { return valueOrDefault( "https://freaccess.com.br/freaccess/getImage.php?&cliID=&atividade=getFoto&Documento=$document&tipo=$type", @@ -404,6 +405,7 @@ class ProvisionalHistoryState extends State { } Map _getStatusMap(BuildContext context, dynamic json) { + // ignore: unused_local_variable late Map statusColorMap; log(DateTime.parse(json['AGP_DT_VISITA']).toString()); log(DateTime.now().toString()); diff --git a/lib/features/history/presentation/widgets/access_filter_modal.dart b/lib/features/history/presentation/widgets/access_filter_modal.dart index 1346fe8b..7ce32e33 100644 --- a/lib/features/history/presentation/widgets/access_filter_modal.dart +++ b/lib/features/history/presentation/widgets/access_filter_modal.dart @@ -187,6 +187,7 @@ class _AccessFilterState extends State { ); } + // ignore: unused_element void _updateSelection(String? value, String key) { setState(() { if (value == '.') { diff --git a/lib/features/history/presentation/widgets/provisional_filter_modal.dart b/lib/features/history/presentation/widgets/provisional_filter_modal.dart index 44d0f538..5928e05d 100644 --- a/lib/features/history/presentation/widgets/provisional_filter_modal.dart +++ b/lib/features/history/presentation/widgets/provisional_filter_modal.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:flutter/material.dart'; import '/flutter_flow/flutter_flow_util.dart'; diff --git a/lib/features/home/presentation/pages/about_system.dart b/lib/features/home/presentation/pages/about_system.dart index 7255388f..19b9de55 100644 --- a/lib/features/home/presentation/pages/about_system.dart +++ b/lib/features/home/presentation/pages/about_system.dart @@ -1,6 +1,5 @@ import 'dart:developer'; -import 'package:firebase_crashlytics/firebase_crashlytics.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; @@ -8,8 +7,6 @@ import 'package:hub/flutter_flow/flutter_flow_icon_button.dart'; import 'package:hub/flutter_flow/flutter_flow_theme.dart'; import 'package:hub/flutter_flow/flutter_flow_util.dart'; import 'package:hub/flutter_flow/internationalization.dart'; -import 'package:hub/shared/utils/log_util.dart'; -import 'package:stack_trace/stack_trace.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:url_launcher/url_launcher.dart'; diff --git a/lib/features/home/presentation/widgets/drawer_widget.dart b/lib/features/home/presentation/widgets/drawer_widget.dart index 014fa9f8..d71d58b9 100644 --- a/lib/features/home/presentation/widgets/drawer_widget.dart +++ b/lib/features/home/presentation/widgets/drawer_widget.dart @@ -136,6 +136,7 @@ class DrawerWidget extends StatelessWidget { ); } + // ignore: unused_element Padding _buildSearchBar(BuildContext context, HomeState state) { final theme = FlutterFlowTheme.of(context); final errorColor = theme.error; diff --git a/lib/features/local/data/data_sources/locals_local_data_source.dart b/lib/features/local/data/data_sources/locals_local_data_source.dart index e259f2c3..c63aec38 100644 --- a/lib/features/local/data/data_sources/locals_local_data_source.dart +++ b/lib/features/local/data/data_sources/locals_local_data_source.dart @@ -58,7 +58,7 @@ class LocalsLocalDataSourceImpl implements LocalsLocalDataSource { }, conflictAlgorithm: ConflictAlgorithm.replace, ); - } catch (e, s) {} + } catch (e) {} } @override diff --git a/lib/features/local/data/data_sources/locals_remote_data_source.dart b/lib/features/local/data/data_sources/locals_remote_data_source.dart index a40883cf..fbe41ff1 100644 --- a/lib/features/local/data/data_sources/locals_remote_data_source.dart +++ b/lib/features/local/data/data_sources/locals_remote_data_source.dart @@ -41,10 +41,10 @@ class LocalsRemoteDataSourceImpl implements LocalsRemoteDataSource { return; } if (response.jsonBody == null) { - final String errorMsg = FFLocalizations.of(context).getVariableText( - enText: 'Verify your connection', - ptText: 'Verifique sua conexão', - ); + // final String errorMsg = FFLocalizations.of(context).getVariableText( + // enText: 'Verify your connection', + // ptText: 'Verifique sua conexão', + // ); // await DialogUtil.error(context, errorMsg).whenComplete(() async => await selectLocal(context, response)); return; } @@ -82,10 +82,10 @@ class LocalsRemoteDataSourceImpl implements LocalsRemoteDataSource { return false; } if (response.jsonBody == null) { - final String errorMsg = FFLocalizations.of(context).getVariableText( - enText: 'Verify your connection', - ptText: 'Verifique sua conexão', - ); + // final String errorMsg = FFLocalizations.of(context).getVariableText( + // enText: 'Verify your connection', + // ptText: 'Verifique sua conexão', + // ); // await DialogUtil.error(context, errorMsg).whenComplete(() async => await selectLocal(context, response)); return false; } @@ -140,10 +140,10 @@ class LocalsRemoteDataSourceImpl implements LocalsRemoteDataSource { log('() => stack: $s'); log('() => catch: $e', stackTrace: s); // return await selectLocal(context); - final String errorMsg = FFLocalizations.of(context).getVariableText( - enText: 'Error getting locals, verify your connection', - ptText: 'Erro ao obter locais, verifique sua conexão', - ); + // final String errorMsg = FFLocalizations.of(context).getVariableText( + // enText: 'Error getting locals, verify your connection', + // ptText: 'Erro ao obter locais, verifique sua conexão', + // ); // await DialogUtil.error(context, errorMsg).whenComplete(() async => await selectLocal(context, null)); return false; } @@ -168,10 +168,10 @@ class LocalsRemoteDataSourceImpl implements LocalsRemoteDataSource { if (response.jsonBody == null) { final GetLocalsCall callback = PhpGroup.getLocalsCall; response = await callback.call(); - final String errorMsg = FFLocalizations.of(context).getVariableText( - enText: 'Verify your connection', - ptText: 'Verifique sua conexão', - ); + // final String errorMsg = FFLocalizations.of(context).getVariableText( + // enText: 'Verify your connection', + // ptText: 'Verifique sua conexão', + // ); // await DialogUtil.error(context, errorMsg).whenComplete(() async => await selectLocal(context, response)); return false; } @@ -180,8 +180,8 @@ class LocalsRemoteDataSourceImpl implements LocalsRemoteDataSource { if (isError == true) { final GetLocalsCall callback = PhpGroup.getLocalsCall; response = await callback.call(); - final String errorMsg = - response.jsonBody['error_msg'] ?? 'Local indisponível'; + // final String errorMsg = + // response.jsonBody['error_msg'] ?? 'Local indisponível'; // await DialogUtil.error(context, errorMsg).whenComplete(() async => await selectLocal(context, response)); return false; } else { @@ -190,10 +190,10 @@ class LocalsRemoteDataSourceImpl implements LocalsRemoteDataSource { } } catch (e, s) { log('() => error processData: $e', stackTrace: s); - final String errorMsg = FFLocalizations.of(context).getVariableText( - enText: 'Error getting data, verify your connection', - ptText: 'Erro ao obter dados, verifique sua conexão', - ); + // final String errorMsg = FFLocalizations.of(context).getVariableText( + // enText: 'Error getting data, verify your connection', + // ptText: 'Erro ao obter dados, verifique sua conexão', + // ); // await DialogUtil.error(context, errorMsg).whenComplete(() async => await selectLocal(context, null)); return false; } diff --git a/lib/features/local/data/repositories/locals_repository_impl.dart b/lib/features/local/data/repositories/locals_repository_impl.dart index d171256e..a1cf5fdc 100644 --- a/lib/features/local/data/repositories/locals_repository_impl.dart +++ b/lib/features/local/data/repositories/locals_repository_impl.dart @@ -107,6 +107,7 @@ class LocalsRepositoryImpl implements LocalsRepository { await DialogUtil.error(context, errorMsg); } + // ignore: unused_element Future _notifyBlocs(BuildContext context) async { context.read().add(LocalProfileEvent()); context.read().add(MenuEvent()); diff --git a/lib/features/menu/presentation/mappers/menu_entry.dart b/lib/features/menu/presentation/mappers/menu_entry.dart index 7e1a4b00..f290b98c 100644 --- a/lib/features/menu/presentation/mappers/menu_entry.dart +++ b/lib/features/menu/presentation/mappers/menu_entry.dart @@ -160,6 +160,16 @@ class MenuEntry implements BaseModule { route: '/acessHistoryPage', types: [MenuEntryType.Home, MenuEntryType.Drawer], ), + MenuEntry( + key: 'FRE-HUB-DOCUMENT', + icon: Icons.document_scanner, + name: FFLocalizations.of(navigatorKey.currentContext!).getVariableText( + ptText: 'Documentos', + enText: 'Documents', + ), + route: '/documentPage', + types: [MenuEntryType.Home, MenuEntryType.Drawer], + ), MenuEntry( key: 'FRE-HUB-LIBERATIONS', icon: Icons.how_to_reg_outlined, diff --git a/lib/features/menu/presentation/widgets/menu_item/menu_item_button.dart b/lib/features/menu/presentation/widgets/menu_item/menu_item_button.dart index c8f81e31..d5142c21 100644 --- a/lib/features/menu/presentation/widgets/menu_item/menu_item_button.dart +++ b/lib/features/menu/presentation/widgets/menu_item/menu_item_button.dart @@ -76,16 +76,15 @@ class _MenuButtonWidgetState extends State { mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (widget.icon != null) - Container( - alignment: Alignment.topLeft, - padding: const EdgeInsets.only(left: 8.0), - child: Icon( - widget.icon, - size: 24.0, - color: FlutterFlowTheme.of(context).primaryText, - ), + Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.only(left: 8.0), + child: Icon( + widget.icon, + size: 24.0, + color: FlutterFlowTheme.of(context).primaryText, ), + ), Flexible( child: Padding( padding: const EdgeInsets.only(left: 10.0), diff --git a/lib/features/module/data/models/license_model.dart b/lib/features/module/data/models/license_model.dart index 48e215cc..df9e8f87 100644 --- a/lib/features/module/data/models/license_model.dart +++ b/lib/features/module/data/models/license_model.dart @@ -1,4 +1,3 @@ -import 'package:flutter/material.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'module_model.dart'; diff --git a/lib/features/module/data/models/module_model.dart b/lib/features/module/data/models/module_model.dart index e373edc3..17a2f23b 100644 --- a/lib/features/module/data/models/module_model.dart +++ b/lib/features/module/data/models/module_model.dart @@ -1,4 +1,3 @@ -import 'package:flutter/material.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'module_model.freezed.dart'; diff --git a/lib/features/module/domain/entities/base_module.dart b/lib/features/module/domain/entities/base_module.dart index 1cfbeef0..9e8759aa 100644 --- a/lib/features/module/domain/entities/base_module.dart +++ b/lib/features/module/domain/entities/base_module.dart @@ -1,6 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:hub/flutter_flow/internationalization.dart'; -import 'package:hub/flutter_flow/nav/nav.dart'; abstract class BaseModule { String get key; diff --git a/lib/features/module/domain/entities/license.dart b/lib/features/module/domain/entities/license.dart index 17a70650..b5b73407 100644 --- a/lib/features/module/domain/entities/license.dart +++ b/lib/features/module/domain/entities/license.dart @@ -25,7 +25,8 @@ enum LicenseKeys { people('FRE-HUB-PEOPLE'), petsHistory('FRE-HUB-PETS-HISTORY'), settings('FRE-HUB-SETTINGS'), - logout('FRE-HUB-LOGOUT'); + logout('FRE-HUB-LOGOUT'), + document('FRE-HUB-DOCUMENT'); final String value; const LicenseKeys(this.value); @@ -137,6 +138,13 @@ class License { startDate: '', quantity: 0, ), + Module( + key: LicenseKeys.document.value, + display: ModuleStatus.active.key, + expirationDate: '', + startDate: '', + quantity: 0, + ), Module( key: LicenseKeys.openedVisits.value, display: isNewVersionWithModule diff --git a/lib/features/notification/deep_link_service.dart b/lib/features/notification/deep_link_service.dart index 4f6ba58d..77792519 100644 --- a/lib/features/notification/deep_link_service.dart +++ b/lib/features/notification/deep_link_service.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:developer'; import 'package:app_links/app_links.dart'; import 'package:flutter/material.dart'; import 'package:hub/features/storage/index.dart'; diff --git a/lib/features/profile/data/data_sources/profile_local_data_source.dart b/lib/features/profile/data/data_sources/profile_local_data_source.dart index 8b0f9d1e..ee9bb176 100644 --- a/lib/features/profile/data/data_sources/profile_local_data_source.dart +++ b/lib/features/profile/data/data_sources/profile_local_data_source.dart @@ -2,7 +2,6 @@ import 'dart:developer'; import 'package:hub/features/storage/constants/profile_constants.dart'; import 'package:hub/features/storage/index.dart'; -import 'package:hub/shared/constants/index.dart'; import 'package:sqflite/sqflite.dart'; diff --git a/lib/features/storage/enums/shared_storage_key.dart b/lib/features/storage/enums/shared_storage_key.dart index 7cd1dc3e..2f615f4f 100644 --- a/lib/features/storage/enums/shared_storage_key.dart +++ b/lib/features/storage/enums/shared_storage_key.dart @@ -9,8 +9,6 @@ extension SharedPreferencesKeyExtension on SharedPreferencesKey { switch (this) { case SharedPreferencesKey.isFirstRun: return 'fre_isFirstRun'; - default: - return ''; } } } diff --git a/lib/features/storage/repositories/storage_repository_impl.dart b/lib/features/storage/repositories/storage_repository_impl.dart index 30f3fd0d..1649b05e 100644 --- a/lib/features/storage/repositories/storage_repository_impl.dart +++ b/lib/features/storage/repositories/storage_repository_impl.dart @@ -1,7 +1,6 @@ import 'dart:developer'; import 'package:flutter/cupertino.dart'; -import 'package:hub/features/profile/data/data_sources/profile_local_data_source.dart'; import 'package:hub/features/storage/index.dart'; class StorageHelper implements StorageRepository { diff --git a/lib/flutter_flow/flutter_flow_widgets.dart b/lib/flutter_flow/flutter_flow_widgets.dart index 6845d783..122432eb 100644 --- a/lib/flutter_flow/flutter_flow_widgets.dart +++ b/lib/flutter_flow/flutter_flow_widgets.dart @@ -1,6 +1,5 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:flutter/material.dart'; -import 'package:auto_size_text/auto_size_text.dart'; class FFButtonOptions { const FFButtonOptions({ diff --git a/lib/flutter_flow/nav/nav.dart b/lib/flutter_flow/nav/nav.dart index 600a43ad..0da33c1e 100644 --- a/lib/flutter_flow/nav/nav.dart +++ b/lib/flutter_flow/nav/nav.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:hub/features/backend/index.dart'; +import 'package:hub/features/documents/index.dart'; import 'package:hub/features/history/index.dart'; import 'package:hub/features/home/index.dart'; import 'package:hub/features/local/index.dart'; @@ -298,6 +299,13 @@ GoRouter createRouter(AppStateNotifier appStateNotifier) { return PetsPageWidget(pet: pet); }, ), + FFRoute( + name: 'documentPage', + path: '/documentPage', + builder: (context, params) { + return FREDocumentPage(); + }, + ), // FFRoute(name: 'settingsPage', path: '/settingsPage', builder: (context, params) => params.isEmpty ? const NavBarPage(initialPage: 'settingsPage') : const SettingsPageWidget()) ].map((r) => r.toRoute(appStateNotifier)).toList(), ); diff --git a/lib/flutter_flow/nav/serialization_util.dart b/lib/flutter_flow/nav/serialization_util.dart index a4d36fd0..94a8350b 100644 --- a/lib/flutter_flow/nav/serialization_util.dart +++ b/lib/flutter_flow/nav/serialization_util.dart @@ -218,9 +218,6 @@ dynamic deserializeParam( case ParamType.Enum: return deserializeEnum(param); - - default: - return null; } } catch (e) { return null; diff --git a/lib/initialization.dart b/lib/initialization.dart index 04aa29c6..29273fef 100644 --- a/lib/initialization.dart +++ b/lib/initialization.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:app_tracking_transparency/app_tracking_transparency.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_crashlytics/firebase_crashlytics.dart'; @@ -56,21 +54,21 @@ Future _initializeSystemSettings() async { await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); if (kDebugMode) { - //kDebugMode print('Debug mode'); - bool unsentReports = - await FirebaseCrashlytics.instance.checkForUnsentReports(); - if (unsentReports) { - // Existem relatórios não enviados - await crashlyticsInstance.sendUnsentReports(); - print('Existem relatórios de falhas não enviados.'); - } else { - // Não existem relatórios não enviados - print('Todos os relatórios de falhas foram enviados.'); - } } else { print('Release mode'); + // bool unsentReports = + // await FirebaseCrashlytics.instance.checkForUnsentReports(); + // if (unsentReports) { + // // Existem relatórios não enviados + // await crashlyticsInstance.sendUnsentReports(); + // print('Existem relatórios de falhas não enviados.'); + // } else { + // // Não existem relatórios não enviados + // print('Todos os relatórios de falhas foram enviados.'); + // } + await crashlyticsInstance.setCrashlyticsCollectionEnabled(true); // if (crashlyticsInstance.isCrashlyticsCollectionEnabled) { FlutterError.onError = await crashlyticsInstance.recordFlutterError; diff --git a/lib/main.dart b/lib/main.dart index 623ac049..34d1a25f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -6,6 +6,7 @@ import 'dart:io'; import 'package:firebase_crashlytics/firebase_crashlytics.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -14,13 +15,21 @@ import 'package:hub/features/storage/index.dart'; import 'package:hub/flutter_flow/flutter_flow_theme.dart'; import 'package:hub/flutter_flow/internationalization.dart'; import 'package:hub/flutter_flow/nav/nav.dart'; -import 'package:hub/shared/utils/test_util.dart'; import 'package:responsive_framework/responsive_framework.dart'; import 'initialization.dart'; final GlobalKey navigatorKey = GlobalKey(); +class CustomScrollBehavior extends MaterialScrollBehavior { + // Override behavior methods and getters like dragDevices + @override + Set get dragDevices => { + PointerDeviceKind.touch, + PointerDeviceKind.mouse, + }; +} + void main() async { await initializeApp(); runApp(const ProviderScope(child: App())); @@ -193,6 +202,7 @@ class _AppState extends State { @override Widget build(BuildContext context) { return MaterialApp.router( + scrollBehavior: CustomScrollBehavior(), key: navigatorKey, title: 'FRE ACCESS HUB', builder: builder, diff --git a/lib/pages/qr_code_page/qr_code_page_widget.dart b/lib/pages/qr_code_page/qr_code_page_widget.dart index f7832214..bab42743 100644 --- a/lib/pages/qr_code_page/qr_code_page_widget.dart +++ b/lib/pages/qr_code_page/qr_code_page_widget.dart @@ -103,8 +103,7 @@ class _QrCodePageWidgetState extends State screenWidth < screenHeight ? screenWidth : screenHeight; double dimension = smallerDimension * 0.75; double totalTimeInSeconds = 100.0; - double limitedHeaderTextSize = - LimitedFontSizeUtil.getCalculateFontSize(context, 18, 18, 16); + LimitedFontSizeUtil.getCalculateFontSize(context, 18, 18, 16); double limitedBodyFontSize = LimitedFontSizeUtil.getBodyFontSize(context); return SafeArea( diff --git a/lib/pages/reception_page/reception_page_widget.dart b/lib/pages/reception_page/reception_page_widget.dart index e7d5718e..ddd1854a 100644 --- a/lib/pages/reception_page/reception_page_widget.dart +++ b/lib/pages/reception_page/reception_page_widget.dart @@ -1,6 +1,3 @@ -import 'dart:developer'; - -import 'package:awesome_notifications/awesome_notifications.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; @@ -31,9 +28,7 @@ class _ReceptionPageWidgetState extends State void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); - () async { - final lifecycle = await AwesomeNotifications().getAppLifeCycle(); - }(); + () async {}(); FirebaseMessagingService().updateDeviceToken(); LocalsRepositoryImpl().validateLocal(context); diff --git a/lib/pages/schedule_complete_visit_page/schedule_complete_visit_page_widget.dart b/lib/pages/schedule_complete_visit_page/schedule_complete_visit_page_widget.dart index f5247241..ce4d41a3 100644 --- a/lib/pages/schedule_complete_visit_page/schedule_complete_visit_page_widget.dart +++ b/lib/pages/schedule_complete_visit_page/schedule_complete_visit_page_widget.dart @@ -90,6 +90,7 @@ class _ScheduleCompleteVisitPageWidgetState } } + // ignore: unused_element void _scrollListener() { if (_visitHistoryController.position.pixels == _visitHistoryController.position.maxScrollExtent) { diff --git a/lib/pages/visits_on_the_property/visits_on_the_property_screen.dart b/lib/pages/visits_on_the_property/visits_on_the_property_screen.dart index 345d8a93..65f9a039 100644 --- a/lib/pages/visits_on_the_property/visits_on_the_property_screen.dart +++ b/lib/pages/visits_on_the_property/visits_on_the_property_screen.dart @@ -54,8 +54,8 @@ class _VisitsOnThePropertyState extends State @override Widget build(BuildContext context) { - late final limitedBodyTextSize = - LimitedFontSizeUtil.getBodyFontSize(context); + // late final limitedBodyTextSize = + // LimitedFontSizeUtil.getBodyFontSize(context); late final limitedHeaderTextSize = LimitedFontSizeUtil.getHeaderFontSize(context); diff --git a/lib/shared/widgets/component.dart b/lib/shared/widgets/component.dart new file mode 100644 index 00000000..fb1383dc --- /dev/null +++ b/lib/shared/widgets/component.dart @@ -0,0 +1,22 @@ +part of 'widgets.dart'; + +/// [ComponentWidget] + +abstract class ComponentWidget extends Widget { + const ComponentWidget({super.key}); +} + +abstract class ModelComponent extends ModelWidget + implements ComponentWidget { + const ModelComponent({super.key}); +} + +abstract class StatelessComponent extends StatelessWidget + implements ComponentWidget { + const StatelessComponent({super.key}); +} + +abstract class StatefulComponent extends StatefulWidget + implements ComponentWidget { + const StatefulComponent({super.key}); +} diff --git a/lib/shared/widgets/entity.dart b/lib/shared/widgets/entity.dart new file mode 100644 index 00000000..ae05f63f --- /dev/null +++ b/lib/shared/widgets/entity.dart @@ -0,0 +1,3 @@ +part of 'widgets.dart'; + +abstract class Entity {} diff --git a/lib/shared/widgets/model.dart b/lib/shared/widgets/model.dart new file mode 100644 index 00000000..c35a6491 --- /dev/null +++ b/lib/shared/widgets/model.dart @@ -0,0 +1,11 @@ +part of 'widgets.dart'; + +class ModelWidget extends Widget { + const ModelWidget({super.key}); + + @override + Element createElement() { + // TODO: implement createElement + throw UnimplementedError(); + } +} diff --git a/lib/shared/widgets/page.dart b/lib/shared/widgets/page.dart new file mode 100644 index 00000000..ed08ad58 --- /dev/null +++ b/lib/shared/widgets/page.dart @@ -0,0 +1,64 @@ +part of 'widgets.dart'; + +mixin MixinPage { + PreferredSizeWidget buildAppBar(String title, BuildContext context) { + return AppBar( + backgroundColor: FlutterFlowTheme.of(context).primaryBackground, + automaticallyImplyLeading: false, + title: Text( + title, + style: FlutterFlowTheme.of(context).headlineMedium.override( + fontFamily: FlutterFlowTheme.of(context).headlineMediumFamily, + color: FlutterFlowTheme.of(context).primaryText, + fontSize: 16.0, + fontWeight: FontWeight.bold, + letterSpacing: 0.0, + useGoogleFonts: GoogleFonts.asMap().containsKey( + FlutterFlowTheme.of(context).headlineMediumFamily), + ), + ), + leading: _backButton(context, FlutterFlowTheme.of(context)), + centerTitle: true, + elevation: 0.0, + actions: [], + ); + } + + Widget _backButton(BuildContext context, FlutterFlowTheme theme) { + return FlutterFlowIconButton( + key: ValueKey('BackNavigationAppBar'), + borderColor: Colors.transparent, + borderRadius: 30.0, + borderWidth: 1.0, + buttonSize: 60.0, + icon: Icon( + Icons.keyboard_arrow_left, + color: theme.primaryText, + size: 30.0, + ), + onPressed: () => Navigator.of(context).pop(), + ); + } +} + +/// [PageWidget] + +abstract class PageWidget extends Widget { + const PageWidget({super.key}); +} + +abstract class ModelPage extends ModelWidget implements PageWidget { + const ModelPage({super.key}); +} + +abstract class StatelessPage extends StatelessWidget + implements PageWidget { + const StatelessPage({super.key}); +} + +abstract class StatefulPage extends StatefulWidget implements PageWidget { + const StatefulPage({super.key}); +} + +abstract class PageState extends State + with MixinPage {} diff --git a/lib/shared/widgets/screen.dart b/lib/shared/widgets/screen.dart new file mode 100644 index 00000000..754a3094 --- /dev/null +++ b/lib/shared/widgets/screen.dart @@ -0,0 +1,19 @@ +part of 'widgets.dart'; + +abstract class ScreenWidget extends Widget { + const ScreenWidget({super.key}); +} + +abstract class ModelScreen extends ModelWidget implements ScreenWidget { + const ModelScreen({super.key}); +} + +abstract class StatelessScreen extends StatelessWidget + implements ScreenWidget { + const StatelessScreen({super.key}); +} + +abstract class StatefulScreen extends StatefulWidget + implements ScreenWidget { + const StatefulScreen({super.key}); +} diff --git a/lib/shared/widgets/text.dart b/lib/shared/widgets/text.dart new file mode 100644 index 00000000..68b8b065 --- /dev/null +++ b/lib/shared/widgets/text.dart @@ -0,0 +1,12 @@ +part of 'widgets.dart'; + +class AutoText extends AutoSizeText { + const AutoText( + super.text, { + super.key, + super.style, + super.overflow, + super.textAlign, + super.maxLines, + }); +} diff --git a/lib/shared/widgets/view/carousel_view.dart b/lib/shared/widgets/view/carousel_view.dart new file mode 100644 index 00000000..fd215ce6 --- /dev/null +++ b/lib/shared/widgets/view/carousel_view.dart @@ -0,0 +1,77 @@ +part of '../widgets.dart'; + +class CategoryCarousel extends StatelessWidget { + final List categories; + final void Function(Category) onSelect; + final void Function(Category) onUnselect; + + const CategoryCarousel({ + super.key, + required this.categories, + required this.onSelect, + required this.onUnselect, + }); + + @override + Widget build(BuildContext context) { + final backgroundTheme = FlutterFlowTheme.of(context).primaryBackground; + + bool isSelected = false; + Category? current = null; + + return Container( + 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]; + } + }, + children: categories.map((category) { + return GestureDetector( + onTap: () {}, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + Container( + padding: const EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: category.color, + shape: BoxShape.circle, + ), + child: Icon( + Icons.folder, + color: Colors.white, + size: 40, + ), + ), + const SizedBox(height: 8), + Text( + category.title, + style: TextStyle( + color: category.color, + fontWeight: FontWeight.bold, + ), + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + ); + }).toList(), + ), + ); + } +} diff --git a/lib/shared/widgets/view/search_view.dart b/lib/shared/widgets/view/search_view.dart new file mode 100644 index 00000000..89bec502 --- /dev/null +++ b/lib/shared/widgets/view/search_view.dart @@ -0,0 +1,200 @@ +part of '../widgets.dart'; + +/// ----------------------------------------------- +/// [SearchView] + +class SearchView extends StatefulComponent { + const SearchView({super.key}); + + @override + State createState() => _SearchViewState(); +} + +class _SearchViewState extends State { + @override + Widget build(BuildContext context) { + return const Placeholder(); + } +} + +class LocalSearchView extends SearchView { + final List list; + final Widget Function(T) itemBuilder; + final bool Function(T, String) filter; + final Widget header; + final List Function(String)? onSearch; + + LocalSearchView({ + Key? key, + 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); + + // return documents.where((documents) => filter(documents, query)).toList(); + + @override + LocalSearchViewState createState() => LocalSearchViewState(); +} + +class LocalSearchViewState extends State> { + TextEditingController editingController = TextEditingController(); + late List filteredItems; + + @override + void initState() { + filteredItems = widget.list; + super.initState(); + } + + @override + Widget build(BuildContext context) { + void filter(value) { + safeSetState(() { + filteredItems = widget.onSearch!(value); + }); + } + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + 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: 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 + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(15.0)), + borderSide: BorderSide( + color: Colors.black), // Set focused border color here + ), + ), + ), + ), + ], + ); + } +} + +class RemoteSearchListView extends SearchView { + final List items; + final String title; + final Widget Function(T) itemBuilder; + final Future> Function(String) fetchItems; + + RemoteSearchListView({ + Key? key, + required this.items, + required this.title, + required this.itemBuilder, + required this.fetchItems, + }) : super(key: key); + + @override + _RemoteSearchViewState createState() => _RemoteSearchViewState(); +} + +class _RemoteSearchViewState extends State> { + TextEditingController editingController = TextEditingController(); + late List filteredItems; + bool isLoading = false; + + @override + void initState() { + filteredItems = widget.items; + super.initState(); + } + + void filterSearchResults(String query) async { + setState(() { + isLoading = true; + }); + final results = await widget.fetchItems(query); + setState(() { + filteredItems = results; + isLoading = false; + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: Container( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: TextField( + onChanged: (value) { + filterSearchResults(value); + }, + controller: editingController, + decoration: InputDecoration( + labelText: "Search", + hintText: "Search", + prefixIcon: Icon(Icons.search), + border: OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(25.0)), + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(15, 0, 50, 0), + child: Text('Últimos Documentos'), + ), + isLoading + ? Center(child: CircularProgressIndicator()) + : Expanded( + child: ListView.builder( + shrinkWrap: true, + itemCount: filteredItems.length, + itemBuilder: (context, index) { + return widget.itemBuilder(filteredItems[index]); + }, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/shared/widgets/viewer/viewer.dart b/lib/shared/widgets/viewer/viewer.dart new file mode 100644 index 00000000..2e5f01c9 --- /dev/null +++ b/lib/shared/widgets/viewer/viewer.dart @@ -0,0 +1,26 @@ +part of '../widgets.dart'; + +typedef PDFViewerState = GlobalKey; + +abstract class Viewer extends StatelessComponent { + const Viewer({super.key, required this.src}); + final String src; + + @override + Widget build(BuildContext context) { + return buildViewer(context); + } + + 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; + + @override + Widget buildViewer(BuildContext context) { + return SfPdfViewer.network(src, key: key as PDFViewerState); + } +} diff --git a/lib/shared/widgets/widgets.dart b/lib/shared/widgets/widgets.dart new file mode 100644 index 00000000..7b240209 --- /dev/null +++ b/lib/shared/widgets/widgets.dart @@ -0,0 +1,21 @@ +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:hub/features/documents/index.dart'; +import 'package:hub/flutter_flow/index.dart'; +import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart'; + +part 'page.dart'; +part 'component.dart'; +part 'screen.dart'; +part 'model.dart'; +part 'entity.dart'; + +/// [View]'s +part 'view/search_view.dart'; +part 'view/carousel_view.dart'; + +/// [Viewer] +part 'viewer/viewer.dart'; + +part 'text.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index 26ed4273..0a647c1b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,7 +13,8 @@ dependencies: sdk: flutter flutter_localizations: sdk: flutter - auto_size_text: 3.0.0 + auto_size_text: ^3.0.0 + syncfusion_flutter_pdfviewer: ^28.2.4 barcode_widget: ^2.0.4 cached_network_image: ^3.4.0 firebase_core: ^3.4.0 @@ -24,7 +25,7 @@ dependencies: app_links: ^6.3.2 # crop_your_image: 1.1.0 csv: 6.0.0 - device_info_plus: ^10.1.2 + device_info_plus: ^11.2.2 firebase_messaging: ^15.1.0 dropdown_button2: 2.3.9 easy_debounce: 2.0.3 @@ -50,7 +51,7 @@ dependencies: from_css_color: 2.0.0 go_router: ^14.3.0 google_fonts: 6.2.1 - http: 1.2.1 + http: 1.3.0 image_picker: 1.1.2 image_picker_android: ^0.8.12+15 image_picker_for_web: ^3.0.5 @@ -106,7 +107,7 @@ dependencies: # json_annotation: ^4.9.0 dependency_overrides: - http: 1.2.1 + http: 1.3.0 uuid: ^4.0.0 win32: 5.5.1 @@ -145,6 +146,7 @@ flutter: - assets/images/ - assets/images/dark/ - assets/images/light/ + - assets/files/ fonts: - family: "SF Pro" fonts: From 56f16d29342b5184d9fb8e1cdb8e60b3015019cc Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Mon, 10 Feb 2025 17:58:53 -0300 Subject: [PATCH 02/32] WIP --- integration_test/utils_test.dart | 4 +- ..._arrow_linked_locals_component_widget.dart | 5 +- .../schedule_visit_detail_widget.dart | 7 +- ..._arrow_linked_locals_component_widget.dart | 4 +- ...cation_modal_template_component_model.dart | 4 +- .../provisional_shcedule_template_widget.dart | 12 +- ...siter_vistor_template_component_model.dart | 2 +- ...iter_vistor_template_component_widget.dart | 11 +- .../view_visit_detail_widget.dart | 4 +- ...earch_modal_template_component_widget.dart | 10 +- lib/features/auth/authentication_service.dart | 15 +- .../backend/api_requests/api_calls.dart | 235 ++++++++++++------ .../documents/document_item_component.dart | 4 +- .../documents/document_manager_screen.dart | 19 +- .../documents/document_page_widget.dart | 1 - .../pages/acess_history_page_widget.dart | 2 +- .../pages/provisional_history_page.dart | 4 +- .../locals_remote_data_source.dart | 15 +- lib/features/local/utils/local_util.dart | 2 +- .../menu/presentation/blocs/menu_bloc.dart | 3 +- .../repositories/license_repository_impl.dart | 2 +- .../firebase_messaging_service.dart | 2 +- .../notification/notification_service.dart | 3 +- .../liberation_history_model.dart | 3 +- .../liberation_history_widget.dart | 2 +- .../message_history_page_widget.dart | 2 +- .../package_order_page.dart | 4 +- .../people_on_the_property_page_widget.dart | 4 +- .../pets_history_screen.dart | 2 +- lib/pages/pets_page/pets_page_model.dart | 6 +- .../preferences_settings_model.dart | 8 +- .../reception_page/reception_page_widget.dart | 2 +- .../residents_on_the_property_screen.dart | 4 +- .../schedule_complete_visit_page_model.dart | 2 +- .../schedule_complete_visit_page_widget.dart | 32 +-- .../visit_history_page_widget.dart | 2 +- .../vehicles_on_the_property.dart | 4 +- .../visits_on_the_property_screen.dart | 2 +- lib/shared/mixins/pegeable_mixin.dart | 120 +++++++++ lib/shared/utils/log_util.dart | 2 +- lib/shared/widgets/view/search_view.dart | 165 ++++++++---- lib/shared/widgets/widgets.dart | 6 + pubspec.yaml | 1 + 43 files changed, 515 insertions(+), 228 deletions(-) create mode 100644 lib/shared/mixins/pegeable_mixin.dart diff --git a/integration_test/utils_test.dart b/integration_test/utils_test.dart index cce351a2..1fb39e07 100644 --- a/integration_test/utils_test.dart +++ b/integration_test/utils_test.dart @@ -25,7 +25,7 @@ Future _loggedWithMultiLocalsAccount( .set(ProfileStorageKey.ownerUUID.key, '7'); await StorageHelper() // .set(ProfileStorageKey.clientName.key, 'FRE ACCESS DEMO'); - await PhpGroup // + await FreAccessWSGlobal // .resopndeVinculo .call(tarefa: 'A'); await LicenseRepositoryImpl() // @@ -58,7 +58,7 @@ Future _loggedWithSomeoneLocalAccount( .set(ProfileStorageKey.ownerUUID.key, '7'); await StorageHelper() // .set(ProfileStorageKey.clientName.key, 'FRE ACCESS DEMO'); - await PhpGroup // + await FreAccessWSGlobal // .resopndeVinculo .call(tarefa: 'A'); await LicenseRepositoryImpl() // diff --git a/lib/components/organism_components/bottom_arrow_linked_locals_component/bottom_arrow_linked_locals_component_widget.dart b/lib/components/organism_components/bottom_arrow_linked_locals_component/bottom_arrow_linked_locals_component_widget.dart index 00f5922e..a5db861f 100644 --- a/lib/components/organism_components/bottom_arrow_linked_locals_component/bottom_arrow_linked_locals_component_widget.dart +++ b/lib/components/organism_components/bottom_arrow_linked_locals_component/bottom_arrow_linked_locals_component_widget.dart @@ -77,7 +77,7 @@ class _BottomArrowLinkedLocalsComponentWidgetState Future _fetchLocals() async { try { setState(() => _loading = true); - widget.response = await PhpGroup.getLocalsCall.call(); + widget.response = await FreAccessWSGlobal.getLocalsCall.call(); final bool isError = widget.response?.jsonBody['error']; if (isError) { @@ -149,7 +149,8 @@ class _BottomArrowLinkedLocalsComponentWidgetState Future _fetchResponseLink(String status, String cliID) async { try { await StorageHelper().set(ProfileStorageKey.clientUUID.key, cliID); - var response = await PhpGroup.resopndeVinculo.call(tarefa: status); + var response = + await FreAccessWSGlobal.resopndeVinculo.call(tarefa: status); if (response.jsonBody['error'] == false) { return { diff --git a/lib/components/organism_components/schedule_visit_detail/schedule_visit_detail_widget.dart b/lib/components/organism_components/schedule_visit_detail/schedule_visit_detail_widget.dart index 0baeea54..3810f5bb 100644 --- a/lib/components/organism_components/schedule_visit_detail/schedule_visit_detail_widget.dart +++ b/lib/components/organism_components/schedule_visit_detail/schedule_visit_detail_widget.dart @@ -781,7 +781,7 @@ class _ScheduleVisitDetailWidgetState extends State { IconButton( onPressed: () async { _model.postScheduleVisit = - await PhpGroup.postScheduleVisitCall.call( + await FreAccessWSGlobal.postScheduleVisitCall.call( devDesc: widget.visitObsStr, idVisitante: widget.visitorStrList, dtInicio: @@ -793,13 +793,14 @@ class _ScheduleVisitDetailWidgetState extends State { obs: widget.visitObsStr, ); - if (PhpGroup.postScheduleVisitCall.error( + if (FreAccessWSGlobal.postScheduleVisitCall.error( (_model.postScheduleVisit?.jsonBody ?? ''), ) == false) { context.pop(); } else { - final String? msg = PhpGroup.postScheduleVisitCall.errorMsg( + final String? msg = + FreAccessWSGlobal.postScheduleVisitCall.errorMsg( (_model.postScheduleVisit?.jsonBody ?? ''), ); await DialogUtil.error(context, msg!) diff --git a/lib/components/organism_components/up_arrow_linked_locals_component/up_arrow_linked_locals_component_widget.dart b/lib/components/organism_components/up_arrow_linked_locals_component/up_arrow_linked_locals_component_widget.dart index d1ad656e..6ddc5984 100644 --- a/lib/components/organism_components/up_arrow_linked_locals_component/up_arrow_linked_locals_component_widget.dart +++ b/lib/components/organism_components/up_arrow_linked_locals_component/up_arrow_linked_locals_component_widget.dart @@ -75,7 +75,7 @@ class _UpArrowLinkedLocalsComponentWidgetState ), ), child: FutureBuilder( - future: PhpGroup.getLocalsCall.call(), + future: FreAccessWSGlobal.getLocalsCall.call(), builder: (context, snapshot) { // Customize what your widget looks like when it's loading. if (!snapshot.hasData) { @@ -94,7 +94,7 @@ class _UpArrowLinkedLocalsComponentWidgetState return Builder( builder: (context) { - final eachLocals = (PhpGroup.getLocalsCall + final eachLocals = (FreAccessWSGlobal.getLocalsCall .locais( gridViewGetLocalsResponse.jsonBody, ) diff --git a/lib/components/templates_components/access_notification_modal_template_component/access_notification_modal_template_component_model.dart b/lib/components/templates_components/access_notification_modal_template_component/access_notification_modal_template_component_model.dart index 03352151..69e93268 100644 --- a/lib/components/templates_components/access_notification_modal_template_component/access_notification_modal_template_component_model.dart +++ b/lib/components/templates_components/access_notification_modal_template_component/access_notification_modal_template_component_model.dart @@ -63,14 +63,14 @@ class AccessNotificationModalTemplateComponentModel }) async { ApiCallResponse? visitRequest; - visitRequest = await PhpGroup.respondeSolicitacaoCall.call( + visitRequest = await FreAccessWSGlobal.respondeSolicitacaoCall.call( referencia: refUUID, tarefa: actionValue, resposta: responseValue, idVisitante: vteUUID, ); - if (PhpGroup.respondeSolicitacaoCall.error( + if (FreAccessWSGlobal.respondeSolicitacaoCall.error( (visitRequest.jsonBody ?? ''), ) == false) { diff --git a/lib/components/templates_components/provisional_schedule_template/provisional_shcedule_template_widget.dart b/lib/components/templates_components/provisional_schedule_template/provisional_shcedule_template_widget.dart index 54e96540..783c114d 100644 --- a/lib/components/templates_components/provisional_schedule_template/provisional_shcedule_template_widget.dart +++ b/lib/components/templates_components/provisional_schedule_template/provisional_shcedule_template_widget.dart @@ -951,9 +951,10 @@ class _ScheduleProvisionalVisitPageWidgetState ? null : () async { try { - model.provVisitSchedule = await PhpGroup - .postProvVisitSchedulingCall - .call( + model.provVisitSchedule = + await FreAccessWSGlobal + .postProvVisitSchedulingCall + .call( data: DateFormat('dd/MM/yyyy HH:mm:ss') .format(DateFormat( 'dd/MM/yyyy HH:mm:ss') @@ -964,7 +965,8 @@ class _ScheduleProvisionalVisitPageWidgetState nome: model.personNameTextController.text, proID: model.ownerUUID, ); - if (PhpGroup.postProvVisitSchedulingCall + if (FreAccessWSGlobal + .postProvVisitSchedulingCall .error((model.provVisitSchedule ?.jsonBody ?? '')) == @@ -982,7 +984,7 @@ class _ScheduleProvisionalVisitPageWidgetState model.notesTextController?.clear(); }); } else { - var message = PhpGroup + var message = FreAccessWSGlobal .postProvVisitSchedulingCall .msg((model.provVisitSchedule ?.jsonBody ?? diff --git a/lib/components/templates_components/regisiter_vistor_template_component/regisiter_vistor_template_component_model.dart b/lib/components/templates_components/regisiter_vistor_template_component/regisiter_vistor_template_component_model.dart index acff7931..1c427c65 100644 --- a/lib/components/templates_components/regisiter_vistor_template_component/regisiter_vistor_template_component_model.dart +++ b/lib/components/templates_components/regisiter_vistor_template_component/regisiter_vistor_template_component_model.dart @@ -46,7 +46,7 @@ class RegisiterVistorTemplateComponentModel Future getVisitanteByDocument( String document, BuildContext context) async { - final response = await PhpGroup.getVisitorByDocCall.call( + final response = await FreAccessWSGlobal.getVisitorByDocCall.call( documento: document, ); diff --git a/lib/components/templates_components/regisiter_vistor_template_component/regisiter_vistor_template_component_widget.dart b/lib/components/templates_components/regisiter_vistor_template_component/regisiter_vistor_template_component_widget.dart index e17b808b..3ebb0c26 100644 --- a/lib/components/templates_components/regisiter_vistor_template_component/regisiter_vistor_template_component_widget.dart +++ b/lib/components/templates_components/regisiter_vistor_template_component/regisiter_vistor_template_component_widget.dart @@ -760,7 +760,7 @@ class _RegisiterVistorTemplateComponentWidgetState _model.uploadedLocalFile, ); _model.scheduleVisitor = - await PhpGroup.postScheduleVisitorCall + await FreAccessWSGlobal.postScheduleVisitorCall .call( documento: _model.textController2.text, nome: _model.textController1.text, @@ -775,7 +775,7 @@ class _RegisiterVistorTemplateComponentWidgetState .onError((e, s) async { return await DialogUtil.errorDefault(context); }); - if (PhpGroup.postScheduleVisitorCall.error( + if (FreAccessWSGlobal.postScheduleVisitorCall.error( (_model.scheduleVisitor?.jsonBody ?? ''), ) == false) { @@ -822,8 +822,8 @@ class _RegisiterVistorTemplateComponentWidgetState } else { return await DialogUtil.error( context, - PhpGroup.postScheduleVisitorCall.errorMsg( - _model + FreAccessWSGlobal.postScheduleVisitorCall + .errorMsg(_model .scheduleVisitor?.jsonBody) == null ? FFLocalizations.of(context) @@ -832,7 +832,8 @@ class _RegisiterVistorTemplateComponentWidgetState 'Erro ao se conectar com o servidor', enText: 'Error connecting to server', ) - : PhpGroup.postScheduleVisitorCall + : FreAccessWSGlobal + .postScheduleVisitorCall .errorMsg( _model.scheduleVisitor?.jsonBody) .toString()); diff --git a/lib/components/templates_components/view_visit_detail/view_visit_detail_widget.dart b/lib/components/templates_components/view_visit_detail/view_visit_detail_widget.dart index 41f38054..0104c183 100644 --- a/lib/components/templates_components/view_visit_detail/view_visit_detail_widget.dart +++ b/lib/components/templates_components/view_visit_detail/view_visit_detail_widget.dart @@ -818,11 +818,11 @@ class _ViewVisitDetailWidgetState extends State { ), onPressed: () async { _model.deleteVisit = - await PhpGroup.deleteVisitCall.call( + await FreAccessWSGlobal.deleteVisitCall.call( idVisita: widget.visitIdStr, ); - if (PhpGroup.deleteVisitCall.error( + if (FreAccessWSGlobal.deleteVisitCall.error( (_model.deleteVisit?.jsonBody ?? ''), ) == false) { diff --git a/lib/components/templates_components/visitor_search_modal_template_component/visitor_search_modal_template_component_widget.dart b/lib/components/templates_components/visitor_search_modal_template_component/visitor_search_modal_template_component_widget.dart index 41fe84e8..a6b016c8 100644 --- a/lib/components/templates_components/visitor_search_modal_template_component/visitor_search_modal_template_component_widget.dart +++ b/lib/components/templates_components/visitor_search_modal_template_component/visitor_search_modal_template_component_widget.dart @@ -406,23 +406,23 @@ class _VisitorSearchModalTemplateComponentWidgetState _model.textController?.selection = TextSelection.collapsed(offset: _model.textController!.text.length); }); - _model.getVisitorByDoc = await PhpGroup.getVisitorByDocCall.call( + _model.getVisitorByDoc = await FreAccessWSGlobal.getVisitorByDocCall.call( documento: _model.textController.text.replaceFirst(RegExp(r'^0+'), '')); - if (PhpGroup.getVisitorByDocCall + if (FreAccessWSGlobal.getVisitorByDocCall .vistanteId((_model.getVisitorByDoc?.jsonBody ?? '')) != '0' && - PhpGroup.getVisitorByDocCall + FreAccessWSGlobal.getVisitorByDocCall .error((_model.getVisitorByDoc?.jsonBody ?? '')) == false && - PhpGroup.getVisitorByDocCall + FreAccessWSGlobal.getVisitorByDocCall .vistanteId((_model.getVisitorByDoc?.jsonBody ?? '')) != null) { String newDoc = _model.textController.text.replaceFirst(RegExp(r'^0+'), ''); bool existDoc = _model.docs.contains(newDoc); if (existDoc == false) { - _model.addToVisitors(PhpGroup.getVisitorByDocCall + _model.addToVisitors(FreAccessWSGlobal.getVisitorByDocCall .visitante((_model.getVisitorByDoc?.jsonBody ?? ''))); safeSetState(() {}); _model.addToDocs( diff --git a/lib/features/auth/authentication_service.dart b/lib/features/auth/authentication_service.dart index 24f5c0c9..88f3acf7 100644 --- a/lib/features/auth/authentication_service.dart +++ b/lib/features/auth/authentication_service.dart @@ -14,7 +14,7 @@ import '../../../flutter_flow/random_data_util.dart'; class AuthenticationService { static Future login(BuildContext context) async { try { - final GetLocalsCall callback = PhpGroup.getLocalsCall; + final GetLocalsCall callback = FreAccessWSGlobal.getLocalsCall; final response = await callback.call(); if (response.jsonBody['error']) { @@ -46,7 +46,7 @@ class AuthenticationService { }) async { try { final ApiCallResponse? response; - final LoginCall callback = PhpGroup.loginCall; + final LoginCall callback = FreAccessWSGlobal.loginCall; String deviceDescription = randomString(10, 10, true, false, false); await StorageHelper() .set(SecureStorageKey.deviceDescription.value, deviceDescription); @@ -111,7 +111,7 @@ class AuthenticationService { if ((email != null && email != '') && (passwd != null && passwd != '' && passwd.length > 7) && (name != null && name != '')) { - response = await PhpGroup.registerCall.call( + response = await FreAccessWSGlobal.registerCall.call( name: name, password: passwd, email: email, @@ -137,7 +137,7 @@ class AuthenticationService { } static Future signOut(BuildContext context) async { - await PhpGroup.unregisterDevice.call(); + await FreAccessWSGlobal.unregisterDevice.call(); final Map extra = { kTransitionInfoKey: const TransitionInfo( hasTransition: true, @@ -158,7 +158,7 @@ class AuthenticationService { static Future forgotPassword(BuildContext context, String email) async { try { final ApiCallResponse? response; - final ForgotPasswordCall callback = PhpGroup.forgotPasswordCall; + final ForgotPasswordCall callback = FreAccessWSGlobal.forgotPasswordCall; final String message = FFLocalizations.of(context).getVariableText( enText: "Send E-mail Successful!", ptText: "E-mail Enviado com Sucesso!"); @@ -183,7 +183,8 @@ class AuthenticationService { static Future changePassword( BuildContext context, String email, String password, String token) async { try { - final ApiCallResponse response = await PhpGroup.changePasswordCall + final ApiCallResponse response = await FreAccessWSGlobal + .changePasswordCall .call(email: email, psswd: password, token: token); if (response.jsonBody['error'] == false) { @@ -209,7 +210,7 @@ class AuthenticationService { static Future deleteAccount(BuildContext context) async { String content; try { - await PhpGroup.deleteAccount.call().then((value) async { + await FreAccessWSGlobal.deleteAccount.call().then((value) async { if (value.jsonBody['error'] == false) { content = FFLocalizations.of(context).getVariableText( enText: 'Account deleted successfully', diff --git a/lib/features/backend/api_requests/api_calls.dart b/lib/features/backend/api_requests/api_calls.dart index e4bdd4fc..d9fce908 100644 --- a/lib/features/backend/api_requests/api_calls.dart +++ b/lib/features/backend/api_requests/api_calls.dart @@ -24,7 +24,9 @@ abstract class Api { GetLicense getLicense = GetLicense(); } -class PhpGroup extends Api { +abstract class Endpoint {} + +class FreAccessWSGlobal extends Api { static String getBaseUrl() => 'https://freaccess.com.br/freaccess'; static Map headers = {}; static LoginCall loginCall = LoginCall(); @@ -70,11 +72,86 @@ class PhpGroup extends Api { static GetOpenedVisits getOpenedVisits = GetOpenedVisits(); GetLicense getLicense = GetLicense(); static GetProvSchedules getProvSchedules = GetProvSchedules(); + static GetCategories getCategories = GetCategories(); + static GetDocuments getDocuments = GetDocuments(); } -class GetProvSchedules { +class GetCategories extends Endpoint { + Future call() async { + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); + final String devUUID = + (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; + final String userUUID = + (await StorageHelper().get(ProfileStorageKey.userUUID.key)) ?? ''; + final String cliID = + (await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? ''; + const String atividade = 'listaCategoriasDocumentos'; + const String pageSize = '10'; + // final bool isFiltered = filter != '' && filter != '.*'; + return await ApiManager.instance.makeApiCall( + callName: 'listaCategoriasDocumentos', + apiUrl: '$baseUrl/processRequest.php', + callType: ApiCallType.POST, + headers: {'Content-Type': 'application/x-www-form-urlencoded'}, + params: { + // if (isFiltered) 'filter': filter, + 'devUUID': devUUID, + 'userUUID': userUUID, + 'cliID': cliID, + 'atividade': atividade, + 'pageSize': pageSize, + }, + bodyType: BodyType.X_WWW_FORM_URL_ENCODED, + returnBody: true, + encodeBodyUtf8: false, + decodeUtf8: false, + cache: false, + isStreamingApi: false, + alwaysAllowBody: false, + ); + } +} + +class GetDocuments extends Endpoint { + Future call(final String page) async { + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); + final String devUUID = + (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; + final String userUUID = + (await StorageHelper().get(ProfileStorageKey.userUUID.key)) ?? ''; + final String cliID = + (await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? ''; + const String atividade = 'listaDocumentos'; + const String pageSize = '10'; + // final bool isFiltered = filter != '' && filter != '.*'; + return await ApiManager.instance.makeApiCall( + callName: 'listaDocumentos', + apiUrl: '$baseUrl/processRequest.php', + callType: ApiCallType.POST, + headers: {'Content-Type': 'application/x-www-form-urlencoded'}, + params: { + // if (isFiltered) 'filter': filter, + 'devUUID': devUUID, + 'userUUID': userUUID, + 'cliID': cliID, + 'atividade': atividade, + 'page': page, + 'pageSize': pageSize, + }, + bodyType: BodyType.X_WWW_FORM_URL_ENCODED, + returnBody: true, + encodeBodyUtf8: false, + decodeUtf8: false, + cache: false, + isStreamingApi: false, + alwaysAllowBody: false, + ); + } +} + +class GetProvSchedules extends Endpoint { Future call(final String page, final String status) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; final String userUUID = @@ -110,9 +187,9 @@ class GetProvSchedules { } } -class GetOpenedVisits { +class GetOpenedVisits extends Endpoint { Future call(final String page) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; final String userUUID = @@ -145,9 +222,9 @@ class GetOpenedVisits { } } -class GetResidentsByProperty { +class GetResidentsByProperty extends Endpoint { Future call(final String page) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = await StorageHelper().get(ProfileStorageKey.devUUID.key) ?? ''; final String userUUID = @@ -179,9 +256,9 @@ class GetResidentsByProperty { } } -class GetVehiclesByProperty { +class GetVehiclesByProperty extends Endpoint { Future call(final String page) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; final String userUUID = @@ -215,9 +292,9 @@ class GetVehiclesByProperty { static GetLicense getLicense = GetLicense(); } -class GetLicense { +class GetLicense extends Endpoint { Future call() async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; final String userUUID = @@ -249,9 +326,9 @@ class GetLicense { } } -class UnregisterDevice { +class UnregisterDevice extends Endpoint { Future call() async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; final String userUUID = @@ -279,9 +356,9 @@ class UnregisterDevice { } } -class DeletePet { +class DeletePet extends Endpoint { Future call({final int? petID = 0}) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; final String userUUID = @@ -315,7 +392,7 @@ class DeletePet { } } -class UpdatePet { +class UpdatePet extends Endpoint { Future call({ final int? petID, final String? image, @@ -328,7 +405,7 @@ class UpdatePet { final String? size, final String? notes, }) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; @@ -374,12 +451,12 @@ class UpdatePet { } } -class GetPets { +class GetPets extends Endpoint { Future call({ final int? page, final int? pageSize, }) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; @@ -414,9 +491,9 @@ class GetPets { } } -class GetPetPhoto { +class GetPetPhoto extends Endpoint { Future call({final int? petId}) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; @@ -450,7 +527,7 @@ class GetPetPhoto { } } -class RegisterPet { +class RegisterPet extends Endpoint { Future call({ final String? image, final String? name, @@ -462,7 +539,7 @@ class RegisterPet { final String? size, final String? notes, }) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; @@ -507,7 +584,7 @@ class RegisterPet { } } -class BuscaEnconcomendas { +class BuscaEnconcomendas extends Endpoint { Future call({ final String? page, final String? pageSize, @@ -522,7 +599,7 @@ class BuscaEnconcomendas { (await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? ''; const String atividade = 'getEncomendas'; - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); return await ApiManager.instance.makeApiCall( callName: 'getEncomendas', @@ -551,7 +628,7 @@ class BuscaEnconcomendas { } } -class CancelaVisita { +class CancelaVisita extends Endpoint { Future call({ final int? idDestino, final int? idVisita, @@ -559,7 +636,7 @@ class CancelaVisita { final String? UsuEmail, final String? DevDesc, }) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; @@ -596,14 +673,14 @@ class CancelaVisita { } } -class DeleteAccount { +class DeleteAccount extends Endpoint { Future call() async { final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; final String userUUID = (await StorageHelper().get(ProfileStorageKey.userUUID.key)) ?? ''; - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); return await ApiManager.instance.makeApiCall( callName: 'deleteAccount', apiUrl: '$baseUrl/deleteAccount.php', @@ -626,11 +703,11 @@ class DeleteAccount { } } -class ChangePanic { +class ChangePanic extends Endpoint { Future call({ final String? newSenhaPanico, }) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; @@ -665,11 +742,11 @@ class ChangePanic { } } -class ChangePass { +class ChangePass extends Endpoint { Future call({ final String? newSenha, }) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; @@ -704,11 +781,11 @@ class ChangePass { } } -class RespondeVinculo { +class RespondeVinculo extends Endpoint { Future call({ final String? tarefa, }) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; @@ -741,11 +818,11 @@ class RespondeVinculo { } } -class ChangeNotifica { +class ChangeNotifica extends Endpoint { Future call({ final String? notifica, }) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; @@ -780,9 +857,9 @@ class ChangeNotifica { } } -class UpdateIDE { +class UpdateIDE extends Endpoint { Future call() async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; @@ -819,9 +896,9 @@ class UpdateIDE { } } -class UpdToken { +class UpdToken extends Endpoint { Future call() async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; @@ -852,9 +929,9 @@ class UpdToken { } } -class LoginCall { +class LoginCall extends Endpoint { Future call() async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; final String email = @@ -902,7 +979,7 @@ class LoginCall { } } -class RegisterCall { +class RegisterCall extends Endpoint { Future call({ required final String name, required final String email, @@ -912,7 +989,7 @@ class RegisterCall { required final String tipo, required final String descricao, }) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); return await ApiManager.instance.makeApiCall( callName: 'register', @@ -941,13 +1018,13 @@ class RegisterCall { } } -class ChangePasswordCall { +class ChangePasswordCall extends Endpoint { Future call({ required final String email, required final String token, required final String psswd, }) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; final String userUUID = @@ -981,11 +1058,11 @@ class ChangePasswordCall { } } -class ForgotPasswordCall { +class ForgotPasswordCall extends Endpoint { Future call({ final String? email, }) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); return await ApiManager.instance.makeApiCall( callName: 'forgotPassword', @@ -1008,9 +1085,9 @@ class ForgotPasswordCall { } } -class GetLocalsCall { +class GetLocalsCall extends Endpoint { Future call() async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = await StorageHelper().get(ProfileStorageKey.devUUID.key) ?? ''; @@ -1047,14 +1124,14 @@ class GetLocalsCall { ) as List?; } -class PostScheduleVisitorCall { +class PostScheduleVisitorCall extends Endpoint { Future call({ required final String documento, required final String nome, required final String tipo, required final String foto, }) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; @@ -1101,7 +1178,7 @@ class PostScheduleVisitorCall { )); } -class PostScheduleVisitCall { +class PostScheduleVisitCall extends Endpoint { Future call({ final String? devDesc, final String? idVisitante, @@ -1112,7 +1189,7 @@ class PostScheduleVisitCall { final int? idNAC, final String? obs, }) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; @@ -1163,13 +1240,13 @@ class PostScheduleVisitCall { )); } -class GetScheduleVisitCall { +class GetScheduleVisitCall extends Endpoint { Future call({ final int? pageSize, final int? pageNumber, final String? chaveBusca, }) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; @@ -1444,9 +1521,9 @@ class GetScheduleVisitCall { )); } -class GetDadosCall { +class GetDadosCall extends Endpoint { Future call() async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; @@ -1690,11 +1767,11 @@ class GetDadosCall { )); } -class GetVisitorByDocCall { +class GetVisitorByDocCall extends Endpoint { Future call({ final String? documento, }) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; @@ -1746,12 +1823,12 @@ class GetVisitorByDocCall { )); } -class GetFotoVisitanteCall { +class GetFotoVisitanteCall extends Endpoint { Future call({ final String? documento, final String? tipo, }) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; @@ -1786,14 +1863,14 @@ class GetFotoVisitanteCall { } } -class PostProvVisitSchedulingCall { +class PostProvVisitSchedulingCall extends Endpoint { Future call({ final String? data, final String? motivo, final String? nome, final String? proID, }) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; @@ -1840,12 +1917,12 @@ class PostProvVisitSchedulingCall { )); } -class GetVisitsCall { +class GetVisitsCall extends Endpoint { Future call({ final int? pageSize, final int? pageNumber, }) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; @@ -2107,11 +2184,11 @@ class GetVisitsCall { .toList(); } -class DeleteVisitCall { +class DeleteVisitCall extends Endpoint { Future call({ final String? idVisita, }) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; @@ -2155,9 +2232,9 @@ class DeleteVisitCall { )); } -class GetPessoasLocalCall { +class GetPessoasLocalCall extends Endpoint { Future call() async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; @@ -2220,14 +2297,14 @@ class GetPessoasLocalCall { .toList(); } -class RespondeSolicitacaoCall { +class RespondeSolicitacaoCall extends Endpoint { Future call({ final String? referencia, final String? tarefa, final String? resposta, final String? idVisitante, }) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; @@ -2274,13 +2351,13 @@ class RespondeSolicitacaoCall { )); } -class GetAccessCall { +class GetAccessCall extends Endpoint { Future call({ final String? pageSize, final String? pageNumber, final String? pesTipo, }) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; @@ -2528,9 +2605,9 @@ class GetAccessCall { .toList(); } -class GetLiberationsCall { +class GetLiberationsCall extends Endpoint { Stream call() { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final StreamController controller = StreamController(); Future.microtask(() async { @@ -2721,13 +2798,13 @@ class GetLiberationsCall { .toList(); } -class GetMessagesCall { +class GetMessagesCall extends Endpoint { Future call({ final String? pageSize, final String? pageNumber, final String? tipoDestino, }) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; diff --git a/lib/features/documents/document_item_component.dart b/lib/features/documents/document_item_component.dart index 3c0ff117..963ada8f 100644 --- a/lib/features/documents/document_item_component.dart +++ b/lib/features/documents/document_item_component.dart @@ -12,7 +12,6 @@ interface class Category extends Entity { interface class Document extends Entity { final String title; - final String description; final Category category; final String to; final String from; @@ -20,13 +19,12 @@ interface class Document extends Entity { final String updatedAt; Document({ - required this.createdAt, + required this.createdAt, required this.updatedAt, required this.category, required this.to, required this.from, required this.title, - required this.description, }); } diff --git a/lib/features/documents/document_manager_screen.dart b/lib/features/documents/document_manager_screen.dart index 407a5427..0608e17e 100644 --- a/lib/features/documents/document_manager_screen.dart +++ b/lib/features/documents/document_manager_screen.dart @@ -26,7 +26,20 @@ class DocumentManagerScreen extends StatelessScreen { document.updatedAt.toLowerCase().contains(lowerQuery); } - DocumentItem itemBuilder(document) => DocumentItem(document: document); + 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'); @@ -75,13 +88,15 @@ class DocumentManagerScreen extends StatelessScreen { return Column( children: [ Expanded( - child: LocalSearchView( + child: RemoteSearchListView( key: _listViewKey, header: header, onSearch: filterByString, list: documents, itemBuilder: itemBuilder, filter: filter, + title: '', + // fetchItems: (String ) { },, ), ), ] // diff --git a/lib/features/documents/document_page_widget.dart b/lib/features/documents/document_page_widget.dart index 99dc2849..a70f2775 100644 --- a/lib/features/documents/document_page_widget.dart +++ b/lib/features/documents/document_page_widget.dart @@ -8,7 +8,6 @@ List generateDocuments(int count) { count, (index) => Document( title: 'Lorem Ipsum et Cetera $index', - description: 'Description for document $index', category: Category(color: color(), title: str()), to: str(), from: str(), diff --git a/lib/features/history/presentation/pages/acess_history_page_widget.dart b/lib/features/history/presentation/pages/acess_history_page_widget.dart index 16708425..2d5253b1 100644 --- a/lib/features/history/presentation/pages/acess_history_page_widget.dart +++ b/lib/features/history/presentation/pages/acess_history_page_widget.dart @@ -198,7 +198,7 @@ class _AccessHistoryState extends State { Future fetchAccessHistoryService() async { try { setState(() => _loading = true); - var response = await PhpGroup.getAccessCall.call( + var response = await FreAccessWSGlobal.getAccessCall.call( pageSize: _pageSize.toString(), pageNumber: _pageNumber.toString(), pesTipo: _personType != 'E' && _personType != 'O' ? 'T' : _personType, diff --git a/lib/features/history/presentation/pages/provisional_history_page.dart b/lib/features/history/presentation/pages/provisional_history_page.dart index ae43ad8d..bfaca8cf 100644 --- a/lib/features/history/presentation/pages/provisional_history_page.dart +++ b/lib/features/history/presentation/pages/provisional_history_page.dart @@ -222,8 +222,8 @@ class ProvisionalHistoryState extends State { Future fetchHistoryService() async { try { setState(() => _loading = true); - var response = - await PhpGroup.getProvSchedules(_pageNumber.toString(), status); + var response = await FreAccessWSGlobal.getProvSchedules( + _pageNumber.toString(), status); final List history = response.jsonBody['agendamento']['value'] ?? []; diff --git a/lib/features/local/data/data_sources/locals_remote_data_source.dart b/lib/features/local/data/data_sources/locals_remote_data_source.dart index fbe41ff1..939976ad 100644 --- a/lib/features/local/data/data_sources/locals_remote_data_source.dart +++ b/lib/features/local/data/data_sources/locals_remote_data_source.dart @@ -32,7 +32,7 @@ class LocalsRemoteDataSourceImpl implements LocalsRemoteDataSource { @override Future linkLocal(BuildContext context) async { try { - final GetLocalsCall callback = PhpGroup.getLocalsCall; + final GetLocalsCall callback = FreAccessWSGlobal.getLocalsCall; var response = await callback.call(); final bool? isError = response.jsonBody['error']; @@ -72,7 +72,7 @@ class LocalsRemoteDataSourceImpl implements LocalsRemoteDataSource { Future processLocals(BuildContext context) async { log('() => processLocals'); try { - final GetLocalsCall callback = PhpGroup.getLocalsCall; + final GetLocalsCall callback = FreAccessWSGlobal.getLocalsCall; final ApiCallResponse response = await callback.call(); final bool? isError = response.jsonBody['error']; @@ -163,10 +163,10 @@ class LocalsRemoteDataSourceImpl implements LocalsRemoteDataSource { @override Future processProperty(BuildContext context) async { try { - final GetDadosCall callback = PhpGroup.getDadosCall; + final GetDadosCall callback = FreAccessWSGlobal.getDadosCall; ApiCallResponse? response = await callback.call(); if (response.jsonBody == null) { - final GetLocalsCall callback = PhpGroup.getLocalsCall; + final GetLocalsCall callback = FreAccessWSGlobal.getLocalsCall; response = await callback.call(); // final String errorMsg = FFLocalizations.of(context).getVariableText( // enText: 'Verify your connection', @@ -178,7 +178,7 @@ class LocalsRemoteDataSourceImpl implements LocalsRemoteDataSource { final bool? isError = response.jsonBody['error']; if (isError == true) { - final GetLocalsCall callback = PhpGroup.getLocalsCall; + final GetLocalsCall callback = FreAccessWSGlobal.getLocalsCall; response = await callback.call(); // final String errorMsg = // response.jsonBody['error_msg'] ?? 'Local indisponível'; @@ -231,8 +231,9 @@ class LocalsRemoteDataSourceImpl implements LocalsRemoteDataSource { enText: 'Device unlinked successfully', ptText: 'Dispositivo desvinculado com sucesso', ); - final bool status = - await PhpGroup.resopndeVinculo.call(tarefa: 'I').then((value) async { + final bool status = await FreAccessWSGlobal.resopndeVinculo + .call(tarefa: 'I') + .then((value) async { if (value.jsonBody['error'] == false) { await StorageHelper().set(ProfileStorageKey.clientName.key, ''); await StorageHelper().set(ProfileStorageKey.ownerName.key, ''); diff --git a/lib/features/local/utils/local_util.dart b/lib/features/local/utils/local_util.dart index 8de35b63..04a042da 100644 --- a/lib/features/local/utils/local_util.dart +++ b/lib/features/local/utils/local_util.dart @@ -52,7 +52,7 @@ class LocalUtil { await StorageHelper() .set(ProfileStorageKey.ownerName.key, locals[0]['CLU_OWNER_DSC']); - var response = await PhpGroup.resopndeVinculo.call(tarefa: 'A'); + var response = await FreAccessWSGlobal.resopndeVinculo.call(tarefa: 'A'); if (response.jsonBody['error'] == true) { await StorageHelper().set(ProfileStorageKey.clientUUID.key, ''); await StorageHelper().set(ProfileStorageKey.ownerUUID.key, ''); diff --git a/lib/features/menu/presentation/blocs/menu_bloc.dart b/lib/features/menu/presentation/blocs/menu_bloc.dart index dc384bf0..c5eddf2d 100644 --- a/lib/features/menu/presentation/blocs/menu_bloc.dart +++ b/lib/features/menu/presentation/blocs/menu_bloc.dart @@ -37,7 +37,8 @@ class MenuBloc extends Bloc { } Future _onMenuEvent(MenuEvent event, Emitter emit) async { - await LicenseRemoteDataSourceImpl(PhpGroup()).waitForSaveCompletion(); + await LicenseRemoteDataSourceImpl(FreAccessWSGlobal()) + .waitForSaveCompletion(); final List newEntries = await MenuRepositoryImpl().entries2Items(entries, item); diff --git a/lib/features/module/data/repositories/license_repository_impl.dart b/lib/features/module/data/repositories/license_repository_impl.dart index c578608f..3d7068b4 100644 --- a/lib/features/module/data/repositories/license_repository_impl.dart +++ b/lib/features/module/data/repositories/license_repository_impl.dart @@ -17,7 +17,7 @@ class LicenseRepositoryImpl implements LicenseRepository { LicenseRepositoryImpl([Database? database, Api? api]) : database = database ?? DatabaseService.database, - api = api ?? PhpGroup() { + api = api ?? FreAccessWSGlobal() { localDataSource = LicenseLocalDataSourceImpl(this.database); remoteDataSource = LicenseRemoteDataSourceImpl(this.api); } diff --git a/lib/features/notification/firebase_messaging_service.dart b/lib/features/notification/firebase_messaging_service.dart index 68fc300a..60fb2fa3 100644 --- a/lib/features/notification/firebase_messaging_service.dart +++ b/lib/features/notification/firebase_messaging_service.dart @@ -48,7 +48,7 @@ class FirebaseMessagingService { await StorageHelper().set(SecureStorageKey.token.value, deviceToken); final ApiCallResponse? response; - response = await PhpGroup.updToken.call(); + response = await FreAccessWSGlobal.updToken.call(); if (response.jsonBody['error'] == false) { log('Token Atualizado com Sucesso!'); diff --git a/lib/features/notification/notification_service.dart b/lib/features/notification/notification_service.dart index 8c7b423c..610c83d4 100644 --- a/lib/features/notification/notification_service.dart +++ b/lib/features/notification/notification_service.dart @@ -25,7 +25,8 @@ Future onMessageReceived( required String? response, required String? id}) async { final ApiCallResponse? respondeSolicitacaoCall; - final RespondeSolicitacaoCall callback = PhpGroup.respondeSolicitacaoCall; + final RespondeSolicitacaoCall callback = + FreAccessWSGlobal.respondeSolicitacaoCall; respondeSolicitacaoCall = await callback.call( referencia: ref, diff --git a/lib/pages/liberation_history/liberation_history_model.dart b/lib/pages/liberation_history/liberation_history_model.dart index dc93f26b..044fd7a3 100644 --- a/lib/pages/liberation_history/liberation_history_model.dart +++ b/lib/pages/liberation_history/liberation_history_model.dart @@ -50,7 +50,8 @@ class LiberationHistoryModel extends FlutterFlowModel { required String? response, required String? id}) async { final ApiCallResponse? respondeSolicitacaoCall; - final RespondeSolicitacaoCall callback = PhpGroup.respondeSolicitacaoCall; + final RespondeSolicitacaoCall callback = + FreAccessWSGlobal.respondeSolicitacaoCall; respondeSolicitacaoCall = await callback.call( referencia: ref, diff --git a/lib/pages/liberation_history/liberation_history_widget.dart b/lib/pages/liberation_history/liberation_history_widget.dart index f69abdee..f596ff8f 100644 --- a/lib/pages/liberation_history/liberation_history_widget.dart +++ b/lib/pages/liberation_history/liberation_history_widget.dart @@ -333,7 +333,7 @@ class _LiberationHistoryWidgetState extends State { Future _fetchRequests() async { try { safeSetState(() => _loading = true); - var response = await PhpGroup.getLiberationsCall.call().first; + var response = await FreAccessWSGlobal.getLiberationsCall.call().first; final List requests = response.jsonBody['solicitacoes'] ?? []; diff --git a/lib/pages/message_history_page/message_history_page_widget.dart b/lib/pages/message_history_page/message_history_page_widget.dart index 3e72708b..4e0f64cc 100644 --- a/lib/pages/message_history_page/message_history_page_widget.dart +++ b/lib/pages/message_history_page/message_history_page_widget.dart @@ -97,7 +97,7 @@ class _MessageHistoryPageWidgetState extends State try { setState(() => _loading = true); - var response = await PhpGroup.getMessagesCall.call( + var response = await FreAccessWSGlobal.getMessagesCall.call( pageSize: _pageSize.toString(), pageNumber: _pageNumber.toString(), tipoDestino: _destinyType, diff --git a/lib/pages/package_order_page/package_order_page.dart b/lib/pages/package_order_page/package_order_page.dart index a8d04b60..e6af9962 100644 --- a/lib/pages/package_order_page/package_order_page.dart +++ b/lib/pages/package_order_page/package_order_page.dart @@ -97,7 +97,7 @@ class _PackageOrderPage extends State { try { setState(() => _loading = true); - var response = await PhpGroup.buscaEnconcomendas.call( + var response = await FreAccessWSGlobal.buscaEnconcomendas.call( pageSize: _pageSize.toString(), page: _pageNumber.toString(), adresseeType: _adresseeType == '.*' ? 'TOD' : _adresseeType, @@ -314,7 +314,7 @@ class _PackageOrderPage extends State { } String _imagePath(dynamic order) { - return '${PhpGroup.getBaseUrl()}/getImage.php?cliID=$cliUUID&atividade=getFotoEncomenda&orderId=${order['id'] ?? ''}'; + return '${FreAccessWSGlobal.getBaseUrl()}/getImage.php?cliID=$cliUUID&atividade=getFotoEncomenda&orderId=${order['id'] ?? ''}'; } Map _labelsHashMap(dynamic order) { diff --git a/lib/pages/people_on_the_property_page/people_on_the_property_page_widget.dart b/lib/pages/people_on_the_property_page/people_on_the_property_page_widget.dart index 097f9090..9a802749 100644 --- a/lib/pages/people_on_the_property_page/people_on_the_property_page_widget.dart +++ b/lib/pages/people_on_the_property_page/people_on_the_property_page_widget.dart @@ -82,7 +82,7 @@ class _PeopleOnThePropertyPageState extends State { body: SafeArea( top: true, child: FutureBuilder( - future: PhpGroup.getPessoasLocalCall.call(), + future: FreAccessWSGlobal.getPessoasLocalCall.call(), builder: (context, snapshot) { // Customize what your widget looks like when it's loading. if (!snapshot.hasData) { @@ -123,7 +123,7 @@ class _PeopleOnThePropertyPageState extends State { } final columnGetPessoasLocalResponse = snapshot.data!; - final getPoepleProperty = PhpGroup.getPessoasLocalCall + final getPoepleProperty = FreAccessWSGlobal.getPessoasLocalCall .pessoas( columnGetPessoasLocalResponse.jsonBody, ) diff --git a/lib/pages/pets_on_the_property_page/pets_history_screen.dart b/lib/pages/pets_on_the_property_page/pets_history_screen.dart index 813c1158..f6eace39 100644 --- a/lib/pages/pets_on_the_property_page/pets_history_screen.dart +++ b/lib/pages/pets_on_the_property_page/pets_history_screen.dart @@ -100,7 +100,7 @@ class _PetsHistoryScreenState extends State try { setState(() => _loading = true); - var response = await PhpGroup.getPets.call( + var response = await FreAccessWSGlobal.getPets.call( pageSize: _pageSize, page: _pageNumber, ); diff --git a/lib/pages/pets_page/pets_page_model.dart b/lib/pages/pets_page/pets_page_model.dart index e7de27fe..d9e96e61 100644 --- a/lib/pages/pets_page/pets_page_model.dart +++ b/lib/pages/pets_page/pets_page_model.dart @@ -280,7 +280,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'; - final response = await PhpGroup.updatePet.call( + final response = await FreAccessWSGlobal.updatePet.call( petID: petId, image: img, birthdayDate: textControllerData!.text, @@ -323,7 +323,7 @@ class PetsPageModel extends FlutterFlowModel { Future registerPet() async { var img = await ImageUtils.convertImageFileToBase64(uploadedLocalFile!); img = "base64;jpeg,$img"; - final response = await PhpGroup.registerPet.call( + final response = await FreAccessWSGlobal.registerPet.call( image: img, birthdayDate: textControllerData!.text, color: textControllerColor!.text, @@ -464,7 +464,7 @@ class PetsPageModel extends FlutterFlowModel { enText: 'Are you sure you want to delete this pet?', ), () async { int id = item['id']; - await PhpGroup.deletePet + await FreAccessWSGlobal.deletePet .call( petID: id, ) diff --git a/lib/pages/preferences_settings_page/preferences_settings_model.dart b/lib/pages/preferences_settings_page/preferences_settings_model.dart index 17e79b25..0f57850c 100644 --- a/lib/pages/preferences_settings_page/preferences_settings_model.dart +++ b/lib/pages/preferences_settings_page/preferences_settings_model.dart @@ -62,7 +62,7 @@ class PreferencesPageModel with ChangeNotifier { onConfirm() async { String content; String value = !isNotify ? 'S' : 'N'; - await PhpGroup.changeNotifica + await FreAccessWSGlobal.changeNotifica .call(notifica: value) .then((value) async { if (value.jsonBody['error'] == false) { @@ -109,7 +109,7 @@ class PreferencesPageModel with ChangeNotifier { ); onConfirm() async { String content; - await PhpGroup.updateIDE.call().then((value) async { + await FreAccessWSGlobal.updateIDE.call().then((value) async { if (value.jsonBody['error'] == false) { notifyListeners(); content = FFLocalizations.of(context).getVariableText( @@ -141,7 +141,7 @@ class PreferencesPageModel with ChangeNotifier { Future toggleAccess(BuildContext context) async { onChange(String key) async { await StorageHelper().set(SecureStorageKey.accessPass.value, key); - await PhpGroup.changePass + await FreAccessWSGlobal.changePass .call(newSenha: key) .then((value) async { final String content; @@ -180,7 +180,7 @@ class PreferencesPageModel with ChangeNotifier { Future togglePanic(BuildContext context) async { onChange(String key) async { await StorageHelper().set(SecureStorageKey.panicPass.value, key); - await PhpGroup.changePanic + await FreAccessWSGlobal.changePanic .call(newSenhaPanico: key) .then((value) async { final String content; diff --git a/lib/pages/reception_page/reception_page_widget.dart b/lib/pages/reception_page/reception_page_widget.dart index ddd1854a..98760970 100644 --- a/lib/pages/reception_page/reception_page_widget.dart +++ b/lib/pages/reception_page/reception_page_widget.dart @@ -180,7 +180,7 @@ class _ReceptionPageWidgetState extends State padding: const EdgeInsets.fromLTRB(60, 0, 60, 25), child: FFButtonWidget( onPressed: () async { - PhpGroup.unregisterDevice(); + FreAccessWSGlobal.unregisterDevice(); AuthenticationService.signOut(context); setState(() {}); }, diff --git a/lib/pages/residents_on_the_property/residents_on_the_property_screen.dart b/lib/pages/residents_on_the_property/residents_on_the_property_screen.dart index 8f63d8a2..515d556e 100644 --- a/lib/pages/residents_on_the_property/residents_on_the_property_screen.dart +++ b/lib/pages/residents_on_the_property/residents_on_the_property_screen.dart @@ -175,8 +175,8 @@ class _ResidentsOnThePropertyState extends State try { setState(() => _loading = true); - var response = - await PhpGroup.getResidentsByProperty.call(_pageNumber.toString()); + var response = await FreAccessWSGlobal.getResidentsByProperty + .call(_pageNumber.toString()); final List residents = response.jsonBody['residents'] ?? []; safeSetState(() => count = response.jsonBody['total_rows'] ?? 0); diff --git a/lib/pages/schedule_complete_visit_page/schedule_complete_visit_page_model.dart b/lib/pages/schedule_complete_visit_page/schedule_complete_visit_page_model.dart index f7248d93..18647b22 100644 --- a/lib/pages/schedule_complete_visit_page/schedule_complete_visit_page_model.dart +++ b/lib/pages/schedule_complete_visit_page/schedule_complete_visit_page_model.dart @@ -361,7 +361,7 @@ class ScheduleCompleteVisitPageModel required String? accessKey, required String? email}) async { final ApiCallResponse? response; - final CancelaVisita callback = PhpGroup.cancelaVisita; + final CancelaVisita callback = FreAccessWSGlobal.cancelaVisita; response = await callback.call( idDestino: idDestino, diff --git a/lib/pages/schedule_complete_visit_page/schedule_complete_visit_page_widget.dart b/lib/pages/schedule_complete_visit_page/schedule_complete_visit_page_widget.dart index ce4d41a3..bc34c8f6 100644 --- a/lib/pages/schedule_complete_visit_page/schedule_complete_visit_page_widget.dart +++ b/lib/pages/schedule_complete_visit_page/schedule_complete_visit_page_widget.dart @@ -1072,7 +1072,8 @@ class _ScheduleCompleteVisitPageWidgetState height: scaledSizedBoxSize * 0.5, decoration: const BoxDecoration(), child: FutureBuilder( - future: PhpGroup.getDadosCall.call(), + future: + FreAccessWSGlobal.getDadosCall.call(), builder: (context, snapshot) { if (!snapshot.hasData) { return Center( @@ -1089,13 +1090,14 @@ class _ScheduleCompleteVisitPageWidgetState ); } - final reasonsJsonList = - PhpGroup.getDadosCall.reasonsJsonList( - snapshot.data!.jsonBody); - final reasonsOptionLabels = PhpGroup + final reasonsJsonList = FreAccessWSGlobal .getDadosCall - .reasonsMotDescStrList( + .reasonsJsonList( snapshot.data!.jsonBody); + final reasonsOptionLabels = + FreAccessWSGlobal.getDadosCall + .reasonsMotDescStrList( + snapshot.data!.jsonBody); model.processDropDown1(reasonsJsonList!); @@ -1196,7 +1198,8 @@ class _ScheduleCompleteVisitPageWidgetState height: scaledSizedBoxSize * 0.5, decoration: const BoxDecoration(), child: FutureBuilder( - future: PhpGroup.getDadosCall.call(), + future: + FreAccessWSGlobal.getDadosCall.call(), builder: (context, snapshot) { if (!snapshot.hasData) { return Center( @@ -1213,13 +1216,14 @@ class _ScheduleCompleteVisitPageWidgetState ); } - final lavelsJsonList = - PhpGroup.getDadosCall.levelJsonList( - snapshot.data!.jsonBody); - final lavelsOptionLabels = PhpGroup + final lavelsJsonList = FreAccessWSGlobal .getDadosCall - .levelNACDescricaoStrList( + .levelJsonList( snapshot.data!.jsonBody); + final lavelsOptionLabels = + FreAccessWSGlobal.getDadosCall + .levelNACDescricaoStrList( + snapshot.data!.jsonBody); model.processDropDown2(lavelsJsonList!); return FlutterFlowDropDown( @@ -1557,7 +1561,7 @@ class _ScheduleCompleteVisitPageWidgetState onPressed: model.isValid() ? () async { Future scheduleVisit() async { - await PhpGroup.postScheduleVisitCall + await FreAccessWSGlobal.postScheduleVisitCall .call( devDesc: model.textController3.text, idVisitante: model.visitorStrList, @@ -1573,7 +1577,7 @@ class _ScheduleCompleteVisitPageWidgetState .catchError((e) async => await DialogUtil.errorDefault(context)) .then((value) async { - if (PhpGroup.postScheduleVisitCall.error( + if (FreAccessWSGlobal.postScheduleVisitCall.error( (value.jsonBody ?? ''), ) == false) { diff --git a/lib/pages/schedule_complete_visit_page/visit_history_page_widget.dart b/lib/pages/schedule_complete_visit_page/visit_history_page_widget.dart index 0c4ddaf1..9c4f234e 100644 --- a/lib/pages/schedule_complete_visit_page/visit_history_page_widget.dart +++ b/lib/pages/schedule_complete_visit_page/visit_history_page_widget.dart @@ -76,7 +76,7 @@ class _VisitHistoryWidgetState extends State setState(() => _loading = true); var response = await ScheduleCompleteVisitPageModel().visitHistory( - requestFn: () => PhpGroup.getVisitsCall.call( + requestFn: () => FreAccessWSGlobal.getVisitsCall.call( pageSize: _pageSize, pageNumber: _pageNumber, ), diff --git a/lib/pages/vehicles_on_the_property/vehicles_on_the_property.dart b/lib/pages/vehicles_on_the_property/vehicles_on_the_property.dart index c27a067e..f40576ce 100644 --- a/lib/pages/vehicles_on_the_property/vehicles_on_the_property.dart +++ b/lib/pages/vehicles_on_the_property/vehicles_on_the_property.dart @@ -170,8 +170,8 @@ class _VehicleOnThePropertyState extends State try { setState(() => _loading = true); - var response = - await PhpGroup.getVehiclesByProperty.call(_pageNumber.toString()); + var response = await FreAccessWSGlobal.getVehiclesByProperty + .call(_pageNumber.toString()); final List vehicles = response.jsonBody['vehicles'] ?? []; safeSetState(() => count = response.jsonBody['total_rows'] ?? 0); diff --git a/lib/pages/visits_on_the_property/visits_on_the_property_screen.dart b/lib/pages/visits_on_the_property/visits_on_the_property_screen.dart index 65f9a039..ad1fae23 100644 --- a/lib/pages/visits_on_the_property/visits_on_the_property_screen.dart +++ b/lib/pages/visits_on_the_property/visits_on_the_property_screen.dart @@ -176,7 +176,7 @@ class _VisitsOnThePropertyState extends State setState(() => _loading = true); var response = - await PhpGroup.getOpenedVisits.call(_pageNumber.toString()); + await FreAccessWSGlobal.getOpenedVisits.call(_pageNumber.toString()); final List visits = response.jsonBody['visitas'] ?? []; safeSetState(() => count = response.jsonBody['total_rows'] ?? 0); diff --git a/lib/shared/mixins/pegeable_mixin.dart b/lib/shared/mixins/pegeable_mixin.dart new file mode 100644 index 00000000..1752244a --- /dev/null +++ b/lib/shared/mixins/pegeable_mixin.dart @@ -0,0 +1,120 @@ +import 'package:flutter/material.dart'; +import 'package:hub/flutter_flow/index.dart'; +import 'package:hub/shared/utils/limited_text_size.dart'; +import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; + +extension PagedListViewExtension + on PagedSliverList {} + +mixin Pageable on State { + Expanded buildPaginatedListView( + String noDataFound, + PagingController pg, + Widget Function(BuildContext, Y, int) itemBuilder) { + final theme = FlutterFlowTheme.of(context); + + return Expanded( + child: RefreshIndicator( + backgroundColor: theme.primaryBackground, + color: theme.primary, + onRefresh: () async => pg.refresh(), + child: PagedListView( + pagingController: pg, + builderDelegate: PagedChildBuilderDelegate( + animateTransitions: true, + itemBuilder: (context, item, index) => + itemBuilder(context, item, index), + // noMoreItemsIndicatorBuilder: , + newPageProgressIndicatorBuilder: (context) => + buildLoadingIndicator(context), + firstPageProgressIndicatorBuilder: (context) => + buildLoadingIndicator(context), + noItemsFoundIndicatorBuilder: (context) => + buildNoDataFound(context, noDataFound), + firstPageErrorIndicatorBuilder: (context) => const Placeholder(), + newPageErrorIndicatorBuilder: (context) => const Placeholder(), + ), + ), + ), + ); + } + + Future showError(PagingStatus status, PagingController pg) async { + if (status == PagingStatus.subsequentPageError) { + final message = FFLocalizations.of(context).getVariableText( + enText: 'Something went wrong while fetching a new page.', + ptText: 'Algo deu errado ao buscar uma nova página.', + ); + final retry = FFLocalizations.of(context).getVariableText( + enText: 'Retry', + ptText: 'Recarregar', + ); + + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(message), + action: SnackBarAction( + label: retry, + onPressed: () => pg.retryLastFailedRequest(), + ), + ), + ); + } + } + + Future fetchPage({ + required Future<(bool, dynamic)> Function() dataProvider, + required void Function(dynamic data) onDataAvailable, + required void Function() onDataUnavailable, + required void Function(Object error, StackTrace stackTrace) onFetchError, + }) async { + try { + final (bool isDataAvailable, dynamic data) = await dataProvider(); + if (isDataAvailable) { + onDataAvailable(data); + } else { + onDataUnavailable(); + } + } catch (error, stackTrace) { + onFetchError(error, stackTrace); + } + } + + void showNoMoreDataSnackBar(BuildContext context) { + final message = FFLocalizations.of(context).getVariableText( + ptText: "Não há mais dados.", + enText: "No more data.", + ); + + showSnackbar(context, message, true); + } + + Widget buildNoDataFound(BuildContext context, String title) { + final headerFontSize = LimitedFontSizeUtil.getHeaderFontSize(context); + // final bodyFontSize = LimitedFontSizeUtil.getBodyFontSize(context); + return Expanded( + child: Center( + child: Text( + title, + style: TextStyle( + fontFamily: 'Nunito', + fontSize: headerFontSize, + ), + ), + ), + ); + } + + Widget buildLoadingIndicator(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 15), + child: Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + FlutterFlowTheme.of(context).primary, + ), + ), + ), + ); + } +} diff --git a/lib/shared/utils/log_util.dart b/lib/shared/utils/log_util.dart index c9c9526b..b6bfebf9 100644 --- a/lib/shared/utils/log_util.dart +++ b/lib/shared/utils/log_util.dart @@ -6,7 +6,7 @@ import 'package:hub/features/backend/index.dart'; class LogUtil { static void requestAPIFailed(String url, String body, String reason, dynamic error, StackTrace stack) async { - final fullUrl = "${PhpGroup.getBaseUrl()}/$url"; + final fullUrl = "${FreAccessWSGlobal.getBaseUrl()}/$url"; log("URL: $fullUrl"); log("Body: $body"); log("Reason: $reason"); diff --git a/lib/shared/widgets/view/search_view.dart b/lib/shared/widgets/view/search_view.dart index 89bec502..37a6b20e 100644 --- a/lib/shared/widgets/view/search_view.dart +++ b/lib/shared/widgets/view/search_view.dart @@ -109,92 +109,149 @@ class LocalSearchViewState extends State> { } class RemoteSearchListView extends SearchView { - final List items; + final List list; final String title; final Widget Function(T) itemBuilder; - final Future> Function(String) fetchItems; + // final Future> Function(String) fetchItems; + final bool Function(T, String) filter; + final Widget header; + final List Function(String)? onSearch; RemoteSearchListView({ Key? key, - required this.items, + // required this.fetchItems, required this.title, + required this.list, required this.itemBuilder, - required this.fetchItems, - }) : super(key: key); + 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); @override _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); @override void initState() { - filteredItems = widget.items; + 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(() {}); + }, + ), + ); + _pagingController.addStatusListener(_showError); + super.initState(); } + Future _showError(PagingStatus status) async { + if (status == PagingStatus.subsequentPageError) { + final message = FFLocalizations.of(context).getVariableText( + enText: 'Something went wrong while fetching a new page.', + ptText: 'Algo deu errado ao buscar uma nova página.', + ); + final retry = FFLocalizations.of(context).getVariableText( + enText: 'Retry', + ptText: 'Recarregar', + ); + + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(message), + action: SnackBarAction( + label: retry, + onPressed: () => _pagingController.retryLastFailedRequest(), + ), + ), + ); + } + } + void filterSearchResults(String query) async { setState(() { isLoading = true; }); - final results = await widget.fetchItems(query); - setState(() { - filteredItems = results; - isLoading = false; - }); + // final results = await widget.fetchItems(query); + // setState(() { + // filteredItems = results; + // isLoading = false; + // }); } @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text(widget.title), - ), - body: Container( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.max, - children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: TextField( - onChanged: (value) { - filterSearchResults(value); - }, - controller: editingController, - decoration: InputDecoration( - labelText: "Search", - hintText: "Search", - prefixIcon: Icon(Icons.search), - border: OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(25.0)), - ), - ), + final noDataFound = FFLocalizations.of(context).getVariableText( + ptText: "Nenhum veículo encontrado!", + enText: "No vehicle found", + ); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: TextField( + onChanged: (value) => filterSearchResults(value), + controller: editingController, + decoration: InputDecoration( + labelText: "Search", + hintText: "Search", + prefixIcon: Icon(Icons.search), + border: OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(25.0)), ), ), - Padding( - padding: const EdgeInsets.fromLTRB(15, 0, 50, 0), - child: Text('Últimos Documentos'), - ), - isLoading - ? Center(child: CircularProgressIndicator()) - : Expanded( - child: ListView.builder( - shrinkWrap: true, - itemCount: filteredItems.length, - itemBuilder: (context, index) { - return widget.itemBuilder(filteredItems[index]); - }, - ), - ), - ], + ), ), - ), + widget.header, + buildPaginatedListView( + noDataFound, + _pagingController, + (BuildContext context, dynamic item, int index) => + widget.itemBuilder(item), + ), + ], ); } } diff --git a/lib/shared/widgets/widgets.dart b/lib/shared/widgets/widgets.dart index 7b240209..1ba34d40 100644 --- a/lib/shared/widgets/widgets.dart +++ b/lib/shared/widgets/widgets.dart @@ -1,8 +1,14 @@ +import 'dart:developer'; + import 'package:auto_size_text/auto_size_text.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'; part 'page.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index 0a647c1b..455814dc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -85,6 +85,7 @@ dependencies: url_launcher_android: ^6.3.12 url_launcher_ios: ^6.3.1 url_launcher_platform_interface: 2.3.2 + infinite_scroll_pagination: ^4.1.0 # video_player: 2.8.7 # video_player_android: 2.5.0 # video_player_avfoundation: 2.6.1 From 1dc611004ac07bf4f8e3385c67abfe8c6e2d33ca Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Wed, 12 Feb 2025 17:49:55 -0300 Subject: [PATCH 03/32] 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 From ed66fc86a2b2389de1eae3f01ad8aebeb1a90c72 Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Thu, 13 Feb 2025 08:09:28 -0300 Subject: [PATCH 04/32] WIP --- integration_test/auth_test.dart | 4 ++-- .../shared_components_atoms/media_upload_button.dart | 10 +++++----- .../details_component/details_component_widget.dart | 4 ++-- ..._history_item_details_template_component_model.dart | 1 - .../regisiter_vistor_template_component_model.dart | 1 - .../view_visit_detail/view_visit_detail_model.dart | 1 - .../visitor_search_modal_template_component_model.dart | 1 - lib/features/backend/api_requests/api_calls.dart | 1 - lib/features/documents/document_item_component.dart | 2 +- lib/features/home/presentation/pages/about_system.dart | 2 ++ .../data/data_sources/locals_local_data_source.dart | 1 - .../widgets/local_profile/local_profile_widget.dart | 2 +- lib/features/menu/presentation/mappers/menu_entry.dart | 4 ++++ .../data/data_sources/profile_local_data_source.dart | 1 - lib/initialization.dart | 2 +- .../residents_on_the_property_screen.dart | 2 +- lib/pages/visits_on_the_property/model.dart | 2 +- lib/shared/extensions/string_extensions.dart | 2 +- lib/shared/utils/text_util.dart | 4 ++-- lib/shared/widgets/view/carousel_view.dart | 2 +- lib/shared/widgets/viewer/viewer.dart | 1 + 21 files changed, 25 insertions(+), 25 deletions(-) diff --git a/integration_test/auth_test.dart b/integration_test/auth_test.dart index a60a5b1f..80da3ace 100644 --- a/integration_test/auth_test.dart +++ b/integration_test/auth_test.dart @@ -36,7 +36,7 @@ class AuthenticationTest { Map concat( String username, String domain, String password) { return { - 'emailTextFormField': '${username}@${domain}.test', + 'emailTextFormField': '$username@$domain.test', 'passwordTextFormField': password, }; } @@ -144,7 +144,7 @@ class AuthenticationTest { String name, String username, String domain, String password) { return { 'nameTextFormField': name, - 'emailTextFormField': '${username}@${domain}.test', + 'emailTextFormField': '$username@$domain.test', 'passwordTextFormField': password, }; } diff --git a/lib/components/atomic_components/shared_components_atoms/media_upload_button.dart b/lib/components/atomic_components/shared_components_atoms/media_upload_button.dart index ec1c5af3..c2424e70 100644 --- a/lib/components/atomic_components/shared_components_atoms/media_upload_button.dart +++ b/lib/components/atomic_components/shared_components_atoms/media_upload_button.dart @@ -33,7 +33,7 @@ class _MediaUploadButtonUtilState extends State { @override Widget build(BuildContext context) { double limitedInputTextSize = LimitedFontSizeUtil.getInputFontSize(context); - bool _isLoading = false; + bool isLoading = false; return Builder( builder: (context) { @@ -72,7 +72,7 @@ class _MediaUploadButtonUtilState extends State { child: GestureDetector( onTap: () async { setState(() { - _isLoading = true; + isLoading = true; }); final selectedMedia = await selectMediaWithSourceBottomSheet( @@ -120,7 +120,7 @@ class _MediaUploadButtonUtilState extends State { showUploadMessage(context, message); } else { setState(() { - _isLoading = false; + isLoading = false; }); final message = FFLocalizations.of(context) .getVariableText( @@ -131,7 +131,7 @@ class _MediaUploadButtonUtilState extends State { } } else { setState(() { - _isLoading = false; + isLoading = false; }); } }, @@ -150,7 +150,7 @@ class _MediaUploadButtonUtilState extends State { mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: [ - _isLoading + isLoading ? SizedBox( width: 30.0, height: 30.0, diff --git a/lib/components/templates_components/details_component/details_component_widget.dart b/lib/components/templates_components/details_component/details_component_widget.dart index 6a4f5cec..231092f6 100644 --- a/lib/components/templates_components/details_component/details_component_widget.dart +++ b/lib/components/templates_components/details_component/details_component_widget.dart @@ -10,7 +10,7 @@ import 'package:hub/shared/utils/limited_text_size.dart'; class DetailsComponentWidget extends StatefulWidget { const DetailsComponentWidget({ - Key? key, + super.key, required this.labelsHashMap, required this.statusHashMap, this.imagePath, @@ -188,7 +188,7 @@ class _DetailsComponentWidgetState extends State { // return Text('key: $key, value: $value'); return TextFormField( readOnly: true, - initialValue: '$value', + initialValue: value, style: FlutterFlowTheme.of(context).bodyMedium.override( fontFamily: FlutterFlowTheme.of(context).bodyMediumFamily, diff --git a/lib/components/templates_components/liberation_history_item_details_template_component/liberation_history_item_details_template_component_model.dart b/lib/components/templates_components/liberation_history_item_details_template_component/liberation_history_item_details_template_component_model.dart index 0fefa607..98d1c915 100644 --- a/lib/components/templates_components/liberation_history_item_details_template_component/liberation_history_item_details_template_component_model.dart +++ b/lib/components/templates_components/liberation_history_item_details_template_component/liberation_history_item_details_template_component_model.dart @@ -32,7 +32,6 @@ class LiberationHistoryItemDetailsTemplateComponentModel (await StorageHelper().get(ProfileStorageKey.userUUID.key)) ?? ''; cliUUID = (await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? ''; - ; } @override diff --git a/lib/components/templates_components/regisiter_vistor_template_component/regisiter_vistor_template_component_model.dart b/lib/components/templates_components/regisiter_vistor_template_component/regisiter_vistor_template_component_model.dart index 1c427c65..268d9c3a 100644 --- a/lib/components/templates_components/regisiter_vistor_template_component/regisiter_vistor_template_component_model.dart +++ b/lib/components/templates_components/regisiter_vistor_template_component/regisiter_vistor_template_component_model.dart @@ -140,7 +140,6 @@ class RegisiterVistorTemplateComponentModel (await StorageHelper().get(ProfileStorageKey.userUUID.key)) ?? ''; cliUUID = (await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? ''; - ; } @override diff --git a/lib/components/templates_components/view_visit_detail/view_visit_detail_model.dart b/lib/components/templates_components/view_visit_detail/view_visit_detail_model.dart index d115ada3..b7628924 100644 --- a/lib/components/templates_components/view_visit_detail/view_visit_detail_model.dart +++ b/lib/components/templates_components/view_visit_detail/view_visit_detail_model.dart @@ -41,7 +41,6 @@ class ViewVisitDetailModel extends FlutterFlowModel { (await StorageHelper().get(ProfileStorageKey.userUUID.key)) ?? ''; cliUUID = (await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? ''; - ; } @override diff --git a/lib/components/templates_components/visitor_search_modal_template_component/visitor_search_modal_template_component_model.dart b/lib/components/templates_components/visitor_search_modal_template_component/visitor_search_modal_template_component_model.dart index 17cc8baf..34b0bfc2 100644 --- a/lib/components/templates_components/visitor_search_modal_template_component/visitor_search_modal_template_component_model.dart +++ b/lib/components/templates_components/visitor_search_modal_template_component/visitor_search_modal_template_component_model.dart @@ -43,7 +43,6 @@ class VisitorSearchModalTemplateComponentModel (await StorageHelper().get(ProfileStorageKey.userUUID.key)) ?? ''; cliUUID = (await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? ''; - ; } @override diff --git a/lib/features/backend/api_requests/api_calls.dart b/lib/features/backend/api_requests/api_calls.dart index ded72f97..2ef8b0e4 100644 --- a/lib/features/backend/api_requests/api_calls.dart +++ b/lib/features/backend/api_requests/api_calls.dart @@ -8,7 +8,6 @@ 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'; diff --git a/lib/features/documents/document_item_component.dart b/lib/features/documents/document_item_component.dart index 0228c90f..706dfb49 100644 --- a/lib/features/documents/document_item_component.dart +++ b/lib/features/documents/document_item_component.dart @@ -60,7 +60,7 @@ interface class Document extends DocumentEntity { class DocumentItem extends StatelessWidget { final Document document; - const DocumentItem({Key? key, required this.document}) : super(key: key); + const DocumentItem({super.key, required this.document}); Tooltip _buildTooltip(String text, Color color, BuildContext context, BoxConstraints constraints) { diff --git a/lib/features/home/presentation/pages/about_system.dart b/lib/features/home/presentation/pages/about_system.dart index 19b9de55..36fb77e5 100644 --- a/lib/features/home/presentation/pages/about_system.dart +++ b/lib/features/home/presentation/pages/about_system.dart @@ -12,6 +12,8 @@ import 'package:package_info_plus/package_info_plus.dart'; import 'package:url_launcher/url_launcher.dart'; class AboutSystemPage extends StatefulWidget { + const AboutSystemPage({super.key}); + @override _AboutSystemPageState createState() => _AboutSystemPageState(); } diff --git a/lib/features/local/data/data_sources/locals_local_data_source.dart b/lib/features/local/data/data_sources/locals_local_data_source.dart index c63aec38..21b439a7 100644 --- a/lib/features/local/data/data_sources/locals_local_data_source.dart +++ b/lib/features/local/data/data_sources/locals_local_data_source.dart @@ -1,6 +1,5 @@ import 'dart:developer'; -import 'package:hub/features/storage/constants/locals_constants.dart'; import 'package:hub/features/storage/index.dart'; import 'package:sqflite/sqflite.dart'; diff --git a/lib/features/local/presentation/widgets/local_profile/local_profile_widget.dart b/lib/features/local/presentation/widgets/local_profile/local_profile_widget.dart index 3cabc3e7..4215d501 100644 --- a/lib/features/local/presentation/widgets/local_profile/local_profile_widget.dart +++ b/lib/features/local/presentation/widgets/local_profile/local_profile_widget.dart @@ -255,7 +255,7 @@ class _LocalProfileComponentWidgetState ), Tooltip( message: - valueOrDefault(' ' + state.ownerName, ''), + valueOrDefault(' ${state.ownerName}', ''), child: Text( valueOrDefault( state.ownerName.length > 30 diff --git a/lib/features/menu/presentation/mappers/menu_entry.dart b/lib/features/menu/presentation/mappers/menu_entry.dart index f290b98c..a2494f88 100644 --- a/lib/features/menu/presentation/mappers/menu_entry.dart +++ b/lib/features/menu/presentation/mappers/menu_entry.dart @@ -7,9 +7,13 @@ import 'package:material_symbols_icons/symbols.dart'; enum MenuEntryType { Home, Drawer, Property } class MenuEntry implements BaseModule { + @override final String key; + @override final IconData icon; + @override final String name; + @override final String route; final List types; diff --git a/lib/features/profile/data/data_sources/profile_local_data_source.dart b/lib/features/profile/data/data_sources/profile_local_data_source.dart index ee9bb176..b1ee1258 100644 --- a/lib/features/profile/data/data_sources/profile_local_data_source.dart +++ b/lib/features/profile/data/data_sources/profile_local_data_source.dart @@ -1,6 +1,5 @@ import 'dart:developer'; -import 'package:hub/features/storage/constants/profile_constants.dart'; import 'package:hub/features/storage/index.dart'; import 'package:sqflite/sqflite.dart'; diff --git a/lib/initialization.dart b/lib/initialization.dart index 29273fef..b4aee728 100644 --- a/lib/initialization.dart +++ b/lib/initialization.dart @@ -71,7 +71,7 @@ Future _initializeSystemSettings() async { await crashlyticsInstance.setCrashlyticsCollectionEnabled(true); // if (crashlyticsInstance.isCrashlyticsCollectionEnabled) { - FlutterError.onError = await crashlyticsInstance.recordFlutterError; + FlutterError.onError = crashlyticsInstance.recordFlutterError; print('Crashlytics enabled'); // } } diff --git a/lib/pages/residents_on_the_property/residents_on_the_property_screen.dart b/lib/pages/residents_on_the_property/residents_on_the_property_screen.dart index 515d556e..74d3f8df 100644 --- a/lib/pages/residents_on_the_property/residents_on_the_property_screen.dart +++ b/lib/pages/residents_on_the_property/residents_on_the_property_screen.dart @@ -29,7 +29,7 @@ class _ResidentsOnThePropertyState extends State late final ResidentsOnThePropertyModel model; late Future _future; - List _wrap = []; + final List _wrap = []; @override void initState() { diff --git a/lib/pages/visits_on_the_property/model.dart b/lib/pages/visits_on_the_property/model.dart index fec4e8a1..449f4177 100644 --- a/lib/pages/visits_on_the_property/model.dart +++ b/lib/pages/visits_on_the_property/model.dart @@ -82,7 +82,7 @@ class VisitsModel extends FlutterFlowModel { ), }), imagePath: - 'https://freaccess.com.br/freaccess/getImage.php?devUUID=${devUUID}&cliID=${cliUUID}&atividade=getFoto&Documento=${item['VDO_DOCUMENTO']}&tipo=E', + 'https://freaccess.com.br/freaccess/getImage.php?devUUID=$devUUID&cliID=$cliUUID&atividade=getFoto&Documento=${item['VDO_DOCUMENTO']}&tipo=E', statusHashMap: [ Map.from({ if (item['VTA_FIXA'] != null) diff --git a/lib/shared/extensions/string_extensions.dart b/lib/shared/extensions/string_extensions.dart index 8381f55b..d8d36c65 100644 --- a/lib/shared/extensions/string_extensions.dart +++ b/lib/shared/extensions/string_extensions.dart @@ -15,7 +15,7 @@ extension StringNullableExtensions on String? { extension StringExtensions on String { bool toBoolean() { - return this.toLowerCase() == 'true'; + return toLowerCase() == 'true'; } } diff --git a/lib/shared/utils/text_util.dart b/lib/shared/utils/text_util.dart index d2876a0b..00d40ef9 100644 --- a/lib/shared/utils/text_util.dart +++ b/lib/shared/utils/text_util.dart @@ -6,11 +6,11 @@ class TextUtil extends StatelessWidget { final TextAlign? textAlign; const TextUtil({ - Key? key, + super.key, required this.text, this.style, this.textAlign, - }) : super(key: key); + }); @override Widget build(BuildContext context) { diff --git a/lib/shared/widgets/view/carousel_view.dart b/lib/shared/widgets/view/carousel_view.dart index e00f3067..c5c1d152 100644 --- a/lib/shared/widgets/view/carousel_view.dart +++ b/lib/shared/widgets/view/carousel_view.dart @@ -18,7 +18,7 @@ class CategoryCarousel extends StatelessWidget { height: 120, child: CarouselView( itemExtent: 100, - onTap: (index) => filter(categories[index]!), + onTap: (index) => filter(categories[index] as T), children: categories.map((category) { category as Category?; return ColoredBox( diff --git a/lib/shared/widgets/viewer/viewer.dart b/lib/shared/widgets/viewer/viewer.dart index 21be7ccc..018c8d34 100644 --- a/lib/shared/widgets/viewer/viewer.dart +++ b/lib/shared/widgets/viewer/viewer.dart @@ -22,6 +22,7 @@ class FREViewerPDF extends StatelessComponent { final PDFViewerKey search; const FREViewerPDF({ + super.key, required this.search, required this.src, }); From 263013930e9349c72856b430dfa24dbed1f11923 Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Thu, 13 Feb 2025 16:00:28 -0300 Subject: [PATCH 05/32] WIP --- .../backend/api_requests/api_calls.dart | 18 +++ .../documents/archive_item_component.dart | 3 + .../documents/category_item_component.dart | 68 ++++++++ .../documents/document_item_component.dart | 57 +++---- .../documents/document_manager_screen.dart | 25 ++- .../documents/document_page_bloc.dart | 147 ++++++++++++++++++ .../documents/document_page_model.dart | 140 ++++++++++------- .../documents/document_page_widget.dart | 50 +++--- .../documents/document_viewer_screen.dart | 49 +++--- lib/features/documents/index.dart | 4 + lib/flutter_flow/nav/nav.dart | 7 +- lib/shared/mixins/pegeable_mixin.dart | 29 ++-- lib/shared/widgets/component.dart | 2 + lib/shared/widgets/page.dart | 19 ++- lib/shared/widgets/screen.dart | 4 + lib/shared/widgets/view/carousel_view.dart | 63 +++----- .../view/{search_view.dart => list_view.dart} | 55 ++++--- lib/shared/widgets/widgets.dart | 2 +- 18 files changed, 499 insertions(+), 243 deletions(-) create mode 100644 lib/features/documents/archive_item_component.dart create mode 100644 lib/features/documents/category_item_component.dart create mode 100644 lib/features/documents/document_page_bloc.dart rename lib/shared/widgets/view/{search_view.dart => list_view.dart} (88%) diff --git a/lib/features/backend/api_requests/api_calls.dart b/lib/features/backend/api_requests/api_calls.dart index 2ef8b0e4..87424040 100644 --- a/lib/features/backend/api_requests/api_calls.dart +++ b/lib/features/backend/api_requests/api_calls.dart @@ -5,6 +5,7 @@ import 'dart:convert'; import 'dart:developer'; import 'package:flutter/foundation.dart'; +import 'package:http/http.dart'; import 'package:hub/features/documents/index.dart' as doc; import 'package:hub/features/notification/index.dart'; import 'package:hub/features/storage/index.dart'; @@ -79,6 +80,23 @@ class FreAccessWSGlobal extends Api { static GetDocuments getDocuments = GetDocuments(); } +class GetPDF extends Endpoint { + Future call(final int id) async { + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); + final String devUUID = + (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; + final String userUUID = + (await StorageHelper().get(ProfileStorageKey.userUUID.key)) ?? ''; + final String cliUUID = + (await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? ''; + const String atividade = 'visualizarDocumento'; + const String callname = 'getDocumento.php'; + + return Uri.parse( + "$baseUrl/$callname?devUUID=$devUUID&userUUID=$userUUID&cliID=$cliUUID&atividade=$atividade&documentId=$id"); + } +} + class GetCategories extends Endpoint { @override Future call() async { diff --git a/lib/features/documents/archive_item_component.dart b/lib/features/documents/archive_item_component.dart new file mode 100644 index 00000000..0063e562 --- /dev/null +++ b/lib/features/documents/archive_item_component.dart @@ -0,0 +1,3 @@ +part of 'index.dart'; + +abstract interface class Archive extends Entity {} diff --git a/lib/features/documents/category_item_component.dart b/lib/features/documents/category_item_component.dart new file mode 100644 index 00000000..84659d09 --- /dev/null +++ b/lib/features/documents/category_item_component.dart @@ -0,0 +1,68 @@ +part of 'index.dart'; + +interface class Category extends Archive { + 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; +} + +class CategoryItem extends StatelessComponent { + final Category category; + + const CategoryItem({ + super.key, + required this.category, + }); + + @override + Widget build(BuildContext context) { + final backgroundTheme = FlutterFlowTheme.of(context).primaryBackground; + return ColoredBox( + color: backgroundTheme, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + Container( + padding: const EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: category.color, + shape: BoxShape.circle, + ), + child: Icon( + Icons.folder, + color: Colors.white, + size: 40, + ), + ), + const SizedBox(height: 8), + Text( + category.title, + style: TextStyle( + color: category.color, + fontWeight: FontWeight.bold, + ), + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + ); + } +} diff --git a/lib/features/documents/document_item_component.dart b/lib/features/documents/document_item_component.dart index 706dfb49..5545cf20 100644 --- a/lib/features/documents/document_item_component.dart +++ b/lib/features/documents/document_item_component.dart @@ -1,30 +1,6 @@ part of 'index.dart'; -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 DocumentEntity { +interface class Document extends Archive { final int id; final String description; final String type; @@ -57,10 +33,16 @@ interface class Document extends DocumentEntity { ); } -class DocumentItem extends StatelessWidget { +// ignore: must_be_immutable +class DocumentItem extends StatelessComponent { final Document document; + void Function(Document, BuildContext) onPressed; - const DocumentItem({super.key, required this.document}); + DocumentItem({ + super.key, + required this.document, + required this.onPressed, + }); Tooltip _buildTooltip(String text, Color color, BuildContext context, BoxConstraints constraints) { @@ -106,16 +88,6 @@ 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), @@ -126,7 +98,7 @@ class DocumentItem extends StatelessWidget { : MediaQuery.of(context).size.height * 2; return InkWell( - onTap: onTap, + onTap: () => onPressed(document, context), enableFeedback: true, overlayColor: WidgetStateProperty.all(primaryColor), borderRadius: BorderRadius.circular(10), @@ -189,4 +161,13 @@ class DocumentItem extends StatelessWidget { ), ); } + + DocumentItem copyWith({ + Document? document, + }) { + return DocumentItem( + document: document ?? this.document, + onPressed: onPressed, + ); + } } diff --git a/lib/features/documents/document_manager_screen.dart b/lib/features/documents/document_manager_screen.dart index 354a0825..3374cb1b 100644 --- a/lib/features/documents/document_manager_screen.dart +++ b/lib/features/documents/document_manager_screen.dart @@ -2,24 +2,41 @@ part of 'index.dart'; class DocumentManagerScreen extends StatelessScreen { final DocumentPageModel model; + final DocumentPageState state; const DocumentManagerScreen({ super.key, required this.model, + required this.state, }); @override Widget build(BuildContext context) { - final SizedBox space = SizedBox(height: 30); + final String title = FFLocalizations.of(context).getVariableText( + enText: 'Documents', + ptText: 'Documentos', + ); + final theme = FlutterFlowTheme.of(context); + action() => Navigator.pop(context); + return Scaffold( + backgroundColor: theme.primaryBackground, + appBar: buildAppBar(title, context, action), + body: buildBody(context), + ); + } + + Widget buildBody(BuildContext context) { + final SizedBox space = SizedBox(height: 30); return Column( children: [ Expanded( - child: RemoteSearchView( - key: model.searchKey, + child: EnhancedRemoteListView( + key: model.managerKey, pagingController: model._pagingController, headerBuilder: model.listHeaderBuilder, - bodyBuilder: model.listBodyBuilder, + headerItems: model.generateCategories, + bodyBuilder: model.documentItemBuilder, dataProvider: model.generateDocuments, onFetchError: model.onFetchError, ), diff --git a/lib/features/documents/document_page_bloc.dart b/lib/features/documents/document_page_bloc.dart new file mode 100644 index 00000000..5128ee03 --- /dev/null +++ b/lib/features/documents/document_page_bloc.dart @@ -0,0 +1,147 @@ +part of 'index.dart'; + +/// ----------------------------------------------- +/// [DocumentPageBloc] +/// ----------------------------------------------- + +class DocumentPageBloc extends Bloc { + final DocumentPageModel model; + + DocumentPageBloc._(this.model, DocumentPageState initialState) + : super(initialState) { + on(_selectDocument); + on(_unselectDocument); + on(_selectCategory); + on(_unselectCategory); + } + + static DocumentPageBloc create(DocumentPageModel model) { + final initialState = DocumentPageState( + categories: [], + documents: [], + ); + return DocumentPageBloc._(model, initialState); + } + + Future _selectCategory( + SelectCategoryEvent event, Emitter emit) async { + print('select: ${event.query}'); + final docs = await model.generateDocuments(state.page, event.query); + final bool isSelected = !state.isCategorySelected; + + emit(state.copyWith( + isCategorySelected: isSelected, + documents: isSelected ? docs.$2 : state.documents, + )); + } + + Future _unselectCategory( + UnselectCategoryEvent event, Emitter emit) async { + emit(state); + } + + Future _selectDocument( + SelectDocumentEvent event, Emitter emit) async { + print('-> select'); + emit(state.copyWith( + uri: await GetPDF().call(event.document.id), + currentDocument: event.document, + isDocumentSelected: true, + )); + } + + Future _unselectDocument( + UnselectDocumentEvent event, Emitter emit) async { + final docs = await model.generateDocuments(state.page, state.query); + final cats = await model.generateCategories(); + + emit( + state.copyWith( + currentDocument: null, + isDocumentSelected: false, + documents: docs.$2, + categories: cats, + ), + ); + } +} + +/// ----------------------------------------------- +/// [DocumentPageEvent] +/// ----------------------------------------------- + +abstract class DocumentPageEvent {} + +class SelectDocumentEvent extends DocumentPageEvent { + final Document document; + SelectDocumentEvent( + this.document, + ); +} + +class UnselectDocumentEvent extends DocumentPageEvent {} + +class UnselectCategoryEvent extends DocumentPageEvent {} + +class SelectCategoryEvent extends DocumentPageEvent { + final Query query; + SelectCategoryEvent(this.query); +} + +/// ----------------------------------------------- +/// [DocumentPageState] +/// ----------------------------------------------- + +class DocumentPageState { + final bool isCategorySelected; + final bool isDocumentSelected; + final Document? currentDocument; + final Category? currentCategory; + final Uri? uri; + final int? count; + final dynamic page; + final Query? query; + final List documents; + final List categories; + + const DocumentPageState({ + this.query, + this.count, + this.page, + this.uri, + required this.documents, + this.currentDocument, + this.isCategorySelected = false, + required this.categories, + this.currentCategory, + this.isDocumentSelected = false, + }); + + DocumentPageState copyWith({ + Uri? uri, + Query? query, + int? count, + dynamic page, + List? documents, + Document? currentDocument, + bool? isDocumentSelected, + List? categories, + Category? currentCategory, + bool? isCategorySelected, + }) { + return DocumentPageState( + uri: uri ?? this.uri, + query: query ?? this.query, + count: count ?? this.count, + page: page ?? this.page, + // + documents: documents ?? this.documents, + currentDocument: currentDocument ?? this.currentDocument, + isDocumentSelected: isDocumentSelected ?? this.isDocumentSelected, + // + categories: categories ?? this.categories, + currentCategory: currentCategory ?? this.currentCategory, + isCategorySelected: isCategorySelected ?? this.isCategorySelected, + ); + } +} diff --git a/lib/features/documents/document_page_model.dart b/lib/features/documents/document_page_model.dart index 4439deae..5c883e1b 100644 --- a/lib/features/documents/document_page_model.dart +++ b/lib/features/documents/document_page_model.dart @@ -1,59 +1,80 @@ part of 'index.dart'; class DocumentPageModel extends FlutterFlowModel { - @override - void dispose() {} + DocumentPageModel(); + + late final GlobalKey> pageKey; + late final SearchKey managerKey; + late final DocumentKey viewerKey; + late final PagingController _pagingController; + + /// ------------ @override - void initState(BuildContext context) {} + void initState(BuildContext context) { + pageKey = GlobalKey>(); + managerKey = SearchKey(); + viewerKey = DocumentKey(); - final SearchKey searchKey = SearchKey(); - final DocumentKey docKey = DocumentKey(); + _pagingController = PagingController(firstPageKey: 1); + } - final PagingController _pagingController = - PagingController(firstPageKey: 1); - int count = 0; - final dynamic page = 1; + @override + void dispose() { + _pagingController.dispose(); + // isCategorySelected = false; + // isDocumentSelected = false; + } - Query query = Document.fromDesc(''); + /// ------------ - late Document currentDocument; - bool isCategorySelected = false; - late Category currentCategory; + /// [onView] + void onView(Document document, BuildContext context) async { + context.read().add(SelectDocumentEvent(document)); + } - List documents = []; - List categories = []; + /// [documentItemBuilder] + DocumentItem documentItemBuilder( + BuildContext context, T item, int index) { + return DocumentItem( + document: item, + onPressed: onView, + ); + } - /// [listBodyBuilder] - Widget listBodyBuilder(BuildContext context, Document item, int index) { - return DocumentItem(document: item); + CategoryItem categoryItemBuilder(T? item) { + return CategoryItem(category: item! as Category); } /// [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), + Widget listHeaderBuilder(Future> Function() gen) => + Builder(builder: (context) { + return 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, - ), - ], - ); + EnhancedCarouselView( + generateItems: gen, + itemBuilder: categoryItemBuilder, + filter: filter, + ), + ], + ); + }); /// [generateDocuments] - Future<(bool, List)> generateDocuments( + Future<(bool, List?)> generateDocuments( pageKey, Query query) async { final List error = [null]; print('Query: ${query is Document}'); @@ -103,9 +124,8 @@ class DocumentPageModel extends FlutterFlowModel { } /// [generateCategories] - Future> generateCategories(List documents) async { + Future> generateCategories() async { final List error = [null]; - if (documents == []) return error; final GetCategories getCategories = FreAccessWSGlobal.getCategories; final ApiCallResponse newItems = await getCategories.call(); @@ -130,26 +150,32 @@ class DocumentPageModel extends FlutterFlowModel { 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; - } - }); - } + /// [filter] + void filter(T query, BuildContext context) { + context + .read() + .add(SelectCategoryEvent(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] void onFetchError(Object e, StackTrace s) { - DialogUtil.errorDefault(docKey.currentContext!); + DialogUtil.errorDefault(viewerKey.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 ec558fda..ae7b00e2 100644 --- a/lib/features/documents/document_page_widget.dart +++ b/lib/features/documents/document_page_widget.dart @@ -11,44 +11,34 @@ class DocumentPage extends StatefulPage { class FREDocumentPageState extends PageState { - DocumentPageModel model = DocumentPageModel(); - @override - Widget build(BuildContext context) { - 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), - ); - } + Widget build(BuildContext context) => buildBody(context); + DocumentPageModel model = DocumentPageModel(); @override void initState() { super.initState(); + model.initState(context); } Widget buildBody(BuildContext context) { - return FutureBuilder( - future: initAsync(), - builder: (context, snapshot) { - return DocumentManagerScreen(model: model); - }, - ); - // return DocumentViewScreen(document: documents.first); - } + return BlocProvider( + create: (context) => DocumentPageBloc.create(model), + child: BlocBuilder( + builder: (context, state) { + print('Bloc -> ${state.isCategorySelected}'); - 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'); + if (state.isDocumentSelected) + return DocumentViewScreen( + doc: state.currentDocument!, + uri: state.uri!, + ); + else + return DocumentManagerScreen( + model: model, + state: state, + ); + }), + ); } } diff --git a/lib/features/documents/document_viewer_screen.dart b/lib/features/documents/document_viewer_screen.dart index 7b9b704e..526b02cc 100644 --- a/lib/features/documents/document_viewer_screen.dart +++ b/lib/features/documents/document_viewer_screen.dart @@ -3,43 +3,52 @@ part of 'index.dart'; class DocumentViewScreen extends StatefulScreen { const DocumentViewScreen({ super.key, - required this.document, + required this.doc, + required this.uri, }); - final Document document; + final Document doc; + final Uri uri; @override - State createState() => _DocumentViewScreenState(); + ScreenState createState() => _DocumentViewScreenState(); } -class _DocumentViewScreenState extends State { +class _DocumentViewScreenState extends ScreenState { final PDFViewerKey _viewerKey = PDFViewerKey(); + void onShare() async { + final response = await http.get(widget.uri); + if (response.statusCode == 200) { + final XFile xfile = XFile.fromData(response.bodyBytes, + name: '${widget.doc.description}.pdf', mimeType: 'application/pdf'); + await Share.shareXFiles([xfile], text: 'Confira este PDF!'); + } else { + print('Erro ao baixar o arquivo: ${response.statusCode}'); + } + } + @override Widget build(BuildContext context) { - final Uri url = Uri.parse( - 'https://cdn.syncfusion.com/content/PDFViewer/flutter-succinctly.pdf'); + action() => context.read().add(UnselectDocumentEvent()); - 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}'); - } - } + final String title = widget.doc.description; + final theme = FlutterFlowTheme.of(context); + return Scaffold( + backgroundColor: theme.primaryBackground, + appBar: buildAppBar(title, context, action), + body: buildBody(context), + ); + } + Widget buildBody(BuildContext context) { return Stack( children: [ Padding( padding: EdgeInsets.all(10), child: FREViewerPDF( search: _viewerKey, - src: url.toString(), + src: widget.uri.toString(), ), ), Positioned( @@ -51,7 +60,7 @@ class _DocumentViewScreenState extends State { color: Colors.black, ), color: Colors.black, - onPressed: onPressed, + onPressed: onShare, ), ), ], diff --git a/lib/features/documents/index.dart b/lib/features/documents/index.dart index 864743e1..c5441c4d 100644 --- a/lib/features/documents/index.dart +++ b/lib/features/documents/index.dart @@ -2,6 +2,7 @@ import 'dart:developer'; import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:http/http.dart' as http; import 'package:hub/features/backend/index.dart'; @@ -19,3 +20,6 @@ part 'document_page_widget.dart'; part 'document_viewer_screen.dart'; part 'document_page_model.dart'; part 'document_item_component.dart'; +part 'document_page_bloc.dart'; +part 'category_item_component.dart'; +part 'archive_item_component.dart'; diff --git a/lib/flutter_flow/nav/nav.dart b/lib/flutter_flow/nav/nav.dart index 357346f8..42b6e6de 100644 --- a/lib/flutter_flow/nav/nav.dart +++ b/lib/flutter_flow/nav/nav.dart @@ -310,11 +310,12 @@ GoRouter createRouter(AppStateNotifier appStateNotifier) { name: 'documentViewerScreen', path: '/documentViewerScreen', builder: (context, params) { - final Document document = - params.getParam('document', ParamType.Function); + final Document doc = params.getParam('doc', ParamType.Function); + final Uri uri = params.getParam('uri', ParamType.Function); return DocumentViewScreen( key: UniqueKey(), - document: document, + doc: doc, + uri: uri, ); }, ), diff --git a/lib/shared/mixins/pegeable_mixin.dart b/lib/shared/mixins/pegeable_mixin.dart index e54db8f5..95b60b71 100644 --- a/lib/shared/mixins/pegeable_mixin.dart +++ b/lib/shared/mixins/pegeable_mixin.dart @@ -6,25 +6,30 @@ import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; extension PagedListViewExtension on PagedSliverList {} +typedef PaginatedListViewHeaderBuilder = Widget Function( + Future> Function() gen); +typedef PaginatedListViewBodyBuilder = Widget Function(BuildContext, T, int); + mixin Pageable on State { - Expanded buildPaginatedListView( + Expanded buildPaginatedListView( String noDataFound, - PagingController pg, - Widget Function(BuildContext) headerBuilder, - Widget Function(BuildContext, ItemType, int) bodyBuilder) { + PagingController pg, + Future> Function() headerItems, + PaginatedListViewHeaderBuilder headerBuilder, + PaginatedListViewBodyBuilder 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, int index) { return Column(children: [ - if (index == 0) headerBuilder(context), + if (index == 0) headerBuilder(headerItems), bodyBuilder(context, item, index), ]); }, @@ -33,7 +38,8 @@ mixin Pageable on State { firstPageProgressIndicatorBuilder: (context) => buildLoadingIndicator(context), noItemsFoundIndicatorBuilder: (context) => - buildNoDataFound(context, noDataFound, headerBuilder), + buildNoDataFound( + context, noDataFound, headerItems, headerBuilder), firstPageErrorIndicatorBuilder: (context) => const Placeholder(), newPageErrorIndicatorBuilder: (context) => const Placeholder(), ), @@ -92,16 +98,17 @@ mixin Pageable on State { showSnackbar(context, message, true); } - Widget buildNoDataFound( + Widget buildNoDataFound( BuildContext context, String title, - Widget Function(BuildContext) headerBuilder, + Future> Function() items, + Widget Function(Future> Function() items) headerBuilder, ) { final headerFontSize = LimitedFontSizeUtil.getHeaderFontSize(context); // final bodyFontSize = LimitedFontSizeUtil.getBodyFontSize(context); return Column( children: [ - headerBuilder(context), + headerBuilder(items), Expanded( child: Center( child: Text( diff --git a/lib/shared/widgets/component.dart b/lib/shared/widgets/component.dart index fb1383dc..74fc9ae1 100644 --- a/lib/shared/widgets/component.dart +++ b/lib/shared/widgets/component.dart @@ -20,3 +20,5 @@ abstract class StatefulComponent extends StatefulWidget implements ComponentWidget { const StatefulComponent({super.key}); } + +abstract class ComponentState extends State {} diff --git a/lib/shared/widgets/page.dart b/lib/shared/widgets/page.dart index ed08ad58..87059a12 100644 --- a/lib/shared/widgets/page.dart +++ b/lib/shared/widgets/page.dart @@ -1,7 +1,12 @@ part of 'widgets.dart'; -mixin MixinPage { - PreferredSizeWidget buildAppBar(String title, BuildContext context) { +mixin Template { + PreferredSizeWidget buildAppBar( + String title, + BuildContext context, + dynamic Function()? backAction, + ) { + final theme = FlutterFlowTheme.of(context); return AppBar( backgroundColor: FlutterFlowTheme.of(context).primaryBackground, automaticallyImplyLeading: false, @@ -17,14 +22,15 @@ mixin MixinPage { FlutterFlowTheme.of(context).headlineMediumFamily), ), ), - leading: _backButton(context, FlutterFlowTheme.of(context)), + leading: _backButton(context, theme, backAction), centerTitle: true, elevation: 0.0, actions: [], ); } - Widget _backButton(BuildContext context, FlutterFlowTheme theme) { + Widget _backButton(BuildContext context, FlutterFlowTheme theme, + dynamic Function()? onPressed) { return FlutterFlowIconButton( key: ValueKey('BackNavigationAppBar'), borderColor: Colors.transparent, @@ -36,7 +42,7 @@ mixin MixinPage { color: theme.primaryText, size: 30.0, ), - onPressed: () => Navigator.of(context).pop(), + onPressed: onPressed, ); } } @@ -52,6 +58,7 @@ abstract class ModelPage extends ModelWidget implements PageWidget { } abstract class StatelessPage extends StatelessWidget + with Template implements PageWidget { const StatelessPage({super.key}); } @@ -61,4 +68,4 @@ abstract class StatefulPage extends StatefulWidget implements PageWidget { } abstract class PageState extends State - with MixinPage {} + with Template {} diff --git a/lib/shared/widgets/screen.dart b/lib/shared/widgets/screen.dart index 754a3094..f4dd20c8 100644 --- a/lib/shared/widgets/screen.dart +++ b/lib/shared/widgets/screen.dart @@ -9,6 +9,7 @@ abstract class ModelScreen extends ModelWidget implements ScreenWidget { } abstract class StatelessScreen extends StatelessWidget + with Template implements ScreenWidget { const StatelessScreen({super.key}); } @@ -17,3 +18,6 @@ abstract class StatefulScreen extends StatefulWidget implements ScreenWidget { const StatefulScreen({super.key}); } + +abstract class ScreenState extends State + with Template {} diff --git a/lib/shared/widgets/view/carousel_view.dart b/lib/shared/widgets/view/carousel_view.dart index c5c1d152..402a2567 100644 --- a/lib/shared/widgets/view/carousel_view.dart +++ b/lib/shared/widgets/view/carousel_view.dart @@ -1,59 +1,32 @@ part of '../widgets.dart'; -class CategoryCarousel extends StatelessWidget { - final List categories; - final void Function(T) filter; +class EnhancedCarouselView extends StatelessWidget { + final Future> Function() generateItems; + final void Function(T, BuildContext) filter; + final Widget Function(T? item) itemBuilder; - const CategoryCarousel({ + const EnhancedCarouselView({ super.key, - required this.categories, + required this.generateItems, required this.filter, + required this.itemBuilder, }); @override Widget build(BuildContext context) { - final backgroundTheme = FlutterFlowTheme.of(context).primaryBackground; - return SizedBox( height: 120, - child: CarouselView( - itemExtent: 100, - onTap: (index) => filter(categories[index] as T), - children: categories.map((category) { - category as Category?; - return ColoredBox( - color: backgroundTheme, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - Container( - padding: const EdgeInsets.all(8.0), - decoration: BoxDecoration( - color: category!.color, - shape: BoxShape.circle, - ), - child: Icon( - Icons.folder, - color: Colors.white, - size: 40, - ), - ), - const SizedBox(height: 8), - Text( - category.title, - style: TextStyle( - color: category.color, - fontWeight: FontWeight.bold, - ), - overflow: TextOverflow.ellipsis, - ), - ], - ), - ), - ); - }).toList(), - ), + child: FutureBuilder>( + future: generateItems(), + builder: (context, snapshot) { + if (!snapshot.hasData) return SizedBox(); + return CarouselView( + itemExtent: 100, + onTap: (index) => filter(snapshot.data![index] as T, context), + children: + snapshot.data!.map((item) => itemBuilder(item)).toList(), + ); + }), ); } } diff --git a/lib/shared/widgets/view/search_view.dart b/lib/shared/widgets/view/list_view.dart similarity index 88% rename from lib/shared/widgets/view/search_view.dart rename to lib/shared/widgets/view/list_view.dart index b22eb7fe..17a72c6a 100644 --- a/lib/shared/widgets/view/search_view.dart +++ b/lib/shared/widgets/view/list_view.dart @@ -1,39 +1,32 @@ part of '../widgets.dart'; -typedef SearchKey = GlobalKey; +typedef SearchKey = GlobalKey; -typedef Query = X?; +typedef Query = X?; /// ----------------------------------------------- -/// [SearchView] +/// [EnhancedListView] /// ----------------------------------------------- -class SearchView extends StatefulComponent { - const SearchView({super.key}); - - @override - State createState() => _SearchViewState(); +abstract interface class EnhancedListView extends StatefulWidget { + const EnhancedListView({super.key}); } -class _SearchViewState extends State { - @override - Widget build(BuildContext context) { - return const Placeholder(); - } -} +abstract interface class EnhancedListViewState + extends State {} /// ----------------------------------------------- -/// [LocalSearchView] +/// [EnhancedLocalListView] /// ----------------------------------------------- -class LocalSearchView extends SearchView { +class EnhancedLocalListView extends EnhancedListView { final List list; final Widget Function(T) itemBuilder; final bool Function(T, String) filter; final Widget header; final List Function(String)? onSearch; - LocalSearchView({ + EnhancedLocalListView({ Key? key, required this.list, required this.itemBuilder, @@ -49,10 +42,11 @@ class LocalSearchView extends SearchView { // return documents.where((documents) => filter(documents, query)).toList(); @override - LocalSearchViewState createState() => LocalSearchViewState(); + EnhancedLocalListViewState createState() => + EnhancedLocalListViewState(); } -class LocalSearchViewState extends State> { +class EnhancedLocalListViewState extends State> { TextEditingController editingController = TextEditingController(); late List filteredItems; @@ -141,22 +135,25 @@ class LocalSearchViewState extends State> { } /// ----------------------------------------------- -/// [RemoteSearchView] +/// [EnhancedRemoteListView] /// ----------------------------------------------- -class RemoteSearchView extends SearchView { +// ignore: must_be_immutable +class EnhancedRemoteListView extends EnhancedListView { final Widget Function(BuildContext, T, int) bodyBuilder; - Widget Function(BuildContext) headerBuilder; + final Future> Function() headerItems; + Widget Function(Future> Function() gen) headerBuilder; final PagingController pagingController; - final Future<(bool, List)> Function(int pageKey, Query query) + final Future<(bool, List?)> Function(int pageKey, Query query) dataProvider; final void Function(Object, StackTrace) onFetchError; - RemoteSearchView({ + EnhancedRemoteListView({ Key? key, // required this.fetchItems, required this.bodyBuilder, + required this.headerItems, required this.headerBuilder, required this.pagingController, required this.dataProvider, @@ -164,11 +161,12 @@ class RemoteSearchView extends SearchView { }) : super(key: key); @override - RemoteSearchViewState createState() => RemoteSearchViewState(); + EnhancedRemoteListViewState createState() => + EnhancedRemoteListViewState(); } -class RemoteSearchViewState extends State> - with Pageable { +class EnhancedRemoteListViewState + extends State> with Pageable { TextEditingController editingController = TextEditingController(); bool isLoading = false; Query query = Document.fromDesc(''); @@ -246,9 +244,10 @@ class RemoteSearchViewState extends State> mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.max, children: [ - buildPaginatedListView( + buildPaginatedListView( noDataFound, widget.pagingController, + widget.headerItems, widget.headerBuilder, widget.bodyBuilder, ), diff --git a/lib/shared/widgets/widgets.dart b/lib/shared/widgets/widgets.dart index c930bc37..f56fce44 100644 --- a/lib/shared/widgets/widgets.dart +++ b/lib/shared/widgets/widgets.dart @@ -17,7 +17,7 @@ part 'model.dart'; part 'entity.dart'; /// [View]'s -part 'view/search_view.dart'; +part 'view/list_view.dart'; part 'view/carousel_view.dart'; /// [Viewer] From 2bb3312a39e8c6f21cd9f30ff6eb3d67a31cae75 Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Fri, 14 Feb 2025 12:40:35 -0300 Subject: [PATCH 06/32] impl pdfViewer release --- devtools_options.yaml | 3 +- flutter_01.png | Bin 0 -> 74416 bytes .../documents/document_viewer_screen.dart | 53 +++----- lib/features/documents/index.dart | 1 - lib/shared/widgets/viewer/viewer.dart | 114 +++++++++++++++--- lib/shared/widgets/widgets.dart | 7 +- pubspec.yaml | 4 +- 7 files changed, 124 insertions(+), 58 deletions(-) create mode 100644 flutter_01.png diff --git a/devtools_options.yaml b/devtools_options.yaml index 2bc8e05f..a18a3e2e 100644 --- a/devtools_options.yaml +++ b/devtools_options.yaml @@ -1,4 +1,5 @@ description: This file stores settings for Dart & Flutter DevTools. documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states extensions: - - provider: true \ No newline at end of file + - provider: true + - patrol: true \ No newline at end of file diff --git a/flutter_01.png b/flutter_01.png new file mode 100644 index 0000000000000000000000000000000000000000..99ac4d5a8b9de87a451fda87f92a3cf3c0db6c7b GIT binary patch literal 74416 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 literal 0 HcmV?d00001 diff --git a/lib/features/documents/document_viewer_screen.dart b/lib/features/documents/document_viewer_screen.dart index 526b02cc..da3ce95e 100644 --- a/lib/features/documents/document_viewer_screen.dart +++ b/lib/features/documents/document_viewer_screen.dart @@ -15,55 +15,30 @@ class DocumentViewScreen extends StatefulScreen { } class _DocumentViewScreenState extends ScreenState { - final PDFViewerKey _viewerKey = PDFViewerKey(); - - void onShare() async { - final response = await http.get(widget.uri); - if (response.statusCode == 200) { - final XFile xfile = XFile.fromData(response.bodyBytes, - name: '${widget.doc.description}.pdf', mimeType: 'application/pdf'); - await Share.shareXFiles([xfile], text: 'Confira este PDF!'); - } else { - print('Erro ao baixar o arquivo: ${response.statusCode}'); - } - } - @override Widget build(BuildContext context) { action() => context.read().add(UnselectDocumentEvent()); final String title = widget.doc.description; final theme = FlutterFlowTheme.of(context); - return Scaffold( - backgroundColor: theme.primaryBackground, - appBar: buildAppBar(title, context, action), - body: buildBody(context), + return PopScope( + canPop: false, + onPopInvokedWithResult: (didPop, result) => action(), + child: Scaffold( + backgroundColor: theme.primaryBackground, + appBar: buildAppBar(title, context, action), + body: buildBody(context), + ), ); } Widget buildBody(BuildContext context) { - return Stack( - children: [ - Padding( - padding: EdgeInsets.all(10), - child: FREViewerPDF( - search: _viewerKey, - src: widget.uri.toString(), - ), - ), - Positioned( - bottom: 10, - right: 10, - child: IconButton( - icon: Icon( - Icons.share, - color: Colors.black, - ), - color: Colors.black, - onPressed: onShare, - ), - ), - ], + // final PDFViewerKey _viewerKey = PDFViewerKey(); + + return PDFViewer( + // search: _viewerKey, + title: widget.doc.description, + url: widget.uri.toString(), ); } } diff --git a/lib/features/documents/index.dart b/lib/features/documents/index.dart index c5441c4d..49d30dfc 100644 --- a/lib/features/documents/index.dart +++ b/lib/features/documents/index.dart @@ -12,7 +12,6 @@ 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'; diff --git a/lib/shared/widgets/viewer/viewer.dart b/lib/shared/widgets/viewer/viewer.dart index 018c8d34..94861cd1 100644 --- a/lib/shared/widgets/viewer/viewer.dart +++ b/lib/shared/widgets/viewer/viewer.dart @@ -1,13 +1,14 @@ part of '../widgets.dart'; -typedef PDFViewerKey = GlobalKey; +// typedef PDFViewerKey = GlobalKey; abstract interface class Viewer extends StatelessComponent { + final String src; + const Viewer({ super.key, required this.src, }); - final String src; @override Widget build(BuildContext context) { @@ -17,25 +18,110 @@ abstract interface class Viewer extends StatelessComponent { Widget buildViewer(BuildContext context); } -class FREViewerPDF extends StatelessComponent { - final String src; - final PDFViewerKey search; +class PDFViewer extends StatefulWidget { + final String url; + final String title; - const FREViewerPDF({ + const PDFViewer({ super.key, - required this.search, - required this.src, + required this.url, + required this.title, }); @override - Widget build(BuildContext context) { - return buildViewer(context); + State createState() => PDFViewerState(); +} + +class PDFViewerState extends State { + late PdfController _pdfController; + + Future _initializePdf() async { + final file = await downloadPdf(widget.url); + final Future document = PdfDocument.openFile(file.path); + return PdfController(document: document); } - Widget buildViewer(BuildContext context) { - return SfPdfViewer.network( - src, - key: search, + Future downloadPdf(String url) async { + final response = await http.get(Uri.parse(url)); + if (response.statusCode == 200) { + final bytes = response.bodyBytes; + final dir = await getTemporaryDirectory(); + final file = File('${dir.path}/downloaded.pdf'); + await file.writeAsBytes(bytes); + return file; + } else { + throw Exception('Falha ao baixar o PDF'); + } + } + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + _buildPDFViewer(), + buildShareButton(), + ], ); } + + Positioned buildShareButton() { + return Positioned( + bottom: 10, + right: 10, + child: IconButton( + icon: Icon( + Icons.share, + color: Colors.black, + ), + color: Colors.black, + onPressed: onShare, + ), + ); + } + + void onShare() async { + final Uri uri = Uri.parse(widget.url); + final response = await http.get(uri); + if (response.statusCode == 200) { + final XFile xfile = XFile.fromData(response.bodyBytes, + name: '${widget.title}.pdf', mimeType: 'application/pdf'); + await Share.shareXFiles([xfile], text: 'Confira este PDF!'); + } else { + print('Erro ao baixar o arquivo: ${response.statusCode}'); + } + } + + Widget buildLoadingIndicator(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 15), + child: Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + FlutterFlowTheme.of(context).primary, + ), + ), + ), + ); + } + + Widget _buildPDFViewer() => Padding( + padding: EdgeInsets.all(10), + child: FutureBuilder( + future: _initializePdf(), + builder: (context, snapshot) { + if (!snapshot.hasData) return buildLoadingIndicator(context); + return PdfView( + controller: snapshot.data!, + scrollDirection: Axis.vertical, + ); + }), + ); + + // Widget get progressIndicator => LoadingUtil.buildLoadingIndicator(context); + + @override + void dispose() { + _pdfController.dispose(); + super.dispose(); + } } diff --git a/lib/shared/widgets/widgets.dart b/lib/shared/widgets/widgets.dart index f56fce44..ba41d526 100644 --- a/lib/shared/widgets/widgets.dart +++ b/lib/shared/widgets/widgets.dart @@ -1,14 +1,19 @@ import 'dart:developer'; +import 'dart:io'; 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:http/http.dart' as http; 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'; +import 'package:path_provider/path_provider.dart'; +import 'package:pdfx/pdfx.dart'; +import 'package:share_plus/share_plus.dart'; part 'page.dart'; part 'component.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index d6098524..799c3b86 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,8 +13,8 @@ dependencies: sdk: flutter flutter_localizations: sdk: flutter + pdfx: ^2.8.0 auto_size_text: ^3.0.0 - syncfusion_flutter_pdfviewer: ^28.2.4 barcode_widget: ^2.0.4 cached_network_image: ^3.4.0 firebase_core: ^3.4.0 @@ -25,7 +25,7 @@ dependencies: app_links: ^6.3.2 # crop_your_image: 1.1.0 csv: 6.0.0 - device_info_plus: ^11.2.2 + device_info_plus: ^10.1.2 #11.2.2 firebase_messaging: ^15.1.0 dropdown_button2: 2.3.9 easy_debounce: 2.0.3 From 44ddafcb0cdb0f8449549d6e1e93af42cbb4eb28 Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Fri, 14 Feb 2025 14:18:56 -0300 Subject: [PATCH 07/32] wip - filter category --- .../documents/document_page_bloc.dart | 52 ++++++++++++------- .../documents/document_page_model.dart | 2 +- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/lib/features/documents/document_page_bloc.dart b/lib/features/documents/document_page_bloc.dart index 5128ee03..235718de 100644 --- a/lib/features/documents/document_page_bloc.dart +++ b/lib/features/documents/document_page_bloc.dart @@ -11,8 +11,7 @@ class DocumentPageBloc extends Bloc { : super(initialState) { on(_selectDocument); on(_unselectDocument); - on(_selectCategory); - on(_unselectCategory); + on(_filterCategoryEvent); } static DocumentPageBloc create(DocumentPageModel model) { @@ -23,31 +22,46 @@ class DocumentPageBloc extends Bloc { return DocumentPageBloc._(model, initialState); } - Future _selectCategory( - SelectCategoryEvent event, Emitter emit) async { - print('select: ${event.query}'); - final docs = await model.generateDocuments(state.page, event.query); - final bool isSelected = !state.isCategorySelected; + Future _filterCategoryEvent( + FilterCategoryEvent event, Emitter emit) async { + state.isCategorySelected + ? _unselectCategory(event, emit) + : _selectCategory(event, emit); + } + Future _selectCategory( + FilterCategoryEvent event, Emitter emit) async { emit(state.copyWith( - isCategorySelected: isSelected, - documents: isSelected ? docs.$2 : state.documents, + isCategorySelected: true, )); + + final s = model.managerKey.currentState!; + state.isCategorySelected ? s.filter(event.query) : s.filter(event.query); } Future _unselectCategory( - UnselectCategoryEvent event, Emitter emit) async { - emit(state); + FilterCategoryEvent event, Emitter emit) async { + emit(state.copyWith( + isCategorySelected: false, + )); + + final s = model.managerKey.currentState!; + final Query q = Document.fromDesc(''); + s.filter(q); } Future _selectDocument( SelectDocumentEvent event, Emitter emit) async { print('-> select'); - emit(state.copyWith( - uri: await GetPDF().call(event.document.id), - currentDocument: event.document, - isDocumentSelected: true, - )); + emit( + state.copyWith( + uri: await GetPDF().call( + event.document.id, + ), + currentDocument: event.document, + isDocumentSelected: true, + ), + ); } Future _unselectDocument( @@ -81,11 +95,9 @@ class SelectDocumentEvent extends DocumentPageEvent { class UnselectDocumentEvent extends DocumentPageEvent {} -class UnselectCategoryEvent extends DocumentPageEvent {} - -class SelectCategoryEvent extends DocumentPageEvent { +class FilterCategoryEvent extends DocumentPageEvent { final Query query; - SelectCategoryEvent(this.query); + FilterCategoryEvent(this.query); } /// ----------------------------------------------- diff --git a/lib/features/documents/document_page_model.dart b/lib/features/documents/document_page_model.dart index 5c883e1b..43c2a435 100644 --- a/lib/features/documents/document_page_model.dart +++ b/lib/features/documents/document_page_model.dart @@ -154,7 +154,7 @@ class DocumentPageModel extends FlutterFlowModel { void filter(T query, BuildContext context) { context .read() - .add(SelectCategoryEvent(query as Archive?)); + .add(FilterCategoryEvent(query as Archive?)); } // { // log('filterByCategories: '); From defe7f3401e8eddb90111bb6619ffed0c0393e7a Mon Sep 17 00:00:00 2001 From: Daniel Yukio Date: Fri, 14 Feb 2025 15:21:31 -0300 Subject: [PATCH 08/32] feat: some changes --- .../documents/document_item_component.dart | 10 +++++++--- .../documents/document_page_model.dart | 3 ++- .../menu/presentation/mappers/menu_entry.dart | 20 +++++++++---------- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/lib/features/documents/document_item_component.dart b/lib/features/documents/document_item_component.dart index 5545cf20..29b91a96 100644 --- a/lib/features/documents/document_item_component.dart +++ b/lib/features/documents/document_item_component.dart @@ -48,8 +48,12 @@ class DocumentItem extends StatelessComponent { BoxConstraints constraints) { final Color textColor = FlutterFlowTheme.of(context).info; - final double boxHeight = MediaQuery.of(context).size.height * 0.02; - final double boxWidth = MediaQuery.of(context).size.height * 0.1; + final area = (MediaQuery.of(context).size.height + + MediaQuery.of(context).size.width) / + 2; + + final double boxHeight = area * 0.033; + final double boxWidth = area * 0.19; return Tooltip( message: text, @@ -109,7 +113,7 @@ class DocumentItem extends StatelessComponent { children: [ // const SizedBox(width: 10), Icon(Icons.description, color: document.category.color), - // const SizedBox(width: 10), + const SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/features/documents/document_page_model.dart b/lib/features/documents/document_page_model.dart index 43c2a435..f8e3d25b 100644 --- a/lib/features/documents/document_page_model.dart +++ b/lib/features/documents/document_page_model.dart @@ -57,7 +57,8 @@ class DocumentPageModel extends FlutterFlowModel { Padding( padding: const EdgeInsets.fromLTRB(15, 0, 50, 0), child: Text( - 'Últimos Documentos', + FFLocalizations.of(context).getVariableText( + enText: 'Recent Documents', ptText: 'Últimos Documentos'), style: TextStyle( color: FlutterFlowTheme.of(context).primaryText, fontSize: LimitedFontSizeUtil.getHeaderFontSize(context), diff --git a/lib/features/menu/presentation/mappers/menu_entry.dart b/lib/features/menu/presentation/mappers/menu_entry.dart index a2494f88..ad09f824 100644 --- a/lib/features/menu/presentation/mappers/menu_entry.dart +++ b/lib/features/menu/presentation/mappers/menu_entry.dart @@ -164,16 +164,6 @@ class MenuEntry implements BaseModule { route: '/acessHistoryPage', types: [MenuEntryType.Home, MenuEntryType.Drawer], ), - MenuEntry( - key: 'FRE-HUB-DOCUMENT', - icon: Icons.document_scanner, - name: FFLocalizations.of(navigatorKey.currentContext!).getVariableText( - ptText: 'Documentos', - enText: 'Documents', - ), - route: '/documentPage', - types: [MenuEntryType.Home, MenuEntryType.Drawer], - ), MenuEntry( key: 'FRE-HUB-LIBERATIONS', icon: Icons.how_to_reg_outlined, @@ -204,6 +194,16 @@ class MenuEntry implements BaseModule { route: '/messageHistoryPage', types: [MenuEntryType.Home, MenuEntryType.Drawer], ), + MenuEntry( + key: 'FRE-HUB-DOCUMENT', + icon: Icons.description, + name: FFLocalizations.of(navigatorKey.currentContext!).getVariableText( + ptText: 'Documentos', + enText: 'Documents', + ), + route: '/documentPage', + types: [MenuEntryType.Home, MenuEntryType.Drawer], + ), MenuEntry( key: 'FRE-HUB-ABOUT-PROPERTY', icon: Icons.home, From 65a70cd8de6db0f6895eaecde0affc689f3c95db Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Mon, 17 Feb 2025 09:57:46 -0300 Subject: [PATCH 09/32] WIP --- .vscode/launch.json | 15 ++++++++++++--- .../documents/document_page_bloc.dart | 19 +++++-------------- .../documents/document_page_model.dart | 1 + ...reen.dart => document_screen_manager.dart} | 0 ...creen.dart => document_screen_viewer.dart} | 6 ++++-- lib/features/documents/index.dart | 4 ++-- .../widgets/{view => }/carousel_view.dart | 0 lib/shared/widgets/{view => }/list_view.dart | 0 .../{viewer/viewer.dart => read_view.dart} | 10 +++++----- lib/shared/widgets/widgets.dart | 8 +++----- 10 files changed, 32 insertions(+), 31 deletions(-) rename lib/features/documents/{document_manager_screen.dart => document_screen_manager.dart} (100%) rename lib/features/documents/{document_viewer_screen.dart => document_screen_viewer.dart} (90%) rename lib/shared/widgets/{view => }/carousel_view.dart (100%) rename lib/shared/widgets/{view => }/list_view.dart (100%) rename lib/shared/widgets/{viewer/viewer.dart => read_view.dart} (93%) diff --git a/.vscode/launch.json b/.vscode/launch.json index fb1433dc..6635646f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,20 +9,29 @@ "request": "launch", "type": "dart", "args": [ - "--no-enable-impeller" + "--no-enable-impeller", + " --enable-experiment=macros" ] }, { "name": "flutter-freaccesss-hub (profile mode)", "request": "launch", "type": "dart", - "flutterMode": "profile" + "flutterMode": "profile", + "args": [ + "--no-enable-impeller", + " --enable-experiment=macros" + ] }, { "name": "flutter-freaccesss-hub (release mode)", "request": "launch", "type": "dart", - "flutterMode": "release" + "flutterMode": "release", + "args": [ + "--no-enable-impeller", + " --enable-experiment=macros" + ] } ] } \ No newline at end of file diff --git a/lib/features/documents/document_page_bloc.dart b/lib/features/documents/document_page_bloc.dart index 235718de..3a20209f 100644 --- a/lib/features/documents/document_page_bloc.dart +++ b/lib/features/documents/document_page_bloc.dart @@ -15,10 +15,7 @@ class DocumentPageBloc extends Bloc { } static DocumentPageBloc create(DocumentPageModel model) { - final initialState = DocumentPageState( - categories: [], - documents: [], - ); + final initialState = DocumentPageState(); return DocumentPageBloc._(model, initialState); } @@ -66,15 +63,15 @@ class DocumentPageBloc extends Bloc { Future _unselectDocument( UnselectDocumentEvent event, Emitter emit) async { - final docs = await model.generateDocuments(state.page, state.query); - final cats = await model.generateCategories(); + // final docs = await model.generateDocuments(state.page, state.query); + // final cats = await model.generateCategories(); emit( state.copyWith( currentDocument: null, isDocumentSelected: false, - documents: docs.$2, - categories: cats, + // documents: docs.$2, + // categories: cats, ), ); } @@ -113,18 +110,14 @@ class DocumentPageState { final int? count; final dynamic page; final Query? query; - final List documents; - final List categories; const DocumentPageState({ this.query, this.count, this.page, this.uri, - required this.documents, this.currentDocument, this.isCategorySelected = false, - required this.categories, this.currentCategory, this.isDocumentSelected = false, }); @@ -147,11 +140,9 @@ class DocumentPageState { count: count ?? this.count, page: page ?? this.page, // - documents: documents ?? this.documents, currentDocument: currentDocument ?? this.currentDocument, isDocumentSelected: isDocumentSelected ?? this.isDocumentSelected, // - categories: categories ?? this.categories, currentCategory: currentCategory ?? this.currentCategory, isCategorySelected: isCategorySelected ?? this.isCategorySelected, ); diff --git a/lib/features/documents/document_page_model.dart b/lib/features/documents/document_page_model.dart index f8e3d25b..482bfb3d 100644 --- a/lib/features/documents/document_page_model.dart +++ b/lib/features/documents/document_page_model.dart @@ -36,6 +36,7 @@ class DocumentPageModel extends FlutterFlowModel { /// [documentItemBuilder] DocumentItem documentItemBuilder( BuildContext context, T item, int index) { + print('ItemBuilder -> $index'); return DocumentItem( document: item, onPressed: onView, diff --git a/lib/features/documents/document_manager_screen.dart b/lib/features/documents/document_screen_manager.dart similarity index 100% rename from lib/features/documents/document_manager_screen.dart rename to lib/features/documents/document_screen_manager.dart diff --git a/lib/features/documents/document_viewer_screen.dart b/lib/features/documents/document_screen_viewer.dart similarity index 90% rename from lib/features/documents/document_viewer_screen.dart rename to lib/features/documents/document_screen_viewer.dart index da3ce95e..b54bfa55 100644 --- a/lib/features/documents/document_viewer_screen.dart +++ b/lib/features/documents/document_screen_viewer.dart @@ -17,7 +17,9 @@ class DocumentViewScreen extends StatefulScreen { class _DocumentViewScreenState extends ScreenState { @override Widget build(BuildContext context) { - action() => context.read().add(UnselectDocumentEvent()); + action() { + context.read().add(UnselectDocumentEvent()); + } final String title = widget.doc.description; final theme = FlutterFlowTheme.of(context); @@ -35,7 +37,7 @@ class _DocumentViewScreenState extends ScreenState { Widget buildBody(BuildContext context) { // final PDFViewerKey _viewerKey = PDFViewerKey(); - return PDFViewer( + return ReadView( // search: _viewerKey, title: widget.doc.description, url: widget.uri.toString(), diff --git a/lib/features/documents/index.dart b/lib/features/documents/index.dart index 49d30dfc..d3648a6d 100644 --- a/lib/features/documents/index.dart +++ b/lib/features/documents/index.dart @@ -14,9 +14,9 @@ import 'package:hub/shared/widgets/widgets.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:share_plus/share_plus.dart'; -part 'document_manager_screen.dart'; part 'document_page_widget.dart'; -part 'document_viewer_screen.dart'; +part 'document_screen_manager.dart'; +part 'document_screen_viewer.dart'; part 'document_page_model.dart'; part 'document_item_component.dart'; part 'document_page_bloc.dart'; diff --git a/lib/shared/widgets/view/carousel_view.dart b/lib/shared/widgets/carousel_view.dart similarity index 100% rename from lib/shared/widgets/view/carousel_view.dart rename to lib/shared/widgets/carousel_view.dart diff --git a/lib/shared/widgets/view/list_view.dart b/lib/shared/widgets/list_view.dart similarity index 100% rename from lib/shared/widgets/view/list_view.dart rename to lib/shared/widgets/list_view.dart diff --git a/lib/shared/widgets/viewer/viewer.dart b/lib/shared/widgets/read_view.dart similarity index 93% rename from lib/shared/widgets/viewer/viewer.dart rename to lib/shared/widgets/read_view.dart index 94861cd1..0688906c 100644 --- a/lib/shared/widgets/viewer/viewer.dart +++ b/lib/shared/widgets/read_view.dart @@ -1,4 +1,4 @@ -part of '../widgets.dart'; +part of 'widgets.dart'; // typedef PDFViewerKey = GlobalKey; @@ -18,21 +18,21 @@ abstract interface class Viewer extends StatelessComponent { Widget buildViewer(BuildContext context); } -class PDFViewer extends StatefulWidget { +class ReadView extends StatefulWidget { final String url; final String title; - const PDFViewer({ + const ReadView({ super.key, required this.url, required this.title, }); @override - State createState() => PDFViewerState(); + State createState() => ReadViewState(); } -class PDFViewerState extends State { +class ReadViewState extends State { late PdfController _pdfController; Future _initializePdf() async { diff --git a/lib/shared/widgets/widgets.dart b/lib/shared/widgets/widgets.dart index ba41d526..0edd1805 100644 --- a/lib/shared/widgets/widgets.dart +++ b/lib/shared/widgets/widgets.dart @@ -22,10 +22,8 @@ part 'model.dart'; part 'entity.dart'; /// [View]'s -part 'view/list_view.dart'; -part 'view/carousel_view.dart'; - -/// [Viewer] -part 'viewer/viewer.dart'; +part 'list_view.dart'; +part 'carousel_view.dart'; +part 'read_view.dart'; part 'text.dart'; From 2487801ee1f9bf08b113e6917724cf40c35ab5d2 Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Mon, 17 Feb 2025 11:55:18 -0300 Subject: [PATCH 10/32] WIP --- .../documents/document_page_bloc.dart | 16 +- .../documents/document_page_model.dart | 120 ++-- .../documents/document_screen_manager.dart | 16 +- lib/shared/widgets/carousel_view.dart | 2 +- lib/shared/widgets/enhanced_list_view.dart | 637 ++++++++++++++++++ lib/shared/widgets/list_view.dart | 594 ++++++++-------- lib/shared/widgets/widgets.dart | 7 +- pubspec.yaml | 154 +++-- 8 files changed, 1108 insertions(+), 438 deletions(-) create mode 100644 lib/shared/widgets/enhanced_list_view.dart diff --git a/lib/features/documents/document_page_bloc.dart b/lib/features/documents/document_page_bloc.dart index 3a20209f..a0d75b62 100644 --- a/lib/features/documents/document_page_bloc.dart +++ b/lib/features/documents/document_page_bloc.dart @@ -21,6 +21,7 @@ class DocumentPageBloc extends Bloc { Future _filterCategoryEvent( FilterCategoryEvent event, Emitter emit) async { + log('generateDocuments -> ${state.isCategorySelected}'); state.isCategorySelected ? _unselectCategory(event, emit) : _selectCategory(event, emit); @@ -32,8 +33,12 @@ class DocumentPageBloc extends Bloc { isCategorySelected: true, )); - final s = model.managerKey.currentState!; - state.isCategorySelected ? s.filter(event.query) : s.filter(event.query); + final listViewState = model.enhancedListViewKey.currentState!; + listViewState.safeSetState(() async { + return await listViewState.filterItems(event.query); + }); + + // state.isCategorySelected ? s.filter(event.query) : s.filter(event.query); } Future _unselectCategory( @@ -42,9 +47,10 @@ class DocumentPageBloc extends Bloc { isCategorySelected: false, )); - final s = model.managerKey.currentState!; - final Query q = Document.fromDesc(''); - s.filter(q); + final listViewState = model.enhancedListViewKey.currentState!; + listViewState.safeSetState(() async { + return await listViewState.filterItems(null); + }); } Future _selectDocument( diff --git a/lib/features/documents/document_page_model.dart b/lib/features/documents/document_page_model.dart index 482bfb3d..8501e39b 100644 --- a/lib/features/documents/document_page_model.dart +++ b/lib/features/documents/document_page_model.dart @@ -4,7 +4,8 @@ class DocumentPageModel extends FlutterFlowModel { DocumentPageModel(); late final GlobalKey> pageKey; - late final SearchKey managerKey; + late final EnhancedRemoteListViewKey + enhancedListViewKey; late final DocumentKey viewerKey; late final PagingController _pagingController; @@ -13,7 +14,7 @@ class DocumentPageModel extends FlutterFlowModel { @override void initState(BuildContext context) { pageKey = GlobalKey>(); - managerKey = SearchKey(); + enhancedListViewKey = EnhancedRemoteListViewKey(); viewerKey = DocumentKey(); _pagingController = PagingController(firstPageKey: 1); @@ -33,8 +34,8 @@ class DocumentPageModel extends FlutterFlowModel { context.read().add(SelectDocumentEvent(document)); } - /// [documentItemBuilder] - DocumentItem documentItemBuilder( + /// [itemBodyBuilder] + DocumentItem itemBodyBuilder( BuildContext context, T item, int index) { print('ItemBuilder -> $index'); return DocumentItem( @@ -47,8 +48,8 @@ class DocumentPageModel extends FlutterFlowModel { return CategoryItem(category: item! as Category); } - /// [listHeaderBuilder] - Widget listHeaderBuilder(Future> Function() gen) => + /// [itemHeaderBuilder] + Widget itemHeaderBuilder(Future> Function() gen) => Builder(builder: (context) { return Column( mainAxisSize: MainAxisSize.max, @@ -75,81 +76,64 @@ class DocumentPageModel extends FlutterFlowModel { ); }); - /// [generateDocuments] - Future<(bool, List?)> generateDocuments( - pageKey, Query query) async { - final List error = [null]; + /// [generateBodyItems] + Future> generateBodyItems( + int pageKey, int pageSize, dynamic query) async { + log('generateDocuments: $query'); + + 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); + if (newItems.jsonBody == null || newItems.jsonBody['error'] == true) { + return error; } - return (true, docs); - // listViewKey.currentState!.count = newItems.jsonBody['value']['count'] ?? 0; + final List list = newItems.jsonBody['value']['list']; + final List docs = list.map((item) { + log('-> generateDocuments: $item'); + return Document( + id: item['id'], + description: item['description'], + type: item['type'], + category: Category( + id: item['category']['id'], + color: item['category']['color'].toColor(), + title: item['category']['description'], + ), + person: item['person'] ?? '', + property: item['property'] ?? '', + createdAt: item['createdAt'], + updatedAt: item['updatedAt'], + ); + }).toList(); + + return docs as List; } - /// [generateCategories] - Future> generateCategories() async { - final List error = [null]; + /// [generateHeaderItems] + Future> generateHeaderItems() async { + final List error = [null]; 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); + if (newItems.jsonBody == null || newItems.jsonBody['error'] == true) { + return error; } - log('cats: $cats'); - return cats; + + final List list = newItems.jsonBody['value']; + final List categories = list.map((item) { + return Category( + id: item['id'], + color: item['color'].toColor(), + title: item['description'], + ); + }).toList(); + + log('categories: $categories'); + return categories as List; } /// [filter] diff --git a/lib/features/documents/document_screen_manager.dart b/lib/features/documents/document_screen_manager.dart index 3374cb1b..1db102dd 100644 --- a/lib/features/documents/document_screen_manager.dart +++ b/lib/features/documents/document_screen_manager.dart @@ -31,14 +31,14 @@ class DocumentManagerScreen extends StatelessScreen { return Column( children: [ Expanded( - child: EnhancedRemoteListView( - key: model.managerKey, - pagingController: model._pagingController, - headerBuilder: model.listHeaderBuilder, - headerItems: model.generateCategories, - bodyBuilder: model.documentItemBuilder, - dataProvider: model.generateDocuments, - onFetchError: model.onFetchError, + child: EnhancedListView.remote( + key: model.enhancedListViewKey, + headerBuilder: model.itemHeaderBuilder, + headerItems: model.generateHeaderItems, + bodyBuilder: model.itemBodyBuilder, + bodyItems: model.generateBodyItems, + footerBuilder: null, + footerItems: null, ), ), ] // diff --git a/lib/shared/widgets/carousel_view.dart b/lib/shared/widgets/carousel_view.dart index 402a2567..cce63a45 100644 --- a/lib/shared/widgets/carousel_view.dart +++ b/lib/shared/widgets/carousel_view.dart @@ -1,4 +1,4 @@ -part of '../widgets.dart'; +part of 'widgets.dart'; class EnhancedCarouselView extends StatelessWidget { final Future> Function() generateItems; diff --git a/lib/shared/widgets/enhanced_list_view.dart b/lib/shared/widgets/enhanced_list_view.dart new file mode 100644 index 00000000..5ba1828e --- /dev/null +++ b/lib/shared/widgets/enhanced_list_view.dart @@ -0,0 +1,637 @@ +part of 'widgets.dart'; + +/// [TypeDefs] + +typedef EnhancedRemoteListViewKey + = GlobalKey>; +typedef EnhancedLocalListViewKey + = GlobalKey>; + +typedef PaginatedListViewHeaderBuilder = Widget Function( + Future> Function() headerItems); +typedef PaginatedListViewBodyBuilder = Widget Function( + BuildContext context, T item, int index); +typedef PaginatedListViewFooterBuilder = Widget Function( + Future> Function() footerItems); + +typedef Query = T?; + +typedef BodyItemsBuilder = Future> Function( + int page, int pageSize, Query query); +typedef HeaderItemsBuilder = Future> Function(); +typedef FooterItemsBuilder = Future> Function(); + +/// [Extensions] +extension PaginatedListMergeExtensions + on Stream>> { + Stream> mergeWithPaginatedList( + BehaviorSubject> currentList) { + return map( + (result) { + final current = currentList.value; + if (result is ResultSuccess>) { + final newPaginated = result.data; + return current.items.isEmpty + ? newPaginated + : current.copyWith( + list: [...current.items, ...newPaginated.items], + currentPage: newPaginated.currentPage, + totalCount: newPaginated.totalCount, + error: newPaginated.error, + ); + } else if (result is ResultError>) { + return current.copyWith(error: result.error); + } else { + return current; + } + }, + ); + } +} + +extension PublishSubjectExtensions on PublishSubject { + Stream startWith(T initial) => Rx.concat([Stream.value(initial), this]); +} + +extension StreamStartWithExtension on Stream { + Stream startWith(T initial) => Rx.concat([Stream.value(initial), this]); +} + +extension QueryBlocStreamExtensions on Stream { + Stream>> fetchData( + EnhancedListViewRepository repository, + PaginatedListViewBodyBuilder builder, + BehaviorSubject> paginatedList, + ) => + switchMap((reset) { + if (reset) { + paginatedList.add(paginatedList.value.resetAll()); + } + final nextPage = paginatedList.value.currentPage + 1; + return repository + .fetchPage(nextPage, paginatedList.value.pageSize, builder) + .asResultStream(); + }); +} + +/// [Widgets] + +/// [EnhancedListView] + +interface class EnhancedPaginatedList extends PaginatedList { + @override + final Exception? error; + final bool isInitialized; + @override + final bool isLoading; + final List items; + @override + final int pageSize; + @override + final int? totalCount; + final int currentPage; + + EnhancedPaginatedList({ + required this.items, + required this.pageSize, + this.currentPage = 0, + this.totalCount, + required this.error, + required this.isInitialized, + required this.isLoading, + }) : super( + error: error, + isInitialized: isInitialized, + isLoading: isLoading, + list: items, + pageSize: pageSize, + totalCount: totalCount, + ); + + EnhancedPaginatedList resetAll() => EnhancedPaginatedList( + error: null, + isInitialized: false, + isLoading: false, + items: const [], + pageSize: pageSize, + totalCount: null, + currentPage: 0, + ); + + @override + EnhancedPaginatedList copyWith({ + List? list, + bool? isLoading, + int? totalCount, + Exception? error, + int? pageSize, + bool? isInitialized, + int? currentPage, + }) => + EnhancedPaginatedList( + error: error ?? this.error, + isInitialized: isInitialized ?? this.isInitialized, + isLoading: isLoading ?? this.isLoading, + items: list ?? this.items, + pageSize: pageSize ?? this.pageSize, + totalCount: totalCount ?? this.totalCount, + currentPage: currentPage ?? this.currentPage, + ); + + @override + int get itemCount => items.length; + + @override + T? getItem(int index) => index < items.length ? items[index] : null; + + Future awaitLoad() async => Future.value(); +} + +abstract interface class EnhancedListViewBase extends StatefulWidget { + const EnhancedListViewBase({super.key}); +} + +abstract interface class EnhancedListViewBaseState + extends State {} + +class EnhancedListView { + static EnhancedRemoteListView + remote({ + required Key? key, + required BodyItemsBuilder bodyItems, + required PaginatedListViewBodyBuilder bodyBuilder, + HeaderItemsBuilder? headerItems, + PaginatedListViewHeaderBuilder? headerBuilder, + FooterItemsBuilder? footerItems, + PaginatedListViewFooterBuilder? footerBuilder, + }) { + return EnhancedRemoteListView( + key: key, + bodyItems: bodyItems, + bodyBuilder: bodyBuilder, + headerItems: headerItems, + headerBuilder: headerBuilder, + footerItems: footerItems, + footerBuilder: footerBuilder, + ); + } + + static EnhancedLocalListView local({ + required Key? key, + required List list, + required Widget Function(T) itemBuilder, + required bool Function(T, String) filter, + List Function(String)? onSearch, + Widget? header, + }) { + return EnhancedLocalListView( + key: key, + list: list, + itemBuilder: itemBuilder, + filter: filter, + onSearch: onSearch, + header: header, + ); + } +} + +/// [EnhancedLocalListView] + +class EnhancedLocalListView extends EnhancedListViewBase { + final List list; + final Widget Function(T) itemBuilder; + final bool Function(T, String) filter; + final Widget header; + final List Function(String)? onSearch; + + EnhancedLocalListView({ + Key? key, + 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); + + @override + EnhancedLocalListViewState createState() => + EnhancedLocalListViewState(); +} + +class EnhancedLocalListViewState + extends State> { + TextEditingController editingController = TextEditingController(); + late List 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: [ + 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 + extends EnhancedListViewBase { + final BodyItemsBuilder bodyItems; + final PaginatedListViewBodyBuilder bodyBuilder; + final HeaderItemsBuilder? headerItems; + final PaginatedListViewHeaderBuilder? headerBuilder; + final FooterItemsBuilder? footerItems; + final PaginatedListViewFooterBuilder? footerBuilder; + + const EnhancedRemoteListView({ + Key? key, + required this.bodyItems, + required this.bodyBuilder, + this.headerItems, + this.headerBuilder, + this.footerItems, + this.footerBuilder, + }) : super(key: key); + + @override + EnhancedRemoteListViewState createState() => + EnhancedRemoteListViewState(); +} + +class EnhancedRemoteListViewState + extends State> { + final ScrollController _scrollController = ScrollController(); + bool _isLoadingMore = false; + List _items = []; + int _currentPage = 1; + Query query; + + @override + void initState() { + super.initState(); + _scrollController.addListener(_onScroll); + _loadInitialItems(); + } + + void _onScroll() { + if (_scrollController.position.pixels == + _scrollController.position.maxScrollExtent && + !_isLoadingMore) { + _loadMoreItems(); + } + } + + Future _loadInitialItems() async { + final newItems = await widget.bodyItems(1, 10, query); + setState(() { + _items = newItems; + }); + } + + Future _loadMoreItems() async { + setState(() { + _isLoadingMore = true; + }); + final newItems = await widget.bodyItems(_currentPage + 1, 10, query); + setState(() { + _isLoadingMore = false; + if (newItems.isNotEmpty) { + _items.addAll(newItems); + _currentPage++; + } + }); + } + + Future filterItems(Query newQuery) async { + log('filterItems: $newQuery'); + setState(() { + query = newQuery; + _items = []; + _currentPage = 1; + }); + await _loadInitialItems(); + } + + @override + Widget build(BuildContext context) { + log('key: ${widget.key}'); + return ListView.builder( + controller: _scrollController, + itemCount: _items.length + + (widget.headerItems != null ? 1 : 0) + + (widget.footerItems != null ? 1 : 0) + + (_isLoadingMore ? 1 : 0), + itemBuilder: (context, index) { + if (widget.headerItems != null && index == 0) { + return FutureBuilder>( + 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 && + index == _items.length + (widget.headerItems != null ? 1 : 0)) { + return FutureBuilder>( + 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 && + index == + _items.length + + (widget.headerItems != null ? 1 : 0) + + (widget.footerItems != null ? 1 : 0)) { + return const EnhancedProgressIndicator(); + } + final item = _items[index - (widget.headerItems != null ? 1 : 0)]; + return widget.bodyBuilder(context, item as ItemType, index); + }, + ); + } + + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } +} + +/// [Utils] + +class EnhancedListTile extends StatelessWidget { + const EnhancedListTile( + {required this.leading, required this.title, super.key}); + + final T leading; + final T title; + + @override + Widget build(BuildContext context) { + return Card( + child: ListTile( + leading: leading, + title: title, + ), + ); + } +} + +class EnhancedErrorWidget extends StatelessWidget { + final Object? error; + const EnhancedErrorWidget({required this.error, super.key}); + + @override + Widget build(BuildContext context) { + log('error: $error'); + return Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + error.toString(), + style: const TextStyle(color: Colors.red), + ), + ); + } +} + +class EnhancedProgressIndicator extends StatelessWidget { + const EnhancedProgressIndicator({super.key}); + + @override + Widget build(BuildContext context) => const Center( + child: Padding( + padding: EdgeInsets.symmetric(vertical: 12), + child: CircularProgressIndicator(), + ), + ); +} + +class NetworkError implements Exception { + final String message; + NetworkError(this.message); +} + +class ParsingError implements Exception { + final String message; + ParsingError(this.message); +} + +class AuthorizationError implements Exception { + final String message; + AuthorizationError(this.message); +} + +/// [State Managment] +Stream get loadingState => Stream.empty(); +Stream get errorState => Stream.empty(); + +abstract class EnhancedListViewRepository { + Future> fetchPage( + int page, int pageSize, PaginatedListViewBodyBuilder builder); +} + +abstract class EnhancedListViewBlocStates { + Stream get isLoading; + Stream get errors; + Stream> get paginatedList; + + @RxBlocIgnoreState() + Future get refreshDone; +} + +abstract class EnhancedListViewEvents { + void loadPage({bool reset = false}); +} + +abstract class EnhancedListViewBlocType extends RxBlocTypeBase { + EnhancedListViewEvents get events; + EnhancedListViewBlocStates get states; +} + +abstract class $EnhancedListViewBloc extends RxBlocBase + implements + EnhancedListViewEvents, + EnhancedListViewBlocStates, + EnhancedListViewBlocType { + final _compositeSubscription = CompositeSubscription(); + + final _$loadPageEvent = PublishSubject(); + + late final Stream _isLoadingState = _mapToIsLoadingState(); + late final Stream _errorsState = _mapToErrorsState(); + late final Stream> _paginatedListState = + _mapToPaginatedListState(); + + @override + void loadPage({bool reset = false}) => _$loadPageEvent.add(reset); + + @override + Stream get isLoading => _isLoadingState; + @override + Stream get errors => _errorsState; + @override + Stream> get paginatedList => _paginatedListState; + + Stream _mapToIsLoadingState(); + Stream _mapToErrorsState(); + Stream> _mapToPaginatedListState(); + + @override + EnhancedListViewEvents get events => this; + @override + EnhancedListViewBlocStates get states => this; + + @override + void dispose() { + _$loadPageEvent.close(); + _compositeSubscription.dispose(); + super.dispose(); + } +} + +class EnhancedListViewBloc extends $EnhancedListViewBloc { + EnhancedListViewBloc({ + required EnhancedListViewRepository repository, + required PaginatedListViewBodyBuilder builder, + required T item, + int initialPageSize = 50, + }) { + _$loadPageEvent + .startWith(true) + .fetchData( + repository, + (context, item, index) => builder(context, item, index), + _paginatedList, + ) + .setResultStateHandler(this) + .mergeWithPaginatedList(_paginatedList) + .bind(_paginatedList) + .addTo(_compositeSubscription); + } + + final _paginatedList = BehaviorSubject>.seeded( + EnhancedPaginatedList( + items: [], + pageSize: 1, + currentPage: 1, + error: Exception(), + isInitialized: true, + isLoading: false, + totalCount: 0, + ), + ); + + @override + Future get refreshDone async => _paginatedList.value.awaitLoad(); + + @override + Stream> _mapToPaginatedListState() => _paginatedList; + @override + Stream _mapToErrorsState() => + errorState.map((error) => error.toString()); + @override + Stream _mapToIsLoadingState() => loadingState; + + @override + void dispose() { + _paginatedList.close(); + super.dispose(); + } +} diff --git a/lib/shared/widgets/list_view.dart b/lib/shared/widgets/list_view.dart index 17a72c6a..7304de9f 100644 --- a/lib/shared/widgets/list_view.dart +++ b/lib/shared/widgets/list_view.dart @@ -1,325 +1,325 @@ -part of '../widgets.dart'; +part of 'widgets.dart'; -typedef SearchKey = GlobalKey; +// typedef SearchKey = GlobalKey; -typedef Query = X?; +// typedef Query = X?; -/// ----------------------------------------------- -/// [EnhancedListView] -/// ----------------------------------------------- +// /// ----------------------------------------------- +// /// [EnhancedListView] +// /// ----------------------------------------------- -abstract interface class EnhancedListView extends StatefulWidget { - const EnhancedListView({super.key}); -} +// abstract interface class EnhancedListView extends StatefulWidget { +// const EnhancedListView({super.key}); +// } -abstract interface class EnhancedListViewState - extends State {} +// abstract interface class EnhancedListViewState +// extends State {} -/// ----------------------------------------------- -/// [EnhancedLocalListView] -/// ----------------------------------------------- +// /// ----------------------------------------------- +// /// [EnhancedLocalListView] +// /// ----------------------------------------------- -class EnhancedLocalListView extends EnhancedListView { - final List list; - final Widget Function(T) itemBuilder; - final bool Function(T, String) filter; - final Widget header; - final List Function(String)? onSearch; +// class EnhancedLocalListView extends EnhancedListView { +// final List list; +// final Widget Function(T) itemBuilder; +// final bool Function(T, String) filter; +// final Widget header; +// final List Function(String)? onSearch; - EnhancedLocalListView({ - Key? key, - 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); +// EnhancedLocalListView({ +// Key? key, +// 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); - // return documents.where((documents) => filter(documents, query)).toList(); +// // return documents.where((documents) => filter(documents, query)).toList(); - @override - EnhancedLocalListViewState createState() => - EnhancedLocalListViewState(); -} +// @override +// EnhancedLocalListViewState createState() => +// EnhancedLocalListViewState(); +// } -class EnhancedLocalListViewState extends State> { - TextEditingController editingController = TextEditingController(); - late List filteredItems; +// class EnhancedLocalListViewState extends State> { +// TextEditingController editingController = TextEditingController(); +// late List filteredItems; - @override - void initState() { - filteredItems = widget.list; - super.initState(); - } +// @override +// void initState() { +// filteredItems = widget.list; +// super.initState(); +// } - @override - Widget build(BuildContext context) { - void filter(value) { - safeSetState(() { - filteredItems = widget.onSearch!(value); - }); - } +// @override +// Widget build(BuildContext context) { +// void filter(value) { +// safeSetState(() { +// filteredItems = widget.onSearch!(value); +// }); +// } - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.max, - children: [ - 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), - ), - ), - )), - ], - ); - } -} +// return Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// mainAxisAlignment: MainAxisAlignment.center, +// mainAxisSize: MainAxisSize.max, +// children: [ +// 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] -/// ----------------------------------------------- +// /// ----------------------------------------------- +// /// [EnhancedRemoteListView] +// /// ----------------------------------------------- -// ignore: must_be_immutable -class EnhancedRemoteListView extends EnhancedListView { - final Widget Function(BuildContext, T, int) bodyBuilder; - final Future> Function() headerItems; - Widget Function(Future> Function() gen) headerBuilder; - final PagingController pagingController; - final Future<(bool, List?)> Function(int pageKey, Query query) - dataProvider; +// // ignore: must_be_immutable +// class EnhancedRemoteListView extends EnhancedListView { +// final Widget Function(BuildContext, T, int) bodyBuilder; +// final Future> Function() headerItems; +// Widget Function(Future> Function() gen) headerBuilder; +// final PagingController pagingController; +// final Future<(bool, List?)> Function(int pageKey, Query query) +// dataProvider; - final void Function(Object, StackTrace) onFetchError; +// final void Function(Object, StackTrace) onFetchError; - EnhancedRemoteListView({ - Key? key, - // required this.fetchItems, - required this.bodyBuilder, - required this.headerItems, - required this.headerBuilder, - required this.pagingController, - required this.dataProvider, - required this.onFetchError, - }) : super(key: key); +// EnhancedRemoteListView({ +// Key? key, +// // required this.fetchItems, +// required this.bodyBuilder, +// required this.headerItems, +// required this.headerBuilder, +// required this.pagingController, +// required this.dataProvider, +// required this.onFetchError, +// }) : super(key: key); - @override - EnhancedRemoteListViewState createState() => - EnhancedRemoteListViewState(); -} +// @override +// EnhancedRemoteListViewState createState() => +// EnhancedRemoteListViewState(); +// } -class EnhancedRemoteListViewState - extends State> with Pageable { - TextEditingController editingController = TextEditingController(); - bool isLoading = false; - Query query = Document.fromDesc(''); +// class EnhancedRemoteListViewState +// extends State> with Pageable { +// TextEditingController editingController = TextEditingController(); +// bool isLoading = false; +// Query query = Document.fromDesc(''); - @override - void initState() { - 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), - ); - widget.pagingController.addStatusListener(_showError); +// @override +// void initState() { +// 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), +// ); +// widget.pagingController.addStatusListener(_showError); - super.initState(); - } +// super.initState(); +// } - Future _showError(PagingStatus status) async { - if (status == PagingStatus.subsequentPageError) { - final message = FFLocalizations.of(context).getVariableText( - enText: 'Something went wrong while fetching a new page.', - ptText: 'Algo deu errado ao buscar uma nova página.', - ); - final retry = FFLocalizations.of(context).getVariableText( - enText: 'Retry', - ptText: 'Recarregar', - ); +// Future _showError(PagingStatus status) async { +// if (status == PagingStatus.subsequentPageError) { +// final message = FFLocalizations.of(context).getVariableText( +// enText: 'Something went wrong while fetching a new page.', +// ptText: 'Algo deu errado ao buscar uma nova página.', +// ); +// final retry = FFLocalizations.of(context).getVariableText( +// enText: 'Retry', +// ptText: 'Recarregar', +// ); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(message), - action: SnackBarAction( - label: retry, - onPressed: () => widget.pagingController.retryLastFailedRequest(), - ), - ), - ); - } - } +// ScaffoldMessenger.of(context).showSnackBar( +// SnackBar( +// content: Text(message), +// action: SnackBarAction( +// label: retry, +// onPressed: () => widget.pagingController.retryLastFailedRequest(), +// ), +// ), +// ); +// } +// } - 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}'); +// 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(); - } - } +// 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 item encontrado!", - enText: "No item found", - ); - final theme = FlutterFlowTheme.of(context); - final locale = FFLocalizations.of(context); +// @override +// Widget build(BuildContext context) { +// final noDataFound = FFLocalizations.of(context).getVariableText( +// 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.headerItems, - widget.headerBuilder, - widget.bodyBuilder, - ), - Padding( - padding: const EdgeInsets.all(8.0), - 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( - 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), - ), - ), - ), - ), - ], - ); - } -} +// return Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// mainAxisAlignment: MainAxisAlignment.center, +// mainAxisSize: MainAxisSize.max, +// children: [ +// buildPaginatedListView( +// noDataFound, +// widget.pagingController, +// widget.headerItems, +// widget.headerBuilder, +// widget.bodyBuilder, +// ), +// Padding( +// padding: const EdgeInsets.all(8.0), +// 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( +// 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), +// ), +// ), +// ), +// ), +// ], +// ); +// } +// } diff --git a/lib/shared/widgets/widgets.dart b/lib/shared/widgets/widgets.dart index 0edd1805..c09aeccf 100644 --- a/lib/shared/widgets/widgets.dart +++ b/lib/shared/widgets/widgets.dart @@ -9,12 +9,15 @@ import 'package:http/http.dart' as http; 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:path_provider/path_provider.dart'; import 'package:pdfx/pdfx.dart'; import 'package:share_plus/share_plus.dart'; +import 'package:rx_bloc_list/rx_bloc_list.dart'; +import 'package:rxdart/rxdart.dart'; +import 'package:rx_bloc/rx_bloc.dart'; +/// [Base] part 'page.dart'; part 'component.dart'; part 'screen.dart'; @@ -25,5 +28,7 @@ part 'entity.dart'; part 'list_view.dart'; part 'carousel_view.dart'; part 'read_view.dart'; +part 'enhanced_list_view.dart'; +/// [Component]'s part 'text.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index 799c3b86..7283c1df 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,77 +1,102 @@ +# Informações básicas do projeto name: hub -description: A new Flutter project. - -publish_to: "none" +description: . # Descrição do projeto (adicione mais detalhes se necessário) +publish_to: "none" # Destino de publicação +# Versão do aplicativo version: 1.3.5+24 +# Restrições de versão do SDK Dart environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.5.0-0.0.dev <4.0.0" +# Dependências do aplicativo dependencies: + # Dependências essenciais do Flutter flutter: sdk: flutter flutter_localizations: sdk: flutter - pdfx: ^2.8.0 - auto_size_text: ^3.0.0 + + # Gerenciamento de Estado + provider: 6.1.2 + flutter_bloc: ^9.0.0 + flutter_riverpod: ^2.5.1 + rx_bloc: ^6.0.1 + flutter_rx_bloc: ^7.0.0 + rx_bloc_list: ^5.0.1 + rxdart: ^0.28.0 + rx_bloc_test: ^5.0.0 + bloc_test: ^10.0.0 + bloc_concurrency: ^0.3.0 + hydrated_bloc: ^10.0.0 + + # Programação Funcional + dart_either: ^2.0.0 + result_dart: ^2.0.0 + fpdart: ^1.1.1 + + # Pacotes de UI + auto_size_text: 3.0.0 barcode_widget: ^2.0.4 + infinite_scroll_pagination: ^4.1.0 cached_network_image: ^3.4.0 - firebase_core: ^3.4.0 flutter_inappwebview: ^6.0.0 webview_flutter: ^4.8.0 - rxdart: ^0.28.0 - collection: ^1.18.0 - app_links: ^6.3.2 - # crop_your_image: 1.1.0 - csv: 6.0.0 - device_info_plus: ^10.1.2 #11.2.2 + flutter_spinkit: 5.2.1 + flutter_staggered_grid_view: 0.7.0 + flutter_svg: ^2.0.15 + font_awesome_flutter: ^10.8.0 + google_fonts: 6.2.1 + material_symbols_icons: ^4.2784.0 + fluttertoast: ^8.2.8 + cupertino_icons: ^1.0.0 + qr_flutter: ^4.1.0 + percent_indicator: ^4.2.3 + page_transition: ^2.2.1 + share_plus: ^10.1.4 + pdfx: ^2.8.0 + dropdown_button2: ^2.3.9 + + # Firebase + firebase_core: ^3.4.0 firebase_messaging: ^15.1.0 - dropdown_button2: 2.3.9 + firebase_analytics: ^11.3.0 + firebase_crashlytics: ^4.0.1 + + # Utilidades + app_links: ^6.3.3 + collection: ^1.18.0 + csv: 6.0.0 + device_info_plus: ^10.1.2 easy_debounce: 2.0.3 equatable: ^2.0.6 file_picker: ^8.0.7 - # flutter_expandable_fab: ^2.1.0 - firebase_analytics: ^11.3.0 flutter_animate: ^4.5.2 - # flutter_cache_manager: ^3.4.1 - # flutter_plugin_android_lifecycle: ^2.0.23 - 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 flutter_secure_storage_macos: ^4.0.0 flutter_secure_storage_platform_interface: ^2.0.1 flutter_secure_storage_web: ^2.0.0 flutter_secure_storage_windows: ^4.0.0 - flutter_spinkit: 5.2.1 - flutter_staggered_grid_view: 0.7.0 - flutter_svg: ^2.0.15 - font_awesome_flutter: ^10.8.0 from_css_color: 2.0.0 go_router: ^14.3.0 - google_fonts: 6.2.1 - http: 1.3.0 + http: ^1.3.0 image_picker: 1.1.2 image_picker_android: ^0.8.12+15 image_picker_for_web: ^3.0.5 - persistent_bottom_nav_bar: ^6.2.1 image_picker_ios: ^0.8.12+1 image_picker_platform_interface: ^2.10.1 local_auth: ^2.2.0 intl: ^0.19.0 - # camera: ^0.11.0+2 json_path: ^0.7.4 mime_type: ^1.0.1 - page_transition: ^2.2.1 path_provider: ^2.1.4 path_provider_android: ^2.2.12 google_mlkit_face_detection: ^0.12.0 path_provider_foundation: ^2.4.1 path_provider_platform_interface: 2.1.2 - percent_indicator: ^4.2.3 plugin_platform_interface: 2.1.8 - provider: 6.1.2 shared_preferences: ^2.3.2 shared_preferences_android: ^2.3.3 shared_preferences_foundation: ^2.5.3 @@ -85,69 +110,80 @@ dependencies: url_launcher_android: ^6.3.12 url_launcher_ios: ^6.3.1 url_launcher_platform_interface: 2.3.2 - infinite_scroll_pagination: ^4.1.0 - # video_player: 2.8.7 - # video_player_android: 2.5.0 - # video_player_avfoundation: 2.6.1 - # video_player_platform_interface: 6.2.2 - # video_player_web: 2.3.1 - material_symbols_icons: ^4.2784.0 - fluttertoast: ^8.2.8 - cupertino_icons: ^1.0.0 - flutter_bloc: ^9.0.0 - flutter_riverpod: ^2.5.1 - qr_flutter: ^4.1.0 permission_handler: ^11.3.1 - firebase_crashlytics: ^4.0.1 awesome_notifications: ^0.10.0 app_tracking_transparency: ^2.0.6 - # dio: ^5.7.0 - # crypto: ^3.0.5 freezed_annotation: ^2.4.4 package_info_plus: ^8.1.1 - # json_annotation: ^4.9.0 + sliver_tools: ^0.2.12 + json_annotation: ^4.9.0 + # Dependências a partir de repositório Git (pacotes personalizados) + # base: + # git: + # url: 'git@github.com:FRE-Informatica/flutter-freaccess-base.git' + # path: 'packages/base' + # components: + # git: + # url: 'git@github.com:FRE-Informatica/flutter-freaccess-base.git' + # path: 'packages/components' + # templates: + # git: + # url: 'git@github.com:FRE-Informatica/flutter-freaccess-base.git' + # path: 'packages/templates' + # theme: + # git: + # url: 'git@github.com:FRE-Informatica/flutter-freaccess-base.git' + # path: 'packages/theme' + +# Substituição de versões específicas de pacotes, se necessário dependency_overrides: - http: 1.3.0 + http: ^1.3.0 uuid: ^4.0.0 win32: 5.5.1 +# Dependências para desenvolvimento e testes dev_dependencies: + bloc_lint: ^0.1.0 + rx_bloc_generator: ^8.0.1 flutter_launcher_icons: ^0.14.1 flutter_lints: ^5.0.0 image: ^4.3.0 lints: ^5.0.0 - # build_runner: ^2.4.13 mockito: ^5.4.4 integration_test: sdk: flutter flutter_test: sdk: flutter - build_runner: ^2.4.13 - freezed: ^2.5.7 - json_serializable: ^6.9.0 + build_runner: ^2.4.14 + freezed: ^3.0.0-0.0.dev + json_serializable: ^6.9.4 test: ^1.25.7 patrol: ^3.13.2 patrol_finders: ^2.6.0 +# Configuração do flutter_launcher_icons flutter_launcher_icons: - android: "launcher_icon" - ios: true + android: "launcher_icon" # Nome da pasta/ícone para Android + ios: true # Geração de ícones para iOS web: - generate: true + generate: true # Geração de ícones para Web image_path: "assets/images/app_launcher_icon.svg" adaptive_icon_background: "assets/images/adaptive_background_icon.svg" adaptive_icon_foreground: "assets/images/adaptive_foreground_icon.svg" +# Configurações específicas do Flutter flutter: - uses-material-design: true + uses-material-design: true # Habilita o uso do Material Design + # Definição de assets (imagens, fontes, etc.) assets: - assets/fonts/ - assets/images/ - assets/images/dark/ - assets/images/light/ - - assets/files/ + +# Configuração de fontes customizadas fonts: - family: "SF Pro" fonts: @@ -160,13 +196,15 @@ fonts: - family: Icons fonts: - asset: assets/fonts/icons.ttf + - family: Menu fonts: - asset: assets/fonts/menu.ttf +# Configuração do Patrol (ferramenta para testes de integração) patrol: app_name: FRE ACCESS HUB android: package_name: com.freaccess.hub ios: - bundle_id: br.com.freaccess.hub \ No newline at end of file + bundle_id: br.com.freaccess.hub From 07f55bbceb0ad11b94867f8e75201bee04bbc58f Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Mon, 17 Feb 2025 14:15:46 -0300 Subject: [PATCH 11/32] WIP --- .../documents/document_page_bloc.dart | 38 +-- .../documents/document_page_model.dart | 125 ++++---- .../documents/document_page_widget.dart | 2 +- .../documents/document_screen_manager.dart | 4 +- lib/shared/mixins/pegeable_mixin.dart | 260 +++++++-------- lib/shared/widgets/enhanced_list_view.dart | 296 ++++-------------- 6 files changed, 282 insertions(+), 443 deletions(-) diff --git a/lib/features/documents/document_page_bloc.dart b/lib/features/documents/document_page_bloc.dart index a0d75b62..81406d99 100644 --- a/lib/features/documents/document_page_bloc.dart +++ b/lib/features/documents/document_page_bloc.dart @@ -6,22 +6,22 @@ part of 'index.dart'; class DocumentPageBloc extends Bloc { final DocumentPageModel model; + static DocumentPageBloc? _singleton; - DocumentPageBloc._(this.model, DocumentPageState initialState) - : super(initialState) { + factory DocumentPageBloc(DocumentPageModel model) { + _singleton ??= DocumentPageBloc._internal(model); + return _singleton!; + } + + DocumentPageBloc._internal(this.model) : super(DocumentPageState()) { on(_selectDocument); on(_unselectDocument); on(_filterCategoryEvent); } - static DocumentPageBloc create(DocumentPageModel model) { - final initialState = DocumentPageState(); - return DocumentPageBloc._(model, initialState); - } - Future _filterCategoryEvent( FilterCategoryEvent event, Emitter emit) async { - log('generateDocuments -> ${state.isCategorySelected}'); + _selectCategory(event, emit); state.isCategorySelected ? _unselectCategory(event, emit) : _selectCategory(event, emit); @@ -29,16 +29,14 @@ class DocumentPageBloc extends Bloc { Future _selectCategory( FilterCategoryEvent event, Emitter emit) async { + log('filterItems A: ${event.query}'); emit(state.copyWith( isCategorySelected: true, )); - final listViewState = model.enhancedListViewKey.currentState!; - listViewState.safeSetState(() async { - return await listViewState.filterItems(event.query); - }); - - // state.isCategorySelected ? s.filter(event.query) : s.filter(event.query); + final listViewState = model.vihicleScreenManager.currentState!; + listViewState.widget.bodyItems = (await model.generateBodyItems( + 1, 10, event.query)) as BodyItemsBuilder; } Future _unselectCategory( @@ -47,10 +45,9 @@ class DocumentPageBloc extends Bloc { isCategorySelected: false, )); - final listViewState = model.enhancedListViewKey.currentState!; - listViewState.safeSetState(() async { - return await listViewState.filterItems(null); - }); + final listViewState = model.vihicleScreenManager.currentState!; + listViewState.widget.bodyItems = (await model.generateBodyItems( + 1, 10, null)) as BodyItemsBuilder; } Future _selectDocument( @@ -69,15 +66,10 @@ class DocumentPageBloc extends Bloc { Future _unselectDocument( UnselectDocumentEvent event, Emitter emit) async { - // final docs = await model.generateDocuments(state.page, state.query); - // final cats = await model.generateCategories(); - emit( state.copyWith( currentDocument: null, isDocumentSelected: false, - // documents: docs.$2, - // categories: cats, ), ); } diff --git a/lib/features/documents/document_page_model.dart b/lib/features/documents/document_page_model.dart index 8501e39b..78b696ae 100644 --- a/lib/features/documents/document_page_model.dart +++ b/lib/features/documents/document_page_model.dart @@ -1,21 +1,25 @@ part of 'index.dart'; class DocumentPageModel extends FlutterFlowModel { - DocumentPageModel(); + DocumentPageModel._privateConstructor(); - late final GlobalKey> pageKey; - late final EnhancedRemoteListViewKey - enhancedListViewKey; - late final DocumentKey viewerKey; - late final PagingController _pagingController; + static final DocumentPageModel _instance = + DocumentPageModel._privateConstructor(); + + factory DocumentPageModel() { + return _instance; + } + + late EnhancedListViewKey vihicleScreenManager; + late DocumentKey vehicleScreenViewer; + late PagingController _pagingController; /// ------------ @override void initState(BuildContext context) { - pageKey = GlobalKey>(); - enhancedListViewKey = EnhancedRemoteListViewKey(); - viewerKey = DocumentKey(); + vihicleScreenManager = EnhancedListViewKey(); + vehicleScreenViewer = DocumentKey(); _pagingController = PagingController(firstPageKey: 1); } @@ -23,8 +27,8 @@ class DocumentPageModel extends FlutterFlowModel { @override void dispose() { _pagingController.dispose(); - // isCategorySelected = false; - // isDocumentSelected = false; + vihicleScreenManager.currentState?.dispose(); + vehicleScreenViewer.currentState?.dispose(); } /// ------------ @@ -77,7 +81,7 @@ class DocumentPageModel extends FlutterFlowModel { }); /// [generateBodyItems] - Future> generateBodyItems( + Future> generateBodyItems( int pageKey, int pageSize, dynamic query) async { log('generateDocuments: $query'); @@ -86,54 +90,73 @@ class DocumentPageModel extends FlutterFlowModel { final GetDocuments getDocuments = FreAccessWSGlobal.getDocuments; final ApiCallResponse newItems = await getDocuments.call(pageKey, query); - if (newItems.jsonBody == null || newItems.jsonBody['error'] == true) { - return error; - } + if (newItems.jsonBody == null) return error; + if (newItems.jsonBody['error'] == true) return error; final List list = newItems.jsonBody['value']['list']; - final List docs = list.map((item) { + + late final List docs = []; + + for (var item in list) { log('-> generateDocuments: $item'); - return Document( - id: item['id'], - description: item['description'], - type: item['type'], + 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: item['category']['id'], - color: item['category']['color'].toColor(), - title: item['category']['description'], + id: categoryId, + color: color.toColor(), + title: category, ), - person: item['person'] ?? '', - property: item['property'] ?? '', - createdAt: item['createdAt'], - updatedAt: item['updatedAt'], + person: person, + property: property, + createdAt: createdAt, + updatedAt: updatedAt, ); - }).toList(); + + docs.add(doc); + } return docs as List; } /// [generateHeaderItems] - Future> generateHeaderItems() async { + Future> generateHeaderItems() async { + log('generateCategories: '); final List error = [null]; final GetCategories getCategories = FreAccessWSGlobal.getCategories; final ApiCallResponse newItems = await getCategories.call(); - if (newItems.jsonBody == null || newItems.jsonBody['error'] == true) { - return error; - } + 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 List list = newItems.jsonBody['value']; - final List categories = list.map((item) { - return Category( - id: item['id'], - color: item['color'].toColor(), - title: item['description'], + final cat = Category( + id: id, + color: color.toColor(), + title: title, ); - }).toList(); - - log('categories: $categories'); - return categories as List; + cats.add(cat); + } + log('cats: $cats'); + return cats as List; } /// [filter] @@ -142,26 +165,10 @@ class DocumentPageModel extends FlutterFlowModel { .read() .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] void onFetchError(Object e, StackTrace s) { - DialogUtil.errorDefault(viewerKey.currentContext!); + DialogUtil.errorDefault(vehicleScreenViewer.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 ae7b00e2..815d23bd 100644 --- a/lib/features/documents/document_page_widget.dart +++ b/lib/features/documents/document_page_widget.dart @@ -23,7 +23,7 @@ class FREDocumentPageState Widget buildBody(BuildContext context) { return BlocProvider( - create: (context) => DocumentPageBloc.create(model), + create: (context) => DocumentPageBloc(model), child: BlocBuilder( builder: (context, state) { print('Bloc -> ${state.isCategorySelected}'); diff --git a/lib/features/documents/document_screen_manager.dart b/lib/features/documents/document_screen_manager.dart index 1db102dd..d2b52f3c 100644 --- a/lib/features/documents/document_screen_manager.dart +++ b/lib/features/documents/document_screen_manager.dart @@ -31,8 +31,8 @@ class DocumentManagerScreen extends StatelessScreen { return Column( children: [ Expanded( - child: EnhancedListView.remote( - key: model.enhancedListViewKey, + child: EnhancedListView( + key: model.vihicleScreenManager, headerBuilder: model.itemHeaderBuilder, headerItems: model.generateHeaderItems, bodyBuilder: model.itemBodyBuilder, diff --git a/lib/shared/mixins/pegeable_mixin.dart b/lib/shared/mixins/pegeable_mixin.dart index 95b60b71..20a3cfdb 100644 --- a/lib/shared/mixins/pegeable_mixin.dart +++ b/lib/shared/mixins/pegeable_mixin.dart @@ -1,140 +1,140 @@ -import 'package:flutter/material.dart'; -import 'package:hub/flutter_flow/index.dart'; -import 'package:hub/shared/utils/limited_text_size.dart'; -import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; +// import 'package:flutter/material.dart'; +// import 'package:hub/flutter_flow/index.dart'; +// import 'package:hub/shared/utils/limited_text_size.dart'; +// import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; -extension PagedListViewExtension - on PagedSliverList {} +// extension PagedListViewExtension +// on PagedSliverList {} -typedef PaginatedListViewHeaderBuilder = Widget Function( - Future> Function() gen); -typedef PaginatedListViewBodyBuilder = Widget Function(BuildContext, T, int); +// typedef PaginatedListViewHeaderBuilder = Widget Function( +// Future> Function() gen); +// typedef PaginatedListViewBodyBuilder = Widget Function(BuildContext, T, int); -mixin Pageable on State { - Expanded buildPaginatedListView( - String noDataFound, - PagingController pg, - Future> Function() headerItems, - PaginatedListViewHeaderBuilder headerBuilder, - PaginatedListViewBodyBuilder bodyBuilder) { - final theme = FlutterFlowTheme.of(context); - return Expanded( - child: RefreshIndicator( - backgroundColor: theme.primaryBackground, - color: theme.primary, - onRefresh: () async => pg.refresh(), - child: PagedListView( - pagingController: pg, - builderDelegate: PagedChildBuilderDelegate( - animateTransitions: true, - itemBuilder: (context, item, int index) { - return Column(children: [ - if (index == 0) headerBuilder(headerItems), - bodyBuilder(context, item, index), - ]); - }, - newPageProgressIndicatorBuilder: (context) => - buildLoadingIndicator(context), - firstPageProgressIndicatorBuilder: (context) => - buildLoadingIndicator(context), - noItemsFoundIndicatorBuilder: (context) => - buildNoDataFound( - context, noDataFound, headerItems, headerBuilder), - firstPageErrorIndicatorBuilder: (context) => const Placeholder(), - newPageErrorIndicatorBuilder: (context) => const Placeholder(), - ), - ), - ), - ); - } +// mixin Pageable on State { +// Expanded buildPaginatedListView( +// String noDataFound, +// PagingController pg, +// Future> Function() headerItems, +// PaginatedListViewHeaderBuilder headerBuilder, +// PaginatedListViewBodyBuilder bodyBuilder) { +// final theme = FlutterFlowTheme.of(context); +// return Expanded( +// child: RefreshIndicator( +// backgroundColor: theme.primaryBackground, +// color: theme.primary, +// onRefresh: () async => pg.refresh(), +// child: PagedListView( +// pagingController: pg, +// builderDelegate: PagedChildBuilderDelegate( +// animateTransitions: true, +// itemBuilder: (context, item, int index) { +// return Column(children: [ +// if (index == 0) headerBuilder(headerItems), +// bodyBuilder(context, item, index), +// ]); +// }, +// newPageProgressIndicatorBuilder: (context) => +// buildLoadingIndicator(context), +// firstPageProgressIndicatorBuilder: (context) => +// buildLoadingIndicator(context), +// noItemsFoundIndicatorBuilder: (context) => +// buildNoDataFound( +// context, noDataFound, headerItems, headerBuilder), +// firstPageErrorIndicatorBuilder: (context) => const Placeholder(), +// newPageErrorIndicatorBuilder: (context) => const Placeholder(), +// ), +// ), +// ), +// ); +// } - Future showError(PagingStatus status, PagingController pg) async { - if (status == PagingStatus.subsequentPageError) { - final message = FFLocalizations.of(context).getVariableText( - enText: 'Something went wrong while fetching a new page.', - ptText: 'Algo deu errado ao buscar uma nova página.', - ); - final retry = FFLocalizations.of(context).getVariableText( - enText: 'Retry', - ptText: 'Recarregar', - ); +// Future showError(PagingStatus status, PagingController pg) async { +// if (status == PagingStatus.subsequentPageError) { +// final message = FFLocalizations.of(context).getVariableText( +// enText: 'Something went wrong while fetching a new page.', +// ptText: 'Algo deu errado ao buscar uma nova página.', +// ); +// final retry = FFLocalizations.of(context).getVariableText( +// enText: 'Retry', +// ptText: 'Recarregar', +// ); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(message), - action: SnackBarAction( - label: retry, - onPressed: () => pg.retryLastFailedRequest(), - ), - ), - ); - } - } +// ScaffoldMessenger.of(context).showSnackBar( +// SnackBar( +// content: Text(message), +// action: SnackBarAction( +// label: retry, +// onPressed: () => pg.retryLastFailedRequest(), +// ), +// ), +// ); +// } +// } - Future fetchPage({ - required Future<(bool, dynamic)> Function() dataProvider, - required void Function(dynamic data) onDataAvailable, - required void Function() onDataUnavailable, - required void Function(Object error, StackTrace stackTrace) onFetchError, - }) async { - try { - final (bool isDataAvailable, dynamic data) = await dataProvider(); - if (isDataAvailable) { - onDataAvailable(data); - } else { - onDataUnavailable(); - } - } catch (error, stackTrace) { - onFetchError(error, stackTrace); - } - } +// Future fetchPage({ +// required Future<(bool, dynamic)> Function() dataProvider, +// required void Function(dynamic data) onDataAvailable, +// required void Function() onDataUnavailable, +// required void Function(Object error, StackTrace stackTrace) onFetchError, +// }) async { +// try { +// final (bool isDataAvailable, dynamic data) = await dataProvider(); +// if (isDataAvailable) { +// onDataAvailable(data); +// } else { +// onDataUnavailable(); +// } +// } catch (error, stackTrace) { +// onFetchError(error, stackTrace); +// } +// } - void showNoMoreDataSnackBar(BuildContext context) { - final message = FFLocalizations.of(context).getVariableText( - ptText: "Não há mais dados.", - enText: "No more data.", - ); +// void showNoMoreDataSnackBar(BuildContext context) { +// final message = FFLocalizations.of(context).getVariableText( +// ptText: "Não há mais dados.", +// enText: "No more data.", +// ); - showSnackbar(context, message, true); - } +// showSnackbar(context, message, true); +// } - Widget buildNoDataFound( - BuildContext context, - String title, - Future> Function() items, - Widget Function(Future> Function() items) headerBuilder, - ) { - final headerFontSize = LimitedFontSizeUtil.getHeaderFontSize(context); - // final bodyFontSize = LimitedFontSizeUtil.getBodyFontSize(context); - return Column( - children: [ - headerBuilder(items), - Expanded( - child: Center( - child: Text( - title, - style: TextStyle( - fontFamily: 'Nunito', - fontSize: headerFontSize, - ), - ), - ), - ), - ], - ); - } +// Widget buildNoDataFound( +// BuildContext context, +// String title, +// Future> Function() items, +// Widget Function(Future> Function() items) headerBuilder, +// ) { +// final headerFontSize = LimitedFontSizeUtil.getHeaderFontSize(context); +// // final bodyFontSize = LimitedFontSizeUtil.getBodyFontSize(context); +// return Column( +// children: [ +// headerBuilder(items), +// 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( - child: CircularProgressIndicator( - valueColor: AlwaysStoppedAnimation( - FlutterFlowTheme.of(context).primary, - ), - ), - ), - ); - } -} +// Widget buildLoadingIndicator(BuildContext context) { +// print('Loading'); +// return Container( +// padding: const EdgeInsets.symmetric(vertical: 15), +// child: Center( +// child: CircularProgressIndicator( +// valueColor: AlwaysStoppedAnimation( +// FlutterFlowTheme.of(context).primary, +// ), +// ), +// ), +// ); +// } +// } diff --git a/lib/shared/widgets/enhanced_list_view.dart b/lib/shared/widgets/enhanced_list_view.dart index 5ba1828e..eb2ec5a1 100644 --- a/lib/shared/widgets/enhanced_list_view.dart +++ b/lib/shared/widgets/enhanced_list_view.dart @@ -2,10 +2,8 @@ part of 'widgets.dart'; /// [TypeDefs] -typedef EnhancedRemoteListViewKey - = GlobalKey>; -typedef EnhancedLocalListViewKey - = GlobalKey>; +typedef EnhancedListViewKey + = GlobalKey>; typedef PaginatedListViewHeaderBuilder = Widget Function( Future> Function() headerItems); @@ -154,176 +152,17 @@ abstract interface class EnhancedListViewBase extends StatefulWidget { abstract interface class EnhancedListViewBaseState extends State {} -class EnhancedListView { - static EnhancedRemoteListView - remote({ - required Key? key, - required BodyItemsBuilder bodyItems, - required PaginatedListViewBodyBuilder bodyBuilder, - HeaderItemsBuilder? headerItems, - PaginatedListViewHeaderBuilder? headerBuilder, - FooterItemsBuilder? footerItems, - PaginatedListViewFooterBuilder? footerBuilder, - }) { - return EnhancedRemoteListView( - key: key, - bodyItems: bodyItems, - bodyBuilder: bodyBuilder, - headerItems: headerItems, - headerBuilder: headerBuilder, - footerItems: footerItems, - footerBuilder: footerBuilder, - ); - } - - static EnhancedLocalListView local({ - required Key? key, - required List list, - required Widget Function(T) itemBuilder, - required bool Function(T, String) filter, - List Function(String)? onSearch, - Widget? header, - }) { - return EnhancedLocalListView( - key: key, - list: list, - itemBuilder: itemBuilder, - filter: filter, - onSearch: onSearch, - header: header, - ); - } -} - -/// [EnhancedLocalListView] - -class EnhancedLocalListView extends EnhancedListViewBase { - final List list; - final Widget Function(T) itemBuilder; - final bool Function(T, String) filter; - final Widget header; - final List Function(String)? onSearch; - - EnhancedLocalListView({ - Key? key, - 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); - - @override - EnhancedLocalListViewState createState() => - EnhancedLocalListViewState(); -} - -class EnhancedLocalListViewState - extends State> { - TextEditingController editingController = TextEditingController(); - late List 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: [ - 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 +// ignore: must_be_immutable +class EnhancedListView extends EnhancedListViewBase { - final BodyItemsBuilder bodyItems; - final PaginatedListViewBodyBuilder bodyBuilder; - final HeaderItemsBuilder? headerItems; - final PaginatedListViewHeaderBuilder? headerBuilder; - final FooterItemsBuilder? footerItems; - final PaginatedListViewFooterBuilder? footerBuilder; + BodyItemsBuilder bodyItems; + PaginatedListViewBodyBuilder bodyBuilder; + HeaderItemsBuilder? headerItems; + PaginatedListViewHeaderBuilder? headerBuilder; + FooterItemsBuilder? footerItems; + PaginatedListViewFooterBuilder? footerBuilder; - const EnhancedRemoteListView({ + EnhancedListView({ Key? key, required this.bodyItems, required this.bodyBuilder, @@ -334,62 +173,88 @@ class EnhancedRemoteListView }) : super(key: key); @override - EnhancedRemoteListViewState createState() => - EnhancedRemoteListViewState(); + EnhancedListViewState createState() => + EnhancedListViewState(); } -class EnhancedRemoteListViewState - extends State> { +class EnhancedListViewState + extends State> { final ScrollController _scrollController = ScrollController(); bool _isLoadingMore = false; - List _items = []; - int _currentPage = 1; + List items = []; + List headerItems = []; + List footerItems = []; + int currentPage = 1; Query query; @override void initState() { super.initState(); _scrollController.addListener(_onScroll); - _loadInitialItems(); + _loadBodyItems(); + _loadHeaderItems(); + _loadFooterItems(); } void _onScroll() { if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent && !_isLoadingMore) { - _loadMoreItems(); + _loadMoreBodyItems(); } } - Future _loadInitialItems() async { + Future _loadBodyItems() async { + log('loadInitialItems'); final newItems = await widget.bodyItems(1, 10, query); setState(() { - _items = newItems; + items = newItems; }); } - Future _loadMoreItems() async { + Future _loadMoreBodyItems() async { + log('loadMoreItems'); setState(() { _isLoadingMore = true; }); - final newItems = await widget.bodyItems(_currentPage + 1, 10, query); + final newItems = await widget.bodyItems(currentPage + 1, 10, query); setState(() { _isLoadingMore = false; if (newItems.isNotEmpty) { - _items.addAll(newItems); - _currentPage++; + items.addAll(newItems); + currentPage++; } }); } - Future filterItems(Query newQuery) async { - log('filterItems: $newQuery'); + Future _loadHeaderItems() async { + if (widget.headerItems != null) { + log('loadHeaderItems'); + final newHeaderItems = await widget.headerItems!(); + setState(() { + headerItems = newHeaderItems; + }); + } + } + + Future _loadFooterItems() async { + if (widget.footerItems != null) { + log('loadFooterItems'); + final newFooterItems = await widget.footerItems!(); + setState(() { + footerItems = newFooterItems; + }); + } + } + + Future filterBodyItems(Query newQuery) async { + log('filterItems B: ${newQuery.toString()}'); setState(() { query = newQuery; - _items = []; - _currentPage = 1; + items = []; + currentPage = 1; }); - await _loadInitialItems(); + await _loadBodyItems(); } @override @@ -397,51 +262,26 @@ class EnhancedRemoteListViewState log('key: ${widget.key}'); return ListView.builder( controller: _scrollController, - itemCount: _items.length + - (widget.headerItems != null ? 1 : 0) + - (widget.footerItems != null ? 1 : 0) + + itemCount: items.length + + (headerItems.isNotEmpty ? 1 : 0) + + (footerItems.isNotEmpty ? 1 : 0) + (_isLoadingMore ? 1 : 0), itemBuilder: (context, index) { - if (widget.headerItems != null && index == 0) { - return FutureBuilder>( - 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 (headerItems.isNotEmpty && index == 0) { + return widget.headerBuilder!(() => Future.value(headerItems)); } - if (widget.footerItems != null && - index == _items.length + (widget.headerItems != null ? 1 : 0)) { - return FutureBuilder>( - 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 (footerItems.isNotEmpty && + index == items.length + (headerItems.isNotEmpty ? 1 : 0)) { + return widget.footerBuilder!(() => Future.value(footerItems)); } if (_isLoadingMore && index == - _items.length + - (widget.headerItems != null ? 1 : 0) + - (widget.footerItems != null ? 1 : 0)) { + items.length + + (headerItems.isNotEmpty ? 1 : 0) + + (footerItems.isNotEmpty ? 1 : 0)) { 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); }, ); From 866e438c87a450bc764a2ed8685a33166df17387 Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Mon, 17 Feb 2025 18:00:02 -0300 Subject: [PATCH 12/32] WIP --- .../backend/api_requests/api_calls.dart | 3 +- .../documents/archive_item_component.dart | 2 - .../documents/category_item_component.dart | 67 -- .../documents/document_item_component.dart | 176 ----- .../documents/document_page_bloc.dart | 147 ---- .../documents/document_page_model.dart | 174 ---- .../documents/document_page_widget.dart | 43 - .../documents/document_screen_manager.dart | 48 -- .../documents/document_screen_viewer.dart | 45 -- lib/features/documents/documents.dart | 745 ++++++++++++++++++ lib/features/documents/index.dart | 24 - lib/flutter_flow/nav/nav.dart | 2 +- lib/shared/widgets/enhanced_list_view.dart | 306 +++---- lib/shared/widgets/widgets.dart | 6 +- lib/shared/widgets/widgets.rxb.g.dart | 98 +++ 15 files changed, 965 insertions(+), 921 deletions(-) create mode 100644 lib/features/documents/documents.dart delete mode 100644 lib/features/documents/index.dart create mode 100644 lib/shared/widgets/widgets.rxb.g.dart diff --git a/lib/features/backend/api_requests/api_calls.dart b/lib/features/backend/api_requests/api_calls.dart index 87424040..0e0a7421 100644 --- a/lib/features/backend/api_requests/api_calls.dart +++ b/lib/features/backend/api_requests/api_calls.dart @@ -5,8 +5,7 @@ import 'dart:convert'; import 'dart:developer'; import 'package:flutter/foundation.dart'; -import 'package:http/http.dart'; -import 'package:hub/features/documents/index.dart' as doc; +import 'package:hub/features/documents/documents.dart' as doc; import 'package:hub/features/notification/index.dart'; import 'package:hub/features/storage/index.dart'; diff --git a/lib/features/documents/archive_item_component.dart b/lib/features/documents/archive_item_component.dart index 0063e562..9ec58ac2 100644 --- a/lib/features/documents/archive_item_component.dart +++ b/lib/features/documents/archive_item_component.dart @@ -1,3 +1 @@ part of 'index.dart'; - -abstract interface class Archive extends Entity {} diff --git a/lib/features/documents/category_item_component.dart b/lib/features/documents/category_item_component.dart index 84659d09..9ec58ac2 100644 --- a/lib/features/documents/category_item_component.dart +++ b/lib/features/documents/category_item_component.dart @@ -1,68 +1 @@ part of 'index.dart'; - -interface class Category extends Archive { - 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; -} - -class CategoryItem extends StatelessComponent { - final Category category; - - const CategoryItem({ - super.key, - required this.category, - }); - - @override - Widget build(BuildContext context) { - final backgroundTheme = FlutterFlowTheme.of(context).primaryBackground; - return ColoredBox( - color: backgroundTheme, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - Container( - padding: const EdgeInsets.all(8.0), - decoration: BoxDecoration( - color: category.color, - shape: BoxShape.circle, - ), - child: Icon( - Icons.folder, - color: Colors.white, - size: 40, - ), - ), - const SizedBox(height: 8), - Text( - category.title, - style: TextStyle( - color: category.color, - fontWeight: FontWeight.bold, - ), - overflow: TextOverflow.ellipsis, - ), - ], - ), - ), - ); - } -} diff --git a/lib/features/documents/document_item_component.dart b/lib/features/documents/document_item_component.dart index 29b91a96..9ec58ac2 100644 --- a/lib/features/documents/document_item_component.dart +++ b/lib/features/documents/document_item_component.dart @@ -1,177 +1 @@ part of 'index.dart'; - -interface class Document extends Archive { - final int id; - final String description; - final String type; - final Category category; - 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, - }); - - factory Document.fromDesc(String desc) => Document( - id: 0, - description: desc, - type: '', - category: Category.fromDesc(''), - person: '', - property: '', - createdAt: '', - updatedAt: '', - ); -} - -// ignore: must_be_immutable -class DocumentItem extends StatelessComponent { - final Document document; - void Function(Document, BuildContext) onPressed; - - DocumentItem({ - super.key, - required this.document, - required this.onPressed, - }); - - Tooltip _buildTooltip(String text, Color color, BuildContext context, - BoxConstraints constraints) { - final Color textColor = FlutterFlowTheme.of(context).info; - - final area = (MediaQuery.of(context).size.height + - MediaQuery.of(context).size.width) / - 2; - - final double boxHeight = area * 0.033; - final double boxWidth = area * 0.19; - - return Tooltip( - message: text, - child: Container( - width: boxWidth, - height: boxHeight, - decoration: BoxDecoration( - color: color, - borderRadius: BorderRadius.circular(10), - ), - child: Center( - child: AutoText( - text, - overflow: TextOverflow.ellipsis, - style: TextStyle( - color: textColor, - fontWeight: FontWeight.bold, - ), - ), - ), - ), - ); - } - - @override - Widget build(BuildContext context) { - final Color primaryText = FlutterFlowTheme.of(context).primaryText; - final Color primaryColor = FlutterFlowTheme.of(context).primary; - - final TextStyle textStyleMajor = TextStyle( - color: primaryText, - fontWeight: FontWeight.bold, - ); - final TextStyle textStyleMinor = TextStyle( - color: primaryText, - fontWeight: FontWeight.normal, - fontStyle: FontStyle.italic, - ); - - return Padding( - padding: const EdgeInsets.all(8), - child: LayoutBuilder( - builder: (context, constraints) { - final double boxHeight = constraints.maxHeight > 350 - ? MediaQuery.of(context).size.height * 0.07 - : MediaQuery.of(context).size.height * 2; - - return InkWell( - onTap: () => onPressed(document, context), - enableFeedback: true, - overlayColor: WidgetStateProperty.all(primaryColor), - borderRadius: BorderRadius.circular(10), - child: SizedBox( - height: boxHeight, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - // const SizedBox(width: 10), - Icon(Icons.description, color: document.category.color), - const SizedBox(width: 10), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Tooltip( - message: document.description, - child: AutoText( - document.description, - style: textStyleMajor, - overflow: TextOverflow.ellipsis, - ), - ), - AutoText( - ValidatorUtil.toLocalDateTime( - 'yyyy-MM-dd', document.updatedAt), - style: textStyleMinor, - overflow: TextOverflow.ellipsis, - ), - ], - ), - ), - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - _buildTooltip( - document.category.title, - document.category.color, - context, - constraints, - ), - ], - ), - ), - // const SizedBox(width: 10), - Center( - child: Icon( - Icons.arrow_right, - color: primaryText, - ), - ), - ], - ), - ), - ); - }, - ), - ); - } - - DocumentItem copyWith({ - Document? document, - }) { - return DocumentItem( - document: document ?? this.document, - onPressed: onPressed, - ); - } -} diff --git a/lib/features/documents/document_page_bloc.dart b/lib/features/documents/document_page_bloc.dart index 81406d99..9ec58ac2 100644 --- a/lib/features/documents/document_page_bloc.dart +++ b/lib/features/documents/document_page_bloc.dart @@ -1,148 +1 @@ part of 'index.dart'; - -/// ----------------------------------------------- -/// [DocumentPageBloc] -/// ----------------------------------------------- - -class DocumentPageBloc extends Bloc { - final DocumentPageModel model; - static DocumentPageBloc? _singleton; - - factory DocumentPageBloc(DocumentPageModel model) { - _singleton ??= DocumentPageBloc._internal(model); - return _singleton!; - } - - DocumentPageBloc._internal(this.model) : super(DocumentPageState()) { - on(_selectDocument); - on(_unselectDocument); - on(_filterCategoryEvent); - } - - Future _filterCategoryEvent( - FilterCategoryEvent event, Emitter emit) async { - _selectCategory(event, emit); - state.isCategorySelected - ? _unselectCategory(event, emit) - : _selectCategory(event, emit); - } - - Future _selectCategory( - FilterCategoryEvent event, Emitter emit) async { - log('filterItems A: ${event.query}'); - emit(state.copyWith( - isCategorySelected: true, - )); - - final listViewState = model.vihicleScreenManager.currentState!; - listViewState.widget.bodyItems = (await model.generateBodyItems( - 1, 10, event.query)) as BodyItemsBuilder; - } - - Future _unselectCategory( - FilterCategoryEvent event, Emitter emit) async { - emit(state.copyWith( - isCategorySelected: false, - )); - - final listViewState = model.vihicleScreenManager.currentState!; - listViewState.widget.bodyItems = (await model.generateBodyItems( - 1, 10, null)) as BodyItemsBuilder; - } - - Future _selectDocument( - SelectDocumentEvent event, Emitter emit) async { - print('-> select'); - emit( - state.copyWith( - uri: await GetPDF().call( - event.document.id, - ), - currentDocument: event.document, - isDocumentSelected: true, - ), - ); - } - - Future _unselectDocument( - UnselectDocumentEvent event, Emitter emit) async { - emit( - state.copyWith( - currentDocument: null, - isDocumentSelected: false, - ), - ); - } -} - -/// ----------------------------------------------- -/// [DocumentPageEvent] -/// ----------------------------------------------- - -abstract class DocumentPageEvent {} - -class SelectDocumentEvent extends DocumentPageEvent { - final Document document; - SelectDocumentEvent( - this.document, - ); -} - -class UnselectDocumentEvent extends DocumentPageEvent {} - -class FilterCategoryEvent extends DocumentPageEvent { - final Query query; - FilterCategoryEvent(this.query); -} - -/// ----------------------------------------------- -/// [DocumentPageState] -/// ----------------------------------------------- - -class DocumentPageState { - final bool isCategorySelected; - final bool isDocumentSelected; - final Document? currentDocument; - final Category? currentCategory; - final Uri? uri; - final int? count; - final dynamic page; - final Query? query; - - const DocumentPageState({ - this.query, - this.count, - this.page, - this.uri, - this.currentDocument, - this.isCategorySelected = false, - this.currentCategory, - this.isDocumentSelected = false, - }); - - DocumentPageState copyWith({ - Uri? uri, - Query? query, - int? count, - dynamic page, - List? documents, - Document? currentDocument, - bool? isDocumentSelected, - List? categories, - Category? currentCategory, - bool? isCategorySelected, - }) { - return DocumentPageState( - uri: uri ?? this.uri, - query: query ?? this.query, - count: count ?? this.count, - page: page ?? this.page, - // - currentDocument: currentDocument ?? this.currentDocument, - isDocumentSelected: isDocumentSelected ?? this.isDocumentSelected, - // - currentCategory: currentCategory ?? this.currentCategory, - isCategorySelected: isCategorySelected ?? this.isCategorySelected, - ); - } -} diff --git a/lib/features/documents/document_page_model.dart b/lib/features/documents/document_page_model.dart index 78b696ae..9ec58ac2 100644 --- a/lib/features/documents/document_page_model.dart +++ b/lib/features/documents/document_page_model.dart @@ -1,175 +1 @@ part of 'index.dart'; - -class DocumentPageModel extends FlutterFlowModel { - DocumentPageModel._privateConstructor(); - - static final DocumentPageModel _instance = - DocumentPageModel._privateConstructor(); - - factory DocumentPageModel() { - return _instance; - } - - late EnhancedListViewKey vihicleScreenManager; - late DocumentKey vehicleScreenViewer; - late PagingController _pagingController; - - /// ------------ - - @override - void initState(BuildContext context) { - vihicleScreenManager = EnhancedListViewKey(); - vehicleScreenViewer = DocumentKey(); - - _pagingController = PagingController(firstPageKey: 1); - } - - @override - void dispose() { - _pagingController.dispose(); - vihicleScreenManager.currentState?.dispose(); - vehicleScreenViewer.currentState?.dispose(); - } - - /// ------------ - - /// [onView] - void onView(Document document, BuildContext context) async { - context.read().add(SelectDocumentEvent(document)); - } - - /// [itemBodyBuilder] - DocumentItem itemBodyBuilder( - BuildContext context, T item, int index) { - print('ItemBuilder -> $index'); - return DocumentItem( - document: item, - onPressed: onView, - ); - } - - CategoryItem categoryItemBuilder(T? item) { - return CategoryItem(category: item! as Category); - } - - /// [itemHeaderBuilder] - Widget itemHeaderBuilder(Future> Function() gen) => - Builder(builder: (context) { - return Column( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(15, 0, 50, 0), - child: Text( - FFLocalizations.of(context).getVariableText( - enText: 'Recent Documents', ptText: 'Últimos Documentos'), - style: TextStyle( - color: FlutterFlowTheme.of(context).primaryText, - fontSize: LimitedFontSizeUtil.getHeaderFontSize(context), - ), - ), - ), - EnhancedCarouselView( - generateItems: gen, - itemBuilder: categoryItemBuilder, - filter: filter, - ), - ], - ); - }); - - /// [generateBodyItems] - Future> generateBodyItems( - int pageKey, int pageSize, dynamic query) async { - log('generateDocuments: $query'); - - 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 error; - if (newItems.jsonBody['error'] == true) return 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 docs as List; - } - - /// [generateHeaderItems] - Future> generateHeaderItems() async { - log('generateCategories: '); - final List error = [null]; - - 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 as List; - } - - /// [filter] - void filter(T query, BuildContext context) { - context - .read() - .add(FilterCategoryEvent(query as Archive?)); - } - - /// [onFetchError] - void onFetchError(Object e, StackTrace s) { - DialogUtil.errorDefault(vehicleScreenViewer.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 815d23bd..9ec58ac2 100644 --- a/lib/features/documents/document_page_widget.dart +++ b/lib/features/documents/document_page_widget.dart @@ -1,44 +1 @@ part of 'index.dart'; - -typedef DocumentKey = GlobalKey; - -class DocumentPage extends StatefulPage { - const DocumentPage({super.key}); - - @override - State createState() => FREDocumentPageState(); -} - -class FREDocumentPageState - extends PageState { - @override - Widget build(BuildContext context) => buildBody(context); - DocumentPageModel model = DocumentPageModel(); - - @override - void initState() { - super.initState(); - model.initState(context); - } - - Widget buildBody(BuildContext context) { - return BlocProvider( - create: (context) => DocumentPageBloc(model), - child: BlocBuilder( - builder: (context, state) { - print('Bloc -> ${state.isCategorySelected}'); - - if (state.isDocumentSelected) - return DocumentViewScreen( - doc: state.currentDocument!, - uri: state.uri!, - ); - else - return DocumentManagerScreen( - model: model, - state: state, - ); - }), - ); - } -} diff --git a/lib/features/documents/document_screen_manager.dart b/lib/features/documents/document_screen_manager.dart index d2b52f3c..9ec58ac2 100644 --- a/lib/features/documents/document_screen_manager.dart +++ b/lib/features/documents/document_screen_manager.dart @@ -1,49 +1 @@ part of 'index.dart'; - -class DocumentManagerScreen extends StatelessScreen { - final DocumentPageModel model; - final DocumentPageState state; - - const DocumentManagerScreen({ - super.key, - required this.model, - required this.state, - }); - - @override - Widget build(BuildContext context) { - final String title = FFLocalizations.of(context).getVariableText( - enText: 'Documents', - ptText: 'Documentos', - ); - final theme = FlutterFlowTheme.of(context); - action() => Navigator.pop(context); - - return Scaffold( - backgroundColor: theme.primaryBackground, - appBar: buildAppBar(title, context, action), - body: buildBody(context), - ); - } - - Widget buildBody(BuildContext context) { - final SizedBox space = SizedBox(height: 30); - return Column( - children: [ - Expanded( - child: EnhancedListView( - key: model.vihicleScreenManager, - headerBuilder: model.itemHeaderBuilder, - headerItems: model.generateHeaderItems, - bodyBuilder: model.itemBodyBuilder, - bodyItems: model.generateBodyItems, - footerBuilder: null, - footerItems: null, - ), - ), - ] // - .addToStart(space) - .addToEnd(space), - ); - } -} diff --git a/lib/features/documents/document_screen_viewer.dart b/lib/features/documents/document_screen_viewer.dart index b54bfa55..9ec58ac2 100644 --- a/lib/features/documents/document_screen_viewer.dart +++ b/lib/features/documents/document_screen_viewer.dart @@ -1,46 +1 @@ part of 'index.dart'; - -class DocumentViewScreen extends StatefulScreen { - const DocumentViewScreen({ - super.key, - required this.doc, - required this.uri, - }); - - final Document doc; - final Uri uri; - - @override - ScreenState createState() => _DocumentViewScreenState(); -} - -class _DocumentViewScreenState extends ScreenState { - @override - Widget build(BuildContext context) { - action() { - context.read().add(UnselectDocumentEvent()); - } - - final String title = widget.doc.description; - final theme = FlutterFlowTheme.of(context); - return PopScope( - canPop: false, - onPopInvokedWithResult: (didPop, result) => action(), - child: Scaffold( - backgroundColor: theme.primaryBackground, - appBar: buildAppBar(title, context, action), - body: buildBody(context), - ), - ); - } - - Widget buildBody(BuildContext context) { - // final PDFViewerKey _viewerKey = PDFViewerKey(); - - return ReadView( - // search: _viewerKey, - title: widget.doc.description, - url: widget.uri.toString(), - ); - } -} diff --git a/lib/features/documents/documents.dart b/lib/features/documents/documents.dart new file mode 100644 index 00000000..66e68194 --- /dev/null +++ b/lib/features/documents/documents.dart @@ -0,0 +1,745 @@ +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +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/utils/index.dart'; +import 'package:hub/shared/widgets/widgets.dart'; +import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; + +/// ----------------------------------------------- +/// [TypeDefs] +/// ----------------------------------------------- + +typedef DocumentKey = GlobalKey; + +/// ----------------------------------------------- + +/// [Page] +/// ----------------------------------------------- + +class DocumentPage extends StatefulPage { + const DocumentPage({super.key}); + + @override + State createState() => FREDocumentPageState(); +} + +class FREDocumentPageState + extends PageState { + @override + Widget build(BuildContext context) => buildBody(context); + DocumentPageModel model = DocumentPageModel(); + + @override + void initState() { + super.initState(); + model.initState(context); + } + + Widget buildBody(BuildContext context) { + log('Build -> DocumentPage'); + return BlocProvider( + create: (context) => DocumentPageBloc(model), + child: BlocBuilder( + builder: (context, state) { + log('Build -> DocumentPageBloc'); + print('Bloc -> ${state.isCategorySelected}'); + + if (state.isDocumentSelected) + return DocumentViewScreen( + doc: state.currentDocument!, + uri: state.uri!, + ); + else + return DocumentManagerScreen( + model: model, + state: state, + ); + }), + ); + } +} + +/// ----------------------------------------------- + +/// [Model] +/// ----------------------------------------------- + +class DocumentPageModel extends FlutterFlowModel { + DocumentPageModel._privateConstructor(); + + static final DocumentPageModel _instance = + DocumentPageModel._privateConstructor(); + + factory DocumentPageModel() { + return _instance; + } + + late EnhancedListViewKey vehicleScreenManager; + late DocumentKey vehicleScreenViewer; + late PagingController _pagingController; + + /// ------------ + + @override + void initState(BuildContext context) { + vehicleScreenManager = EnhancedListViewKey(); + vehicleScreenViewer = DocumentKey(); + + _pagingController = PagingController(firstPageKey: 1); + } + + @override + void dispose() { + _pagingController.dispose(); + vehicleScreenManager.currentState?.dispose(); + vehicleScreenViewer.currentState?.dispose(); + } + + /// ------------ + + /// [onView] + void onView(Document document, BuildContext context) async { + vehicleScreenManager.currentContext! + .read() + .add(SelectDocumentEvent(document)); + } + + /// [itemBodyBuilder] + DocumentItem itemBodyBuilder( + BuildContext context, T item, int index) { + print('ItemBuilder -> $index'); + return DocumentItem( + document: item, + onPressed: onView, + ); + } + + CategoryItem categoryItemBuilder(T? item) { + return CategoryItem(category: item! as Category); + } + + /// [itemHeaderBuilder] + Widget itemHeaderBuilder(Future> Function() gen) => + Builder(builder: (context) { + return Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(15, 0, 50, 0), + child: Text( + FFLocalizations.of(context).getVariableText( + enText: 'Recent Documents', ptText: 'Últimos Documentos'), + style: TextStyle( + color: FlutterFlowTheme.of(context).primaryText, + fontSize: LimitedFontSizeUtil.getHeaderFontSize(context), + ), + ), + ), + EnhancedCarouselView( + generateItems: gen, + itemBuilder: categoryItemBuilder, + filter: filter, + ), + ], + ); + }); + + /// [generateBodyItems] + Future> generateBodyItems( + int pageKey, int pageSize, dynamic query) async { + log('generateDocuments: $query'); + + 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 error; + if (newItems.jsonBody['error'] == true) return 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 docs as List; + } + + /// [generateHeaderItems] + Future> generateHeaderItems() async { + log('generateCategories: '); + final List error = [null]; + + 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 as List; + } + + /// [filter] + void filter(T query, BuildContext context) { + context + .read() + .add(FilterCategoryEvent(query as Archive?)); + } + + /// [onFetchError] + void onFetchError(Object e, StackTrace s) { + DialogUtil.errorDefault(vehicleScreenViewer.currentContext!); + LogUtil.requestAPIFailed( + "proccessRequest.php", "", "Consulta de Veículo", e, s); + } +} + +/// ----------------------------------------------- +/// [BLoC] +/// ----------------------------------------------- + +/// [DocumentPageBloc] + +class DocumentPageBloc extends Bloc { + final DocumentPageModel model; + static DocumentPageBloc? _singleton; + + factory DocumentPageBloc(DocumentPageModel model) { + _singleton ??= DocumentPageBloc._internal(model); + return _singleton!; + } + + DocumentPageBloc._internal(this.model) : super(DocumentPageState()) { + on(_selectDocument); + on(_unselectDocument); + on(_filterCategoryEvent); + } + + Future _filterCategoryEvent( + FilterCategoryEvent event, Emitter emit) async { + _selectCategory(event, emit); + state.isCategorySelected + ? _unselectCategory(event, emit) + : _selectCategory(event, emit); + } + + Future _selectCategory( + FilterCategoryEvent event, Emitter emit) async { + log('filterItems A: ${event.query}'); + emit(state.copyWith( + isCategorySelected: true, + )); + + final listViewState = model.vehicleScreenManager.currentState!; + listViewState.widget.bodyItems = (await model.generateBodyItems( + 1, 10, event.query)) as BodyItemsBuilder; + } + + Future _unselectCategory( + FilterCategoryEvent event, Emitter emit) async { + emit(state.copyWith( + isCategorySelected: false, + )); + + final listViewState = model.vehicleScreenManager.currentState!; + listViewState.widget.bodyItems = (await model.generateBodyItems( + 1, 10, null)) as BodyItemsBuilder; + } + + Future _selectDocument( + SelectDocumentEvent event, Emitter emit) async { + print('-> select'); + emit( + state.copyWith( + uri: await GetPDF().call( + event.document.id, + ), + currentDocument: event.document, + isDocumentSelected: true, + ), + ); + } + + Future _unselectDocument( + UnselectDocumentEvent event, Emitter emit) async { + emit( + state.copyWith( + currentDocument: null, + isDocumentSelected: false, + ), + ); + } +} + +/// [DocumentPageEvent] + +abstract class DocumentPageEvent {} + +class SelectDocumentEvent extends DocumentPageEvent { + final Document document; + SelectDocumentEvent( + this.document, + ); +} + +class UnselectDocumentEvent extends DocumentPageEvent {} + +class FilterCategoryEvent extends DocumentPageEvent { + final Query query; + FilterCategoryEvent(this.query); +} + +/// [DocumentPageState] + +class DocumentPageState { + final bool isCategorySelected; + final bool isDocumentSelected; + final Document? currentDocument; + final Category? currentCategory; + final Uri? uri; + final int? count; + final dynamic page; + final Query? query; + + const DocumentPageState({ + this.query, + this.count, + this.page, + this.uri, + this.currentDocument, + this.isCategorySelected = false, + this.currentCategory, + this.isDocumentSelected = false, + }); + + DocumentPageState copyWith({ + Uri? uri, + Query? query, + int? count, + dynamic page, + List? documents, + Document? currentDocument, + bool? isDocumentSelected, + List? categories, + Category? currentCategory, + bool? isCategorySelected, + }) { + return DocumentPageState( + uri: uri ?? this.uri, + query: query ?? this.query, + count: count ?? this.count, + page: page ?? this.page, + // + currentDocument: currentDocument ?? this.currentDocument, + isDocumentSelected: isDocumentSelected ?? this.isDocumentSelected, + // + currentCategory: currentCategory ?? this.currentCategory, + isCategorySelected: isCategorySelected ?? this.isCategorySelected, + ); + } +} + +/// ----------------------------------------------- +/// [Screens] +/// ----------------------------------------------- + +/// [DocumentManagerScreen] + +class DocumentManagerScreen extends StatelessScreen { + final DocumentPageModel model; + final DocumentPageState state; + + const DocumentManagerScreen({ + super.key, + required this.model, + required this.state, + }); + + @override + Widget build(BuildContext context) { + final String title = FFLocalizations.of(context).getVariableText( + enText: 'Documents', + ptText: 'Documentos', + ); + final theme = FlutterFlowTheme.of(context); + action() => Navigator.pop(context); + + return Scaffold( + backgroundColor: theme.primaryBackground, + appBar: buildAppBar(title, context, action), + body: buildBody(context), + ); + } + + Widget buildBody(BuildContext context) { + log('Build -> DocumentManagerScreen'); + final SizedBox space = SizedBox(height: 30); + return Column( + children: [ + Expanded( + child: EnhancedListView( + key: model.vehicleScreenManager, + headerBuilder: model.itemHeaderBuilder, + headerItems: model.generateHeaderItems, + bodyBuilder: model.itemBodyBuilder, + bodyItems: model.generateBodyItems, + footerBuilder: null, + footerItems: null, + ), + ), + ] // + .addToStart(space) + .addToEnd(space), + ); + } +} + +/// [DocumentViewScreen] + +class DocumentViewScreen extends StatefulScreen { + const DocumentViewScreen({ + super.key, + required this.doc, + required this.uri, + }); + + final Document doc; + final Uri uri; + + @override + ScreenState createState() => _DocumentViewScreenState(); +} + +class _DocumentViewScreenState extends ScreenState { + @override + Widget build(BuildContext context) { + action() { + context.read().add(UnselectDocumentEvent()); + } + + final String title = widget.doc.description; + final theme = FlutterFlowTheme.of(context); + return PopScope( + canPop: false, + onPopInvokedWithResult: (didPop, result) => action(), + child: Scaffold( + backgroundColor: theme.primaryBackground, + appBar: buildAppBar(title, context, action), + body: buildBody(context), + ), + ); + } + + Widget buildBody(BuildContext context) { + // final PDFViewerKey _viewerKey = PDFViewerKey(); + + return ReadView( + // search: _viewerKey, + title: widget.doc.description, + url: widget.uri.toString(), + ); + } +} + +/// ----------------------------------------------- +/// [Interfaces] +/// ----------------------------------------------- + +abstract interface class Archive extends Entity {} + +interface class Document extends Archive { + final int id; + final String description; + final String type; + final Category category; + 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, + }); + + factory Document.fromDesc(String desc) => Document( + id: 0, + description: desc, + type: '', + category: Category.fromDesc(''), + person: '', + property: '', + createdAt: '', + updatedAt: '', + ); +} + +interface class Category extends Archive { + 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; +} + +/// ----------------------------------------------- +/// [Widgets] +/// ----------------------------------------------- + +// ignore: must_be_immutable +class DocumentItem extends StatelessComponent { + final Document document; + void Function(Document, BuildContext) onPressed; + + DocumentItem({ + super.key, + required this.document, + required this.onPressed, + }); + + Tooltip _buildTooltip(String text, Color color, BuildContext context, + BoxConstraints constraints) { + final Color textColor = FlutterFlowTheme.of(context).info; + + final area = (MediaQuery.of(context).size.height + + MediaQuery.of(context).size.width) / + 2; + + final double boxHeight = area * 0.033; + final double boxWidth = area * 0.19; + + return Tooltip( + message: text, + child: Container( + width: boxWidth, + height: boxHeight, + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(10), + ), + child: Center( + child: AutoText( + text, + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: textColor, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + final Color primaryText = FlutterFlowTheme.of(context).primaryText; + final Color primaryColor = FlutterFlowTheme.of(context).primary; + + final TextStyle textStyleMajor = TextStyle( + color: primaryText, + fontWeight: FontWeight.bold, + ); + final TextStyle textStyleMinor = TextStyle( + color: primaryText, + fontWeight: FontWeight.normal, + fontStyle: FontStyle.italic, + ); + + return Padding( + padding: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (context, constraints) { + final double boxHeight = constraints.maxHeight > 350 + ? MediaQuery.of(context).size.height * 0.07 + : MediaQuery.of(context).size.height * 2; + + return InkWell( + onTap: () => onPressed(document, context), + enableFeedback: true, + overlayColor: WidgetStateProperty.all(primaryColor), + borderRadius: BorderRadius.circular(10), + child: SizedBox( + height: boxHeight, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // const SizedBox(width: 10), + Icon(Icons.description, color: document.category.color), + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Tooltip( + message: document.description, + child: AutoText( + document.description, + style: textStyleMajor, + overflow: TextOverflow.ellipsis, + ), + ), + AutoText( + ValidatorUtil.toLocalDateTime( + 'yyyy-MM-dd', document.updatedAt), + style: textStyleMinor, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + _buildTooltip( + document.category.title, + document.category.color, + context, + constraints, + ), + ], + ), + ), + // const SizedBox(width: 10), + Center( + child: Icon( + Icons.arrow_right, + color: primaryText, + ), + ), + ], + ), + ), + ); + }, + ), + ); + } + + DocumentItem copyWith({ + Document? document, + }) { + return DocumentItem( + document: document ?? this.document, + onPressed: onPressed, + ); + } +} + +class CategoryItem extends StatelessComponent { + final Category category; + + const CategoryItem({ + super.key, + required this.category, + }); + + @override + Widget build(BuildContext context) { + final backgroundTheme = FlutterFlowTheme.of(context).primaryBackground; + return ColoredBox( + color: backgroundTheme, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + Container( + padding: const EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: category.color, + shape: BoxShape.circle, + ), + child: Icon( + Icons.folder, + color: Colors.white, + size: 40, + ), + ), + const SizedBox(height: 8), + Text( + category.title, + style: TextStyle( + color: category.color, + fontWeight: FontWeight.bold, + ), + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + ); + } +} diff --git a/lib/features/documents/index.dart b/lib/features/documents/index.dart deleted file mode 100644 index d3648a6d..00000000 --- a/lib/features/documents/index.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'dart:developer'; -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.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:share_plus/share_plus.dart'; - -part 'document_page_widget.dart'; -part 'document_screen_manager.dart'; -part 'document_screen_viewer.dart'; -part 'document_page_model.dart'; -part 'document_item_component.dart'; -part 'document_page_bloc.dart'; -part 'category_item_component.dart'; -part 'archive_item_component.dart'; diff --git a/lib/flutter_flow/nav/nav.dart b/lib/flutter_flow/nav/nav.dart index 42b6e6de..bf1a1051 100644 --- a/lib/flutter_flow/nav/nav.dart +++ b/lib/flutter_flow/nav/nav.dart @@ -4,7 +4,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:hub/features/backend/index.dart'; -import 'package:hub/features/documents/index.dart'; +import 'package:hub/features/documents/documents.dart'; import 'package:hub/features/history/index.dart'; import 'package:hub/features/home/index.dart'; import 'package:hub/features/local/index.dart'; diff --git a/lib/shared/widgets/enhanced_list_view.dart b/lib/shared/widgets/enhanced_list_view.dart index eb2ec5a1..506ffccf 100644 --- a/lib/shared/widgets/enhanced_list_view.dart +++ b/lib/shared/widgets/enhanced_list_view.dart @@ -179,117 +179,50 @@ class EnhancedListView class EnhancedListViewState extends State> { - final ScrollController _scrollController = ScrollController(); - bool _isLoadingMore = false; - List items = []; - List headerItems = []; - List footerItems = []; - int currentPage = 1; - Query query; + late EnhancedListViewBloc bloc; @override void initState() { super.initState(); - _scrollController.addListener(_onScroll); - _loadBodyItems(); - _loadHeaderItems(); - _loadFooterItems(); - } - - void _onScroll() { - if (_scrollController.position.pixels == - _scrollController.position.maxScrollExtent && - !_isLoadingMore) { - _loadMoreBodyItems(); - } - } - - Future _loadBodyItems() async { - log('loadInitialItems'); - final newItems = await widget.bodyItems(1, 10, query); - setState(() { - items = newItems; - }); - } - - Future _loadMoreBodyItems() async { - log('loadMoreItems'); - setState(() { - _isLoadingMore = true; - }); - final newItems = await widget.bodyItems(currentPage + 1, 10, query); - setState(() { - _isLoadingMore = false; - if (newItems.isNotEmpty) { - items.addAll(newItems); - currentPage++; - } - }); - } - - Future _loadHeaderItems() async { - if (widget.headerItems != null) { - log('loadHeaderItems'); - final newHeaderItems = await widget.headerItems!(); - setState(() { - headerItems = newHeaderItems; - }); - } - } - - Future _loadFooterItems() async { - if (widget.footerItems != null) { - log('loadFooterItems'); - final newFooterItems = await widget.footerItems!(); - setState(() { - footerItems = newFooterItems; - }); - } - } - - Future filterBodyItems(Query newQuery) async { - log('filterItems B: ${newQuery.toString()}'); - setState(() { - query = newQuery; - items = []; - currentPage = 1; - }); - await _loadBodyItems(); + bloc = EnhancedListViewBloc( + bodyItemsBuilder: widget.bodyItems, + headerItemsBuilder: widget.headerItems ?? () async => [], + footerItemsBuilder: widget.footerItems ?? () async => [], + ); + bloc.events.loadBodyItems(); + bloc.events.loadHeaderItems(); + bloc.events.loadFooterItems(); } @override Widget build(BuildContext context) { - log('key: ${widget.key}'); - return ListView.builder( - controller: _scrollController, - itemCount: items.length + - (headerItems.isNotEmpty ? 1 : 0) + - (footerItems.isNotEmpty ? 1 : 0) + - (_isLoadingMore ? 1 : 0), - itemBuilder: (context, index) { - if (headerItems.isNotEmpty && index == 0) { - return widget.headerBuilder!(() => Future.value(headerItems)); - } - if (footerItems.isNotEmpty && - index == items.length + (headerItems.isNotEmpty ? 1 : 0)) { - return widget.footerBuilder!(() => Future.value(footerItems)); - } - if (_isLoadingMore && - index == - items.length + - (headerItems.isNotEmpty ? 1 : 0) + - (footerItems.isNotEmpty ? 1 : 0)) { - return const EnhancedProgressIndicator(); - } - final item = items[index - (headerItems.isNotEmpty ? 1 : 0)]; - return widget.bodyBuilder(context, item as ItemType, index); + return StreamBuilder>( + stream: bloc.states.bodyItems.cast>(), + builder: (context, bodySnapshot) { + return StreamBuilder>( + stream: bloc.states.headerItems.cast>(), + builder: (context, headerSnapshot) { + return StreamBuilder>( + stream: bloc.states.footerItems.cast>(), + builder: (context, footerSnapshot) { + return ListView.builder( + itemCount: bodySnapshot.data?.length ?? 0, + itemBuilder: (context, index) { + return widget.bodyBuilder( + context, bodySnapshot.data![index], index); + }, + ); + }, + ); + }, + ); }, ); } @override void dispose() { - _scrollController.dispose(); + bloc.dispose(); super.dispose(); } } @@ -358,7 +291,7 @@ class AuthorizationError implements Exception { AuthorizationError(this.message); } -/// [State Managment] +/// [State Management] Stream get loadingState => Stream.empty(); Stream get errorState => Stream.empty(); @@ -367,111 +300,108 @@ abstract class EnhancedListViewRepository { int page, int pageSize, PaginatedListViewBodyBuilder builder); } -abstract class EnhancedListViewBlocStates { +abstract class EnhancedListViewEvents { + void loadBodyItems({bool reset = false}); + void loadHeaderItems(); + void loadFooterItems(); +} + +abstract class EnhancedListViewStates { + Stream> get bodyItems; + Stream> get headerItems; + Stream> get footerItems; Stream get isLoading; Stream get errors; - Stream> get paginatedList; - - @RxBlocIgnoreState() - Future get refreshDone; } -abstract class EnhancedListViewEvents { - void loadPage({bool reset = false}); -} - -abstract class EnhancedListViewBlocType extends RxBlocTypeBase { - EnhancedListViewEvents get events; - EnhancedListViewBlocStates get states; -} - -abstract class $EnhancedListViewBloc extends RxBlocBase - implements - EnhancedListViewEvents, - EnhancedListViewBlocStates, - EnhancedListViewBlocType { - final _compositeSubscription = CompositeSubscription(); - - final _$loadPageEvent = PublishSubject(); - - late final Stream _isLoadingState = _mapToIsLoadingState(); - late final Stream _errorsState = _mapToErrorsState(); - late final Stream> _paginatedListState = - _mapToPaginatedListState(); - - @override - void loadPage({bool reset = false}) => _$loadPageEvent.add(reset); - - @override - Stream get isLoading => _isLoadingState; - @override - Stream get errors => _errorsState; - @override - Stream> get paginatedList => _paginatedListState; - - Stream _mapToIsLoadingState(); - Stream _mapToErrorsState(); - Stream> _mapToPaginatedListState(); - - @override - EnhancedListViewEvents get events => this; - @override - EnhancedListViewBlocStates get states => this; - - @override - void dispose() { - _$loadPageEvent.close(); - _compositeSubscription.dispose(); - super.dispose(); - } -} - -class EnhancedListViewBloc extends $EnhancedListViewBloc { +@RxBloc() +class EnhancedListViewBloc extends $EnhancedListViewBloc { EnhancedListViewBloc({ - required EnhancedListViewRepository repository, - required PaginatedListViewBodyBuilder builder, - required T item, - int initialPageSize = 50, + required this.bodyItemsBuilder, + required this.headerItemsBuilder, + required this.footerItemsBuilder, }) { - _$loadPageEvent + _$loadBodyItemsEvent .startWith(true) - .fetchData( - repository, - (context, item, index) => builder(context, item, index), - _paginatedList, - ) - .setResultStateHandler(this) - .mergeWithPaginatedList(_paginatedList) - .bind(_paginatedList) + .switchMap((reset) => _fetchBodyItems(reset)) + .bind(_bodyItems) + .addTo(_compositeSubscription); + + _$loadHeaderItemsEvent + .switchMap((_) => _fetchHeaderItems()) + .bind(_headerItems) + .addTo(_compositeSubscription); + + _$loadFooterItemsEvent + .switchMap((_) => _fetchFooterItems()) + .bind(_footerItems) .addTo(_compositeSubscription); } - final _paginatedList = BehaviorSubject>.seeded( - EnhancedPaginatedList( - items: [], - pageSize: 1, - currentPage: 1, - error: Exception(), - isInitialized: true, - isLoading: false, - totalCount: 0, - ), - ); + final BodyItemsBuilder bodyItemsBuilder; + final HeaderItemsBuilder headerItemsBuilder; + final FooterItemsBuilder footerItemsBuilder; - @override - Future get refreshDone async => _paginatedList.value.awaitLoad(); + final _bodyItems = BehaviorSubject>.seeded([]); + final _headerItems = BehaviorSubject>.seeded([]); + final _footerItems = BehaviorSubject>.seeded([]); + final _isLoading = BehaviorSubject.seeded(false); + final _errors = BehaviorSubject(); - @override - Stream> _mapToPaginatedListState() => _paginatedList; - @override - Stream _mapToErrorsState() => - errorState.map((error) => error.toString()); - @override - Stream _mapToIsLoadingState() => loadingState; + Stream> _fetchBodyItems(bool reset) async* { + try { + _isLoading.add(true); + final items = await bodyItemsBuilder(1, 10, null); + yield items.whereType().toList(); + } catch (e) { + _errors.add(e.toString()); + } finally { + _isLoading.add(false); + } + } + + Stream> _fetchHeaderItems() async* { + try { + _isLoading.add(true); + final items = await headerItemsBuilder(); + yield items.whereType().toList(); + } catch (e) { + _errors.add(e.toString()); + } finally { + _isLoading.add(false); + } + } + + Stream> _fetchFooterItems() async* { + try { + _isLoading.add(true); + final items = await footerItemsBuilder(); + yield items.whereType().toList(); + } catch (e) { + _errors.add(e.toString()); + } finally { + _isLoading.add(false); + } + } @override void dispose() { - _paginatedList.close(); + _bodyItems.close(); + _headerItems.close(); + _footerItems.close(); + _isLoading.close(); + _errors.close(); super.dispose(); } + + @override + Stream> _mapToBodyItemsState() => _bodyItems.stream; + @override + Stream _mapToErrorsState() => _errors.stream; + @override + Stream> _mapToFooterItemsState() => _footerItems.stream; + @override + Stream> _mapToHeaderItemsState() => _headerItems.stream; + @override + Stream _mapToIsLoadingState() => _isLoading.stream; } diff --git a/lib/shared/widgets/widgets.dart b/lib/shared/widgets/widgets.dart index c09aeccf..be46da76 100644 --- a/lib/shared/widgets/widgets.dart +++ b/lib/shared/widgets/widgets.dart @@ -2,14 +2,10 @@ import 'dart:developer'; import 'dart:io'; 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:http/http.dart' as http; -import 'package:hub/features/documents/index.dart'; import 'package:hub/flutter_flow/index.dart'; -import 'package:hub/shared/mixins/pegeable_mixin.dart'; -import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:path_provider/path_provider.dart'; import 'package:pdfx/pdfx.dart'; import 'package:share_plus/share_plus.dart'; @@ -17,6 +13,8 @@ import 'package:rx_bloc_list/rx_bloc_list.dart'; import 'package:rxdart/rxdart.dart'; import 'package:rx_bloc/rx_bloc.dart'; +part 'widgets.rxb.g.dart'; + /// [Base] part 'page.dart'; part 'component.dart'; diff --git a/lib/shared/widgets/widgets.rxb.g.dart b/lib/shared/widgets/widgets.rxb.g.dart new file mode 100644 index 00000000..e8298e8e --- /dev/null +++ b/lib/shared/widgets/widgets.rxb.g.dart @@ -0,0 +1,98 @@ +// dart format width=80 +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ************************************************************************** +// Generator: RxBlocGeneratorForAnnotation +// ************************************************************************** + +part of 'widgets.dart'; + +/// Used as a contractor for the bloc, events and states classes +/// @nodoc +abstract class EnhancedListViewBlocType extends RxBlocTypeBase { + EnhancedListViewEvents get events; + EnhancedListViewStates get states; +} + +/// [$EnhancedListViewBloc] extended by the [EnhancedListViewBloc] +/// @nodoc +abstract class $EnhancedListViewBloc extends RxBlocBase + implements + EnhancedListViewEvents, + EnhancedListViewStates, + EnhancedListViewBlocType { + final _compositeSubscription = CompositeSubscription(); + + /// Тhe [Subject] where events sink to by calling [loadBodyItems] + final _$loadBodyItemsEvent = PublishSubject(); + + /// Тhe [Subject] where events sink to by calling [loadHeaderItems] + final _$loadHeaderItemsEvent = PublishSubject(); + + /// Тhe [Subject] where events sink to by calling [loadFooterItems] + final _$loadFooterItemsEvent = PublishSubject(); + + /// The state of [bodyItems] implemented in [_mapToBodyItemsState] + late final Stream> _bodyItemsState = _mapToBodyItemsState(); + + /// The state of [headerItems] implemented in [_mapToHeaderItemsState] + late final Stream> _headerItemsState = _mapToHeaderItemsState(); + + /// The state of [footerItems] implemented in [_mapToFooterItemsState] + late final Stream> _footerItemsState = _mapToFooterItemsState(); + + /// The state of [isLoading] implemented in [_mapToIsLoadingState] + late final Stream _isLoadingState = _mapToIsLoadingState(); + + /// The state of [errors] implemented in [_mapToErrorsState] + late final Stream _errorsState = _mapToErrorsState(); + + @override + void loadBodyItems({bool reset = false}) => _$loadBodyItemsEvent.add(reset); + + @override + void loadHeaderItems() => _$loadHeaderItemsEvent.add(null); + + @override + void loadFooterItems() => _$loadFooterItemsEvent.add(null); + + @override + Stream> get bodyItems => _bodyItemsState; + + @override + Stream> get headerItems => _headerItemsState; + + @override + Stream> get footerItems => _footerItemsState; + + @override + Stream get isLoading => _isLoadingState; + + @override + Stream get errors => _errorsState; + + Stream> _mapToBodyItemsState(); + + Stream> _mapToHeaderItemsState(); + + Stream> _mapToFooterItemsState(); + + Stream _mapToIsLoadingState(); + + Stream _mapToErrorsState(); + + @override + EnhancedListViewEvents get events => this; + + @override + EnhancedListViewStates get states => this; + + @override + void dispose() { + _$loadBodyItemsEvent.close(); + _$loadHeaderItemsEvent.close(); + _$loadFooterItemsEvent.close(); + _compositeSubscription.dispose(); + super.dispose(); + } +} From 0e895110e3b87b47fbab85d4769bec4891cda3e8 Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Tue, 18 Feb 2025 08:30:36 -0300 Subject: [PATCH 13/32] WIP --- lib/shared/widgets/enhanced_list_view.dart | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/shared/widgets/enhanced_list_view.dart b/lib/shared/widgets/enhanced_list_view.dart index 506ffccf..6db7ac5c 100644 --- a/lib/shared/widgets/enhanced_list_view.dart +++ b/lib/shared/widgets/enhanced_list_view.dart @@ -152,15 +152,14 @@ abstract interface class EnhancedListViewBase extends StatefulWidget { abstract interface class EnhancedListViewBaseState extends State {} -// ignore: must_be_immutable class EnhancedListView extends EnhancedListViewBase { - BodyItemsBuilder bodyItems; - PaginatedListViewBodyBuilder bodyBuilder; - HeaderItemsBuilder? headerItems; - PaginatedListViewHeaderBuilder? headerBuilder; - FooterItemsBuilder? footerItems; - PaginatedListViewFooterBuilder? footerBuilder; + final BodyItemsBuilder bodyItems; + final PaginatedListViewBodyBuilder bodyBuilder; + final HeaderItemsBuilder? headerItems; + final PaginatedListViewHeaderBuilder? headerBuilder; + final FooterItemsBuilder? footerItems; + final PaginatedListViewFooterBuilder? footerBuilder; EnhancedListView({ Key? key, From e2b0f7538d49357b8d86da45faad347f2371b077 Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Tue, 18 Feb 2025 09:12:14 -0300 Subject: [PATCH 14/32] WIP --- lib/features/documents/documents.dart | 16 +++++++-------- lib/shared/widgets/enhanced_list_view.dart | 24 ++++++++++++++++++++++ 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/lib/features/documents/documents.dart b/lib/features/documents/documents.dart index 66e68194..cf7f214a 100644 --- a/lib/features/documents/documents.dart +++ b/lib/features/documents/documents.dart @@ -231,9 +231,7 @@ class DocumentPageModel extends FlutterFlowModel { /// [filter] void filter(T query, BuildContext context) { - context - .read() - .add(FilterCategoryEvent(query as Archive?)); + vehicleScreenManager.currentState!.filterBodyItems(query); } /// [onFetchError] @@ -280,9 +278,9 @@ class DocumentPageBloc extends Bloc { isCategorySelected: true, )); - final listViewState = model.vehicleScreenManager.currentState!; - listViewState.widget.bodyItems = (await model.generateBodyItems( - 1, 10, event.query)) as BodyItemsBuilder; + // final listViewState = model.vehicleScreenManager.currentState!; + // listViewState.widget.bodyItems = (await model.generateBodyItems( + // 1, 10, event.query)) as BodyItemsBuilder; } Future _unselectCategory( @@ -291,9 +289,9 @@ class DocumentPageBloc extends Bloc { isCategorySelected: false, )); - final listViewState = model.vehicleScreenManager.currentState!; - listViewState.widget.bodyItems = (await model.generateBodyItems( - 1, 10, null)) as BodyItemsBuilder; + // final listViewState = model.vehicleScreenManager.currentState!; + // listViewState.widget.bodyItems = (await model.generateBodyItems( + // 1, 10, null)) as BodyItemsBuilder; } Future _selectDocument( diff --git a/lib/shared/widgets/enhanced_list_view.dart b/lib/shared/widgets/enhanced_list_view.dart index 6db7ac5c..96396739 100644 --- a/lib/shared/widgets/enhanced_list_view.dart +++ b/lib/shared/widgets/enhanced_list_view.dart @@ -193,6 +193,18 @@ class EnhancedListViewState bloc.events.loadFooterItems(); } + void filterBodyItems(Query query) { + bloc.filterBodyItems(query); + } + + void filterHeaderItems(Query query) { + bloc.filterHeaderItems(query); + } + + void filterFooterItems(Query query) { + bloc.filterFooterItems(query); + } + @override Widget build(BuildContext context) { return StreamBuilder>( @@ -383,6 +395,18 @@ class EnhancedListViewBloc extends $EnhancedListViewBloc { } } + void filterBodyItems(Query query) { + _$loadBodyItemsEvent.add(true); + } + + void filterHeaderItems(Query query) { + _$loadHeaderItemsEvent.add(null); + } + + void filterFooterItems(Query query) { + _$loadFooterItemsEvent.add(null); + } + @override void dispose() { _bodyItems.close(); From 963ccb64db7304fad024e62d29d6489cbd62a166 Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Tue, 18 Feb 2025 16:11:55 -0300 Subject: [PATCH 15/32] WIP --- lib/features/documents/documents.dart | 527 +++++++++++--------- lib/features/documents/documents.rxb.g.dart | 70 +++ lib/flutter_flow/nav/nav.dart | 30 +- lib/shared/widgets/enhanced_list_view.dart | 149 ++++-- lib/shared/widgets/page.dart | 33 +- lib/shared/widgets/widgets.dart | 1 + lib/shared/widgets/widgets.rxb.g.dart | 18 +- 7 files changed, 517 insertions(+), 311 deletions(-) create mode 100644 lib/features/documents/documents.rxb.g.dart diff --git a/lib/features/documents/documents.dart b/lib/features/documents/documents.dart index cf7f214a..c105a0b5 100644 --- a/lib/features/documents/documents.dart +++ b/lib/features/documents/documents.dart @@ -1,19 +1,27 @@ import 'dart:developer'; +import 'package:easy_debounce/easy_debounce.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:hub/components/templates_components/details_component/details_component_widget.dart'; 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/utils/index.dart'; import 'package:hub/shared/widgets/widgets.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_rx_bloc/flutter_rx_bloc.dart'; + +import 'package:rx_bloc/rx_bloc.dart'; +import 'package:rxdart/rxdart.dart' as rx; + +part 'documents.rxb.g.dart'; /// ----------------------------------------------- /// [TypeDefs] /// ----------------------------------------------- -typedef DocumentKey = GlobalKey; +typedef DocumentKey = GlobalKey; /// ----------------------------------------------- @@ -24,43 +32,54 @@ class DocumentPage extends StatefulPage { const DocumentPage({super.key}); @override - State createState() => FREDocumentPageState(); + State createState() => DocumentPageState(); } -class FREDocumentPageState - extends PageState { - @override - Widget build(BuildContext context) => buildBody(context); - DocumentPageModel model = DocumentPageModel(); - +class DocumentPageState extends PageState { @override void initState() { super.initState(); - model.initState(context); } - Widget buildBody(BuildContext context) { + @override + Widget build(BuildContext context) { log('Build -> DocumentPage'); - return BlocProvider( - create: (context) => DocumentPageBloc(model), - child: BlocBuilder( - builder: (context, state) { - log('Build -> DocumentPageBloc'); - print('Bloc -> ${state.isCategorySelected}'); - if (state.isDocumentSelected) - return DocumentViewScreen( - doc: state.currentDocument!, - uri: state.uri!, - ); - else - return DocumentManagerScreen( - model: model, - state: state, - ); - }), + return RxBlocMultiBuilder2( + state1: (bloc) => bloc.states.isDocumentSelected, + state2: (bloc) => bloc.states.currentDocument, + bloc: context.read(), + builder: (context, isSelect, current, bloc) { + log('-> Build -> DocumentPage -> RxBlocMultiBuilder2'); + if (isSelect.hasData && isSelect.data!) { + return _buildDocumentViewScreen(current, bloc); + } else { + return _buildDocumentManagerScreen(); + } + }, ); } + + Widget _buildDocumentManagerScreen() { + final model = context.read().model; + + return DocumentManagerScreen( + model: model, + state: this, + ); + } + + Widget _buildDocumentViewScreen( + AsyncSnapshot<(Document, Uri)?> snapshot, DocumentPageBlocType bloc) { + if (snapshot.hasData) { + return DocumentViewScreen( + doc: snapshot.data!, + bloc: bloc, + ); + } else { + return const Center(child: CircularProgressIndicator()); + } + } } /// ----------------------------------------------- @@ -69,27 +88,26 @@ class FREDocumentPageState /// ----------------------------------------------- class DocumentPageModel extends FlutterFlowModel { - DocumentPageModel._privateConstructor(); + final DocumentPageBlocType bloc; + DocumentPageModel(this.bloc); - static final DocumentPageModel _instance = - DocumentPageModel._privateConstructor(); - - factory DocumentPageModel() { - return _instance; - } - - late EnhancedListViewKey vehicleScreenManager; + late EnhancedListViewKey + vehicleScreenManager; late DocumentKey vehicleScreenViewer; late PagingController _pagingController; + late bool categoryIsSelected; + /// ------------ @override void initState(BuildContext context) { - vehicleScreenManager = EnhancedListViewKey(); + vehicleScreenManager = + EnhancedListViewKey(); vehicleScreenViewer = DocumentKey(); - _pagingController = PagingController(firstPageKey: 1); + + categoryIsSelected = false; } @override @@ -101,58 +119,25 @@ class DocumentPageModel extends FlutterFlowModel { /// ------------ - /// [onView] + /// [Body] + void onView(Document document, BuildContext context) async { - vehicleScreenManager.currentContext! - .read() - .add(SelectDocumentEvent(document)); + log('Disparando evento selectDocument'); + bloc.events.selectDocument(document); } - /// [itemBodyBuilder] - DocumentItem itemBodyBuilder( + Widget itemBodyBuilder( BuildContext context, T item, int index) { print('ItemBuilder -> $index'); + return DocumentItem( document: item, onPressed: onView, ); } - CategoryItem categoryItemBuilder(T? item) { - return CategoryItem(category: item! as Category); - } - - /// [itemHeaderBuilder] - Widget itemHeaderBuilder(Future> Function() gen) => - Builder(builder: (context) { - return Column( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(15, 0, 50, 0), - child: Text( - FFLocalizations.of(context).getVariableText( - enText: 'Recent Documents', ptText: 'Últimos Documentos'), - style: TextStyle( - color: FlutterFlowTheme.of(context).primaryText, - fontSize: LimitedFontSizeUtil.getHeaderFontSize(context), - ), - ), - ), - EnhancedCarouselView( - generateItems: gen, - itemBuilder: categoryItemBuilder, - filter: filter, - ), - ], - ); - }); - - /// [generateBodyItems] - Future> generateBodyItems( - int pageKey, int pageSize, dynamic query) async { + Future> generateBodyItems( + int pageKey, int pageSize, Q query) async { log('generateDocuments: $query'); final List error = [null]; @@ -201,8 +186,42 @@ class DocumentPageModel extends FlutterFlowModel { return docs as List; } - /// [generateHeaderItems] - Future> generateHeaderItems() async { + /// [Footer] + + CategoryItem categoryItemBuilder(T? item) { + return CategoryItem(category: item! as Category); + } + + Widget itemFooterBuilder(Future> Function() gen) => + Builder(builder: (context) { + return Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(15, 0, 50, 0), + child: Text( + FFLocalizations.of(context).getVariableText( + ptText: 'Suas Categorias', + enText: 'Your Categories', + ), + style: TextStyle( + color: FlutterFlowTheme.of(context).primaryText, + fontSize: LimitedFontSizeUtil.getHeaderFontSize(context), + ), + ), + ), + EnhancedCarouselView( + generateItems: gen, + itemBuilder: categoryItemBuilder, + filter: filterByCategory, + ), + ], + ); + }); + + Future> generateFooterItems() async { log('generateCategories: '); final List error = [null]; @@ -229,12 +248,105 @@ class DocumentPageModel extends FlutterFlowModel { return cats as List; } - /// [filter] - void filter(T query, BuildContext context) { - vehicleScreenManager.currentState!.filterBodyItems(query); + /// [Header] + + Widget itemHeaderBuilder( + Future> Function() generateHeaderItems) { + return Builder(builder: (context) { + final theme = FlutterFlowTheme.of(context); + final locale = FFLocalizations.of(context); + TextEditingController editingController = TextEditingController(); + return TextFormField( + controller: editingController, + onChanged: (value) => EasyDebounce.debounce( + '_model.keyTextFieldTextController', + const Duration(milliseconds: 500), + () => filterBySearchBar(Document.fromDesc(value), context), + ), + cursorColor: theme.primaryText, + showCursor: false, + cursorWidth: 2.0, + cursorRadius: Radius.circular(100), + style: TextStyle( + color: theme.primaryText, + fontSize: 16.0, + decorationColor: Colors.amber, + ), + keyboardType: TextInputType.text, + textInputAction: TextInputAction.search, + autocorrect: true, + textCapitalization: TextCapitalization.sentences, + decoration: InputDecoration( + prefixIcon: Icon(Icons.search, color: theme.primary), + labelText: locale.getVariableText( + ptText: 'Pesquisar', + enText: 'Search', + ), + labelStyle: TextStyle( + color: theme.primaryText, + fontSize: 16.0, + ), + hintText: locale.getVariableText( + ptText: 'Digite sua pesquisa', + enText: 'Enter your search', + ), + hintStyle: TextStyle( + color: theme.accent2, + fontSize: 14.0, + ), + filled: true, + fillColor: Colors.transparent, + helperStyle: TextStyle( + color: theme.primaryText, + decorationColor: theme.primaryText, + ), + focusColor: theme.primaryText, + contentPadding: + EdgeInsets.symmetric(vertical: 10.0, horizontal: 15.0), + enabledBorder: UnderlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(15.0)), + borderSide: BorderSide(color: theme.primaryText), + ), + focusedBorder: UnderlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(15.0)), + borderSide: BorderSide(color: theme.primaryText), + ), + errorBorder: UnderlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(15.0)), + borderSide: BorderSide(color: theme.primaryText), + ), + focusedErrorBorder: UnderlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(15.0)), + borderSide: BorderSide(color: theme.primaryText, width: 2.0), + ), + ), + ); + }); } - /// [onFetchError] + Future> generateHeaderItems() async { + final Search item = Search(); + return [item] as List; + } + + /// [Filter] + + void filterBySearchBar(T query, BuildContext context) { + final key = vehicleScreenManager.currentState; + return key?.filterBodyItems(query); + } + + void filterByCategory(T query, BuildContext context) { + final key = vehicleScreenManager.currentState; + + categoryIsSelected + ? key?.filterBodyItems(null) + : key?.filterBodyItems(query); + + categoryIsSelected = !categoryIsSelected; + } + + /// [Exception] void onFetchError(Object e, StackTrace s) { DialogUtil.errorDefault(vehicleScreenViewer.currentContext!); LogUtil.requestAPIFailed( @@ -246,145 +358,45 @@ class DocumentPageModel extends FlutterFlowModel { /// [BLoC] /// ----------------------------------------------- -/// [DocumentPageBloc] - -class DocumentPageBloc extends Bloc { - final DocumentPageModel model; - static DocumentPageBloc? _singleton; - - factory DocumentPageBloc(DocumentPageModel model) { - _singleton ??= DocumentPageBloc._internal(model); - return _singleton!; - } - - DocumentPageBloc._internal(this.model) : super(DocumentPageState()) { - on(_selectDocument); - on(_unselectDocument); - on(_filterCategoryEvent); - } - - Future _filterCategoryEvent( - FilterCategoryEvent event, Emitter emit) async { - _selectCategory(event, emit); - state.isCategorySelected - ? _unselectCategory(event, emit) - : _selectCategory(event, emit); - } - - Future _selectCategory( - FilterCategoryEvent event, Emitter emit) async { - log('filterItems A: ${event.query}'); - emit(state.copyWith( - isCategorySelected: true, - )); - - // final listViewState = model.vehicleScreenManager.currentState!; - // listViewState.widget.bodyItems = (await model.generateBodyItems( - // 1, 10, event.query)) as BodyItemsBuilder; - } - - Future _unselectCategory( - FilterCategoryEvent event, Emitter emit) async { - emit(state.copyWith( - isCategorySelected: false, - )); - - // final listViewState = model.vehicleScreenManager.currentState!; - // listViewState.widget.bodyItems = (await model.generateBodyItems( - // 1, 10, null)) as BodyItemsBuilder; - } - - Future _selectDocument( - SelectDocumentEvent event, Emitter emit) async { - print('-> select'); - emit( - state.copyWith( - uri: await GetPDF().call( - event.document.id, - ), - currentDocument: event.document, - isDocumentSelected: true, - ), - ); - } - - Future _unselectDocument( - UnselectDocumentEvent event, Emitter emit) async { - emit( - state.copyWith( - currentDocument: null, - isDocumentSelected: false, - ), - ); - } +extension RxdartStartWithExtension on Stream { + Stream rxdartStartWith(T? value) => + rx.StartWithExtension(this).startWith(value); } -/// [DocumentPageEvent] - -abstract class DocumentPageEvent {} - -class SelectDocumentEvent extends DocumentPageEvent { - final Document document; - SelectDocumentEvent( - this.document, - ); +abstract class DocumentPageBlocEvents { + void selectDocument(Document document); + void unselectDocument(); } -class UnselectDocumentEvent extends DocumentPageEvent {} - -class FilterCategoryEvent extends DocumentPageEvent { - final Query query; - FilterCategoryEvent(this.query); +abstract class DocumentPageBlocStates { + Stream get isDocumentSelected; + Stream<(Document, Uri)?> get currentDocument; } -/// [DocumentPageState] +@RxBloc() +class DocumentPageBloc extends $DocumentPageBloc { + late final DocumentPageModel model; -class DocumentPageState { - final bool isCategorySelected; - final bool isDocumentSelected; - final Document? currentDocument; - final Category? currentCategory; - final Uri? uri; - final int? count; - final dynamic page; - final Query? query; - - const DocumentPageState({ - this.query, - this.count, - this.page, - this.uri, - this.currentDocument, - this.isCategorySelected = false, - this.currentCategory, - this.isDocumentSelected = false, - }); - - DocumentPageState copyWith({ - Uri? uri, - Query? query, - int? count, - dynamic page, - List? documents, - Document? currentDocument, - bool? isDocumentSelected, - List? categories, - Category? currentCategory, - bool? isCategorySelected, - }) { - return DocumentPageState( - uri: uri ?? this.uri, - query: query ?? this.query, - count: count ?? this.count, - page: page ?? this.page, - // - currentDocument: currentDocument ?? this.currentDocument, - isDocumentSelected: isDocumentSelected ?? this.isDocumentSelected, - // - currentCategory: currentCategory ?? this.currentCategory, - isCategorySelected: isCategorySelected ?? this.isCategorySelected, - ); + DocumentPageBloc(BuildContext context) { + model = DocumentPageModel(this); + model.initState(context); } + + @override + Stream<(Document, Uri)?> _mapToCurrentDocumentState() => _$selectDocumentEvent + .switchMap((event) async* { + log('Evento selectDocument recebido: ${event.description}'); + final uri = await GetPDF().call(event.id); + yield (event, uri); + }) + .rxdartStartWith(null) + .mergeWith([_$unselectDocumentEvent.map((_) => null)]); + + @override + Stream _mapToIsDocumentSelectedState() => _mapToCurrentDocumentState() + .map((document) => document != null) + .rxdartStartWith(false) + .map((isSelected) => isSelected ?? false); } /// ----------------------------------------------- @@ -425,14 +437,14 @@ class DocumentManagerScreen extends StatelessScreen { return Column( children: [ Expanded( - child: EnhancedListView( + child: EnhancedListView( key: model.vehicleScreenManager, - headerBuilder: model.itemHeaderBuilder, - headerItems: model.generateHeaderItems, + headerBuilder: model.itemHeaderBuilder, + headerItems: model.generateHeaderItems, bodyBuilder: model.itemBodyBuilder, - bodyItems: model.generateBodyItems, - footerBuilder: null, - footerItems: null, + bodyItems: model.generateBodyItems, + footerBuilder: model.itemFooterBuilder, + footerItems: model.generateFooterItems, ), ), ] // @@ -448,11 +460,11 @@ class DocumentViewScreen extends StatefulScreen { const DocumentViewScreen({ super.key, required this.doc, - required this.uri, + required this.bloc, }); - final Document doc; - final Uri uri; + final (Document, Uri) doc; + final DocumentPageBlocType bloc; @override ScreenState createState() => _DocumentViewScreenState(); @@ -461,18 +473,59 @@ class DocumentViewScreen extends StatefulScreen { class _DocumentViewScreenState extends ScreenState { @override Widget build(BuildContext context) { - action() { - context.read().add(UnselectDocumentEvent()); - } - - final String title = widget.doc.description; + final String title = widget.doc.$1.description; final theme = FlutterFlowTheme.of(context); + final locale = FFLocalizations.of(context); + + backAction() => widget.bloc.events.unselectDocument(); + infoAction() async => await showDialog( + useSafeArea: true, + context: context, + builder: (context) { + return Material( + child: DetailsComponentWidget( + buttons: [], + statusHashMap: [], + labelsHashMap: Map.from({ + locale.getVariableText( + enText: 'Description', + ptText: 'Descrição', + ): widget.doc.$1.description, + locale.getVariableText( + enText: 'Type', + ptText: 'Tipo', + ): widget.doc.$1.type, + locale.getVariableText( + enText: 'Category', + ptText: 'Categoria', + ): widget.doc.$1.category.title, + locale.getVariableText( + enText: 'Person', + ptText: 'Pessoa', + ): widget.doc.$1.person, + locale.getVariableText( + enText: 'Property', + ptText: 'Propriedade', + ): widget.doc.$1.property, + locale.getVariableText( + enText: 'Created At', + ptText: 'Criado em', + ): widget.doc.$1.createdAt, + locale.getVariableText( + enText: 'Updated At', + ptText: 'Atualizado em', + ): widget.doc.$1.updatedAt, + }), + ), + ); + }); + return PopScope( canPop: false, - onPopInvokedWithResult: (didPop, result) => action(), + onPopInvokedWithResult: (didPop, result) => backAction(), child: Scaffold( backgroundColor: theme.primaryBackground, - appBar: buildAppBar(title, context, action), + appBar: buildAppBar(title, context, backAction, infoAction), body: buildBody(context), ), ); @@ -483,8 +536,8 @@ class _DocumentViewScreenState extends ScreenState { return ReadView( // search: _viewerKey, - title: widget.doc.description, - url: widget.uri.toString(), + title: widget.doc.$1.description, + url: widget.doc.$2.toString(), ); } } @@ -495,6 +548,10 @@ class _DocumentViewScreenState extends ScreenState { abstract interface class Archive extends Entity {} +interface class Search extends Archive { + Search(); +} + interface class Document extends Archive { final int id; final String description; diff --git a/lib/features/documents/documents.rxb.g.dart b/lib/features/documents/documents.rxb.g.dart new file mode 100644 index 00000000..38bc4c6b --- /dev/null +++ b/lib/features/documents/documents.rxb.g.dart @@ -0,0 +1,70 @@ +// dart format width=80 +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ************************************************************************** +// Generator: RxBlocGeneratorForAnnotation +// ************************************************************************** + +part of 'documents.dart'; + +/// Used as a contractor for the bloc, events and states classes +/// @nodoc +abstract class DocumentPageBlocType extends RxBlocTypeBase { + DocumentPageBlocEvents get events; + DocumentPageBlocStates get states; +} + +/// [$DocumentPageBloc] extended by the [DocumentPageBloc] +/// @nodoc +abstract class $DocumentPageBloc extends RxBlocBase + implements + DocumentPageBlocEvents, + DocumentPageBlocStates, + DocumentPageBlocType { + final _compositeSubscription = rx.CompositeSubscription(); + + /// Тhe [Subject] where events sink to by calling [selectDocument] + final _$selectDocumentEvent = rx.PublishSubject(); + + /// Тhe [Subject] where events sink to by calling [unselectDocument] + final _$unselectDocumentEvent = rx.PublishSubject(); + + /// The state of [isDocumentSelected] implemented in + /// [_mapToIsDocumentSelectedState] + late final Stream _isDocumentSelectedState = + _mapToIsDocumentSelectedState(); + + /// The state of [currentDocument] implemented in [_mapToCurrentDocumentState] + late final Stream<(Document, Uri)?> _currentDocumentState = + _mapToCurrentDocumentState(); + + @override + void selectDocument(Document document) => _$selectDocumentEvent.add(document); + + @override + void unselectDocument() => _$unselectDocumentEvent.add(null); + + @override + Stream get isDocumentSelected => _isDocumentSelectedState; + + @override + Stream<(Document, Uri)?> get currentDocument => _currentDocumentState; + + Stream _mapToIsDocumentSelectedState(); + + Stream<(Document, Uri)?> _mapToCurrentDocumentState(); + + @override + DocumentPageBlocEvents get events => this; + + @override + DocumentPageBlocStates get states => this; + + @override + void dispose() { + _$selectDocumentEvent.close(); + _$unselectDocumentEvent.close(); + _compositeSubscription.dispose(); + super.dispose(); + } +} diff --git a/lib/flutter_flow/nav/nav.dart b/lib/flutter_flow/nav/nav.dart index bf1a1051..26396286 100644 --- a/lib/flutter_flow/nav/nav.dart +++ b/lib/flutter_flow/nav/nav.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_rx_bloc/flutter_rx_bloc.dart'; import 'package:hub/features/backend/index.dart'; import 'package:hub/features/documents/documents.dart'; import 'package:hub/features/history/index.dart'; @@ -303,22 +304,25 @@ GoRouter createRouter(AppStateNotifier appStateNotifier) { name: 'documentPage', path: '/documentPage', builder: (context, params) { - return DocumentPage(); - }, - ), - FFRoute( - name: 'documentViewerScreen', - path: '/documentViewerScreen', - builder: (context, params) { - final Document doc = params.getParam('doc', ParamType.Function); - final Uri uri = params.getParam('uri', ParamType.Function); - return DocumentViewScreen( - key: UniqueKey(), - doc: doc, - uri: uri, + return RxBlocProvider( + create: (context) => DocumentPageBloc(context), + child: DocumentPage(), ); }, ), + // FFRoute( + // name: 'documentViewerScreen', + // path: '/documentViewerScreen', + // builder: (context, params) { + // final Document doc = params.getParam('doc', ParamType.Function); + // final Uri uri = params.getParam('uri', ParamType.Function); + // return DocumentViewScreen( + // key: UniqueKey(), + // doc: (doc, + // uri: uri, + // ); + // }, + // ), // FFRoute(name: 'settingsPage', path: '/settingsPage', builder: (context, params) => params.isEmpty ? const NavBarPage(initialPage: 'settingsPage') : const SettingsPageWidget()) ].map((r) => r.toRoute(appStateNotifier)).toList(), ); diff --git a/lib/shared/widgets/enhanced_list_view.dart b/lib/shared/widgets/enhanced_list_view.dart index 96396739..a24af23c 100644 --- a/lib/shared/widgets/enhanced_list_view.dart +++ b/lib/shared/widgets/enhanced_list_view.dart @@ -2,8 +2,8 @@ part of 'widgets.dart'; /// [TypeDefs] -typedef EnhancedListViewKey - = GlobalKey>; +typedef EnhancedListViewKey + = GlobalKey>; typedef PaginatedListViewHeaderBuilder = Widget Function( Future> Function() headerItems); @@ -14,8 +14,8 @@ typedef PaginatedListViewFooterBuilder = Widget Function( typedef Query = T?; -typedef BodyItemsBuilder = Future> Function( - int page, int pageSize, Query query); +typedef BodyItemsBuilder = Future> Function( + int page, int pageSize, Q query); typedef HeaderItemsBuilder = Future> Function(); typedef FooterItemsBuilder = Future> Function(); @@ -145,16 +145,17 @@ interface class EnhancedPaginatedList extends PaginatedList { Future awaitLoad() async => Future.value(); } -abstract interface class EnhancedListViewBase extends StatefulWidget { +abstract interface class EnhancedListViewBase + extends StatefulWidget { const EnhancedListViewBase({super.key}); } abstract interface class EnhancedListViewBaseState extends State {} -class EnhancedListView - extends EnhancedListViewBase { - final BodyItemsBuilder bodyItems; +class EnhancedListView + extends EnhancedListViewBase { + final BodyItemsBuilder bodyItems; final PaginatedListViewBodyBuilder bodyBuilder; final HeaderItemsBuilder? headerItems; final PaginatedListViewHeaderBuilder? headerBuilder; @@ -172,18 +173,21 @@ class EnhancedListView }) : super(key: key); @override - EnhancedListViewState createState() => - EnhancedListViewState(); + EnhancedListViewState + createState() => + EnhancedListViewState(); } -class EnhancedListViewState - extends State> { - late EnhancedListViewBloc bloc; +class EnhancedListViewState + extends State< + EnhancedListView> { + late EnhancedListViewBloc bloc; @override void initState() { super.initState(); - bloc = EnhancedListViewBloc( + + bloc = EnhancedListViewBloc( bodyItemsBuilder: widget.bodyItems, headerItemsBuilder: widget.headerItems ?? () async => [], footerItemsBuilder: widget.footerItems ?? () async => [], @@ -193,41 +197,71 @@ class EnhancedListViewState bloc.events.loadFooterItems(); } - void filterBodyItems(Query query) { - bloc.filterBodyItems(query); - } + void filterBodyItems(Query query) => bloc.filterBodyItems(query); - void filterHeaderItems(Query query) { - bloc.filterHeaderItems(query); - } + void filterHeaderItems(Query query) => bloc.filterHeaderItems(query); - void filterFooterItems(Query query) { - bloc.filterFooterItems(query); - } + void filterFooterItems(Query query) => bloc.filterFooterItems(query); @override Widget build(BuildContext context) { - return StreamBuilder>( - stream: bloc.states.bodyItems.cast>(), - builder: (context, bodySnapshot) { - return StreamBuilder>( - stream: bloc.states.headerItems.cast>(), - builder: (context, headerSnapshot) { - return StreamBuilder>( - stream: bloc.states.footerItems.cast>(), - builder: (context, footerSnapshot) { - return ListView.builder( - itemCount: bodySnapshot.data?.length ?? 0, - itemBuilder: (context, index) { - return widget.bodyBuilder( - context, bodySnapshot.data![index], index); - }, - ); + final header = StreamBuilder>( + stream: bloc.states.headerItems.cast>(), + builder: (context, headerSnapshot) { + if (headerSnapshot.connectionState == ConnectionState.waiting) { + return const EnhancedProgressIndicator(); + } else if (headerSnapshot.hasError) { + return EnhancedErrorWidget(error: headerSnapshot.error); + } else if (!headerSnapshot.hasData || headerSnapshot.data!.isEmpty) { + return const SizedBox.shrink(); + } else { + return widget.headerBuilder!(() async => headerSnapshot.data!); + } + }, + ); + final footer = StreamBuilder>( + stream: bloc.states.footerItems.cast>(), + builder: (context, footerSnapshot) { + if (footerSnapshot.connectionState == ConnectionState.waiting) { + return const EnhancedProgressIndicator(); + } else if (footerSnapshot.hasError) { + return EnhancedErrorWidget(error: footerSnapshot.error); + } else if (!footerSnapshot.hasData || footerSnapshot.data!.isEmpty) { + return const SizedBox.shrink(); + } else { + return widget.footerBuilder!(() async => footerSnapshot.data!); + } + }, + ); + final body = Expanded( + child: StreamBuilder>( + stream: bloc.states.bodyItems.cast>(), + builder: (context, bodySnapshot) { + if (bodySnapshot.connectionState == ConnectionState.waiting) { + return const EnhancedProgressIndicator(); + } else if (bodySnapshot.hasError) { + return EnhancedErrorWidget(error: bodySnapshot.error); + } else if (!bodySnapshot.hasData || bodySnapshot.data!.isEmpty) { + return const SizedBox.shrink(); + } else { + return ListView.builder( + itemCount: bodySnapshot.data?.length ?? 0, + itemBuilder: (context, index) { + return widget.bodyBuilder( + context, bodySnapshot.data![index], index); }, ); - }, - ); - }, + } + }, + ), + ); + + return Column( + children: [ + if (widget.headerBuilder != null) header, + body, + if (widget.footerBuilder != null) footer, + ], ); } @@ -311,13 +345,13 @@ abstract class EnhancedListViewRepository { int page, int pageSize, PaginatedListViewBodyBuilder builder); } -abstract class EnhancedListViewEvents { - void loadBodyItems({bool reset = false}); +abstract class EnhancedListViewEvents { + void loadBodyItems({bool reset = false, dynamic query = null}); void loadHeaderItems(); void loadFooterItems(); } -abstract class EnhancedListViewStates { +abstract class EnhancedListViewStates { Stream> get bodyItems; Stream> get headerItems; Stream> get footerItems; @@ -326,15 +360,16 @@ abstract class EnhancedListViewStates { } @RxBloc() -class EnhancedListViewBloc extends $EnhancedListViewBloc { +class EnhancedListViewBloc + extends $EnhancedListViewBloc { EnhancedListViewBloc({ required this.bodyItemsBuilder, required this.headerItemsBuilder, required this.footerItemsBuilder, }) { _$loadBodyItemsEvent - .startWith(true) - .switchMap((reset) => _fetchBodyItems(reset)) + .startWith((query: null, reset: true)) + .switchMap((item) => _fetchBodyItems(item.reset, item.query)) .bind(_bodyItems) .addTo(_compositeSubscription); @@ -349,20 +384,23 @@ class EnhancedListViewBloc extends $EnhancedListViewBloc { .addTo(_compositeSubscription); } - final BodyItemsBuilder bodyItemsBuilder; + final BodyItemsBuilder bodyItemsBuilder; final HeaderItemsBuilder headerItemsBuilder; final FooterItemsBuilder footerItemsBuilder; final _bodyItems = BehaviorSubject>.seeded([]); + final _headerItems = BehaviorSubject>.seeded([]); + final _footerItems = BehaviorSubject>.seeded([]); + final _isLoading = BehaviorSubject.seeded(false); final _errors = BehaviorSubject(); - Stream> _fetchBodyItems(bool reset) async* { + Stream> _fetchBodyItems(bool reset, Q? query) async* { try { _isLoading.add(true); - final items = await bodyItemsBuilder(1, 10, null); + final items = await bodyItemsBuilder(1, 10, query); yield items.whereType().toList(); } catch (e) { _errors.add(e.toString()); @@ -395,15 +433,16 @@ class EnhancedListViewBloc extends $EnhancedListViewBloc { } } - void filterBodyItems(Query query) { - _$loadBodyItemsEvent.add(true); + void filterBodyItems(Q query) { + print('filterBodyItems Q: ${query == null}'); + _$loadBodyItemsEvent.add((query: query, reset: true)); } - void filterHeaderItems(Query query) { + void filterHeaderItems(Q query) { _$loadHeaderItemsEvent.add(null); } - void filterFooterItems(Query query) { + void filterFooterItems(Q query) { _$loadFooterItemsEvent.add(null); } diff --git a/lib/shared/widgets/page.dart b/lib/shared/widgets/page.dart index 87059a12..cf9a9b76 100644 --- a/lib/shared/widgets/page.dart +++ b/lib/shared/widgets/page.dart @@ -3,9 +3,10 @@ part of 'widgets.dart'; mixin Template { PreferredSizeWidget buildAppBar( String title, - BuildContext context, + BuildContext context, [ dynamic Function()? backAction, - ) { + dynamic Function()? frontAction, + ]) { final theme = FlutterFlowTheme.of(context); return AppBar( backgroundColor: FlutterFlowTheme.of(context).primaryBackground, @@ -25,12 +26,36 @@ mixin Template { leading: _backButton(context, theme, backAction), centerTitle: true, elevation: 0.0, - actions: [], + actions: _frontButton(context, theme, frontAction), ); } - Widget _backButton(BuildContext context, FlutterFlowTheme theme, + List _frontButton(BuildContext context, FlutterFlowTheme theme, + dynamic Function()? action) { + if (action == null) return []; + return [ + IconButton( + onPressed: () async => await showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('Info'), + content: Text('This is a sample app.'), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: Text('Close')) + ], + )), + icon: Icon( + Symbols.info_i_rounded, + color: Colors.black, + )) + ]; + } + + Widget? _backButton(BuildContext context, FlutterFlowTheme theme, dynamic Function()? onPressed) { + if (onPressed == null) return null; return FlutterFlowIconButton( key: ValueKey('BackNavigationAppBar'), borderColor: Colors.transparent, diff --git a/lib/shared/widgets/widgets.dart b/lib/shared/widgets/widgets.dart index be46da76..4237da5a 100644 --- a/lib/shared/widgets/widgets.dart +++ b/lib/shared/widgets/widgets.dart @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; import 'package:hub/flutter_flow/index.dart'; +import 'package:material_symbols_icons/symbols.dart'; import 'package:path_provider/path_provider.dart'; import 'package:pdfx/pdfx.dart'; import 'package:share_plus/share_plus.dart'; diff --git a/lib/shared/widgets/widgets.rxb.g.dart b/lib/shared/widgets/widgets.rxb.g.dart index e8298e8e..44258652 100644 --- a/lib/shared/widgets/widgets.rxb.g.dart +++ b/lib/shared/widgets/widgets.rxb.g.dart @@ -14,9 +14,9 @@ abstract class EnhancedListViewBlocType extends RxBlocTypeBase { EnhancedListViewStates get states; } -/// [$EnhancedListViewBloc] extended by the [EnhancedListViewBloc] +/// [$EnhancedListViewBloc] extended by the [EnhancedListViewBloc] /// @nodoc -abstract class $EnhancedListViewBloc extends RxBlocBase +abstract class $EnhancedListViewBloc extends RxBlocBase implements EnhancedListViewEvents, EnhancedListViewStates, @@ -24,7 +24,7 @@ abstract class $EnhancedListViewBloc extends RxBlocBase final _compositeSubscription = CompositeSubscription(); /// Тhe [Subject] where events sink to by calling [loadBodyItems] - final _$loadBodyItemsEvent = PublishSubject(); + final _$loadBodyItemsEvent = PublishSubject<({bool reset, dynamic query})>(); /// Тhe [Subject] where events sink to by calling [loadHeaderItems] final _$loadHeaderItemsEvent = PublishSubject(); @@ -48,7 +48,14 @@ abstract class $EnhancedListViewBloc extends RxBlocBase late final Stream _errorsState = _mapToErrorsState(); @override - void loadBodyItems({bool reset = false}) => _$loadBodyItemsEvent.add(reset); + void loadBodyItems({ + bool reset = false, + dynamic query = null, + }) => + _$loadBodyItemsEvent.add(( + reset: reset, + query: query, + )); @override void loadHeaderItems() => _$loadHeaderItemsEvent.add(null); @@ -96,3 +103,6 @@ abstract class $EnhancedListViewBloc extends RxBlocBase super.dispose(); } } + +// ignore: unused_element +typedef _LoadBodyItemsEventArgs = ({bool reset, dynamic query}); From 642daa3848c930e454cebad02f7c437abde7dbab Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Tue, 18 Feb 2025 16:29:07 -0300 Subject: [PATCH 16/32] WIP --- integration_test/auth_test.dart | 20 ++--- integration_test/menu_test.dart | 5 +- ..._arrow_linked_locals_component_widget.dart | 1 - .../card_item_template_component_widget.dart | 3 +- lib/features/documents/documents.dart | 90 ++++++++----------- .../locals_local_data_source.dart | 2 +- .../locals_remote_data_source.dart | 23 +++-- .../notification/deep_link_service.dart | 14 +-- .../services/database_storage_service.dart | 14 +-- lib/flutter_flow/custom_functions.dart | 2 +- lib/initialization.dart | 38 ++++---- lib/shared/mixins/pegeable_mixin.dart | 1 - lib/shared/widgets/enhanced_list_view.dart | 1 - lib/shared/widgets/list_view.dart | 1 - lib/shared/widgets/page.dart | 24 ++--- 15 files changed, 107 insertions(+), 132 deletions(-) diff --git a/integration_test/auth_test.dart b/integration_test/auth_test.dart index 80da3ace..b1e2ceff 100644 --- a/integration_test/auth_test.dart +++ b/integration_test/auth_test.dart @@ -48,11 +48,11 @@ class AuthenticationTest { await _navigateToSignIn($); for (var credential in credentials) { - print('Função: ${credential.functionName}'); - print('Entradas: ${credential.inputs}'); - print('Saída: ${credential.output}'); - print('Mensagem: ${credential.message}'); - print('---'); + log('Função: ${credential.functionName}'); + log('Entradas: ${credential.inputs}'); + log('Saída: ${credential.output}'); + log('Mensagem: ${credential.message}'); + log('---'); await _auth(credential.output, $, throwsException); } @@ -156,11 +156,11 @@ class AuthenticationTest { await $.pumpWidgetAndSettle(const App()); for (var credential in credentials) { - print('Função: ${credential.functionName}'); - print('Entradas: ${credential.inputs}'); - print('Saída: ${credential.output}'); - print('Mensagem: ${credential.message}'); - print('---'); + log('Função: ${credential.functionName}'); + log('Entradas: ${credential.inputs}'); + log('Saída: ${credential.output}'); + log('Mensagem: ${credential.message}'); + log('---'); await _navigateToSignUp($); await _auth(credential.output, $, throwsException); diff --git a/integration_test/menu_test.dart b/integration_test/menu_test.dart index 10b0ab62..a4a5d54b 100644 --- a/integration_test/menu_test.dart +++ b/integration_test/menu_test.dart @@ -68,8 +68,8 @@ class MenuTest { route = entry.key; title = entry.value; - print('route: $route'); - print('title: $title'); + log('route: $route'); + log('title: $title'); if (route == '/petsPage') continue; if (route == '/fastPassPage') continue; @@ -326,7 +326,6 @@ class MenuTest { final Key? widgetKey = entry.key; expect(widgetKey, isNotNull); - print('WIDGETKEY = $widgetKey'); if (widgetKey == ValueKey('FRE-HUB-FASTPASS')) continue; if (widgetKey == ValueKey('FRE-HUB-QRCODE')) continue; if (widgetKey == ValueKey('FRE-HUB-RESERVATIONS')) continue; diff --git a/lib/components/organism_components/bottom_arrow_linked_locals_component/bottom_arrow_linked_locals_component_widget.dart b/lib/components/organism_components/bottom_arrow_linked_locals_component/bottom_arrow_linked_locals_component_widget.dart index a5db861f..6daec993 100644 --- a/lib/components/organism_components/bottom_arrow_linked_locals_component/bottom_arrow_linked_locals_component_widget.dart +++ b/lib/components/organism_components/bottom_arrow_linked_locals_component/bottom_arrow_linked_locals_component_widget.dart @@ -206,7 +206,6 @@ class _BottomArrowLinkedLocalsComponentWidgetState } Widget _item(BuildContext context, dynamic local) { - log('local: ${local['CLI_NOME']}'); return CardItemTemplateComponentWidget( key: ValueKey(local['CLI_NOME']), imagePath: _imagePath(local), 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 9e968770..26d90fa3 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 @@ -1,4 +1,5 @@ import 'dart:collection'; +import 'dart:developer'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; @@ -101,7 +102,7 @@ class _CardItemTemplateComponentWidgetState } Widget _generateImage() { - print('img: ${widget.imagePath ?? ''}'); + log('img: ${widget.imagePath ?? ''}'); // CachedNetworkImage.evictFromCache(widget.imagePath ?? ''); return ClipRRect( borderRadius: BorderRadius.circular(20), diff --git a/lib/features/documents/documents.dart b/lib/features/documents/documents.dart index c105a0b5..68b244d1 100644 --- a/lib/features/documents/documents.dart +++ b/lib/features/documents/documents.dart @@ -43,14 +43,11 @@ class DocumentPageState extends PageState { @override Widget build(BuildContext context) { - log('Build -> DocumentPage'); - return RxBlocMultiBuilder2( state1: (bloc) => bloc.states.isDocumentSelected, state2: (bloc) => bloc.states.currentDocument, bloc: context.read(), builder: (context, isSelect, current, bloc) { - log('-> Build -> DocumentPage -> RxBlocMultiBuilder2'); if (isSelect.hasData && isSelect.data!) { return _buildDocumentViewScreen(current, bloc); } else { @@ -122,13 +119,12 @@ class DocumentPageModel extends FlutterFlowModel { /// [Body] void onView(Document document, BuildContext context) async { - log('Disparando evento selectDocument'); bloc.events.selectDocument(document); } Widget itemBodyBuilder( BuildContext context, T item, int index) { - print('ItemBuilder -> $index'); + log('ItemBuilder -> $index'); return DocumentItem( document: item, @@ -138,10 +134,8 @@ class DocumentPageModel extends FlutterFlowModel { Future> generateBodyItems( int pageKey, int pageSize, Q query) async { - log('generateDocuments: $query'); - final List error = [null]; - print('Query: ${query is Document}'); + log('Query: ${query is Document}'); final GetDocuments getDocuments = FreAccessWSGlobal.getDocuments; final ApiCallResponse newItems = await getDocuments.call(pageKey, query); @@ -153,7 +147,6 @@ class DocumentPageModel extends FlutterFlowModel { 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']; @@ -222,7 +215,6 @@ class DocumentPageModel extends FlutterFlowModel { }); Future> generateFooterItems() async { - log('generateCategories: '); final List error = [null]; final GetCategories getCategories = FreAccessWSGlobal.getCategories; @@ -244,7 +236,6 @@ class DocumentPageModel extends FlutterFlowModel { ); cats.add(cat); } - log('cats: $cats'); return cats as List; } @@ -385,7 +376,6 @@ class DocumentPageBloc extends $DocumentPageBloc { @override Stream<(Document, Uri)?> _mapToCurrentDocumentState() => _$selectDocumentEvent .switchMap((event) async* { - log('Evento selectDocument recebido: ${event.description}'); final uri = await GetPDF().call(event.id); yield (event, uri); }) @@ -432,7 +422,6 @@ class DocumentManagerScreen extends StatelessScreen { } Widget buildBody(BuildContext context) { - log('Build -> DocumentManagerScreen'); final SizedBox space = SizedBox(height: 30); return Column( children: [ @@ -478,47 +467,40 @@ class _DocumentViewScreenState extends ScreenState { final locale = FFLocalizations.of(context); backAction() => widget.bloc.events.unselectDocument(); - infoAction() async => await showDialog( - useSafeArea: true, - context: context, - builder: (context) { - return Material( - child: DetailsComponentWidget( - buttons: [], - statusHashMap: [], - labelsHashMap: Map.from({ - locale.getVariableText( - enText: 'Description', - ptText: 'Descrição', - ): widget.doc.$1.description, - locale.getVariableText( - enText: 'Type', - ptText: 'Tipo', - ): widget.doc.$1.type, - locale.getVariableText( - enText: 'Category', - ptText: 'Categoria', - ): widget.doc.$1.category.title, - locale.getVariableText( - enText: 'Person', - ptText: 'Pessoa', - ): widget.doc.$1.person, - locale.getVariableText( - enText: 'Property', - ptText: 'Propriedade', - ): widget.doc.$1.property, - locale.getVariableText( - enText: 'Created At', - ptText: 'Criado em', - ): widget.doc.$1.createdAt, - locale.getVariableText( - enText: 'Updated At', - ptText: 'Atualizado em', - ): widget.doc.$1.updatedAt, - }), - ), - ); - }); + infoAction() => DetailsComponentWidget( + buttons: [], + statusHashMap: [], + labelsHashMap: Map.from({ + locale.getVariableText( + enText: 'Description', + ptText: 'Descrição', + ): widget.doc.$1.description, + locale.getVariableText( + enText: 'Type', + ptText: 'Tipo', + ): widget.doc.$1.type, + locale.getVariableText( + enText: 'Category', + ptText: 'Categoria', + ): widget.doc.$1.category.title, + locale.getVariableText( + enText: 'Person', + ptText: 'Pessoa', + ): widget.doc.$1.person, + locale.getVariableText( + enText: 'Property', + ptText: 'Propriedade', + ): widget.doc.$1.property, + locale.getVariableText( + enText: 'Created At', + ptText: 'Criado em', + ): widget.doc.$1.createdAt, + locale.getVariableText( + enText: 'Updated At', + ptText: 'Atualizado em', + ): widget.doc.$1.updatedAt, + }), + ); return PopScope( canPop: false, diff --git a/lib/features/local/data/data_sources/locals_local_data_source.dart b/lib/features/local/data/data_sources/locals_local_data_source.dart index 21b439a7..80e97bdc 100644 --- a/lib/features/local/data/data_sources/locals_local_data_source.dart +++ b/lib/features/local/data/data_sources/locals_local_data_source.dart @@ -74,7 +74,7 @@ class LocalsLocalDataSourceImpl implements LocalsLocalDataSource { await DatabaseService.database .delete(LocalsConstants.tableLocalsKeychain); } catch (e, s) { - log('() => clearAll keychain: $e', stackTrace: s); + log('() => clearAll keychain: $e stackTrace: $s'); } } } diff --git a/lib/features/local/data/data_sources/locals_remote_data_source.dart b/lib/features/local/data/data_sources/locals_remote_data_source.dart index 939976ad..d105af49 100644 --- a/lib/features/local/data/data_sources/locals_remote_data_source.dart +++ b/lib/features/local/data/data_sources/locals_remote_data_source.dart @@ -70,7 +70,7 @@ class LocalsRemoteDataSourceImpl implements LocalsRemoteDataSource { @override Future processLocals(BuildContext context) async { - log('() => processLocals'); + print('-> processLocals'); try { final GetLocalsCall callback = FreAccessWSGlobal.getLocalsCall; final ApiCallResponse response = await callback.call(); @@ -114,31 +114,30 @@ class LocalsRemoteDataSourceImpl implements LocalsRemoteDataSource { } else if (isEnabled) { return await LocalUtil.handleEnabled(context, locals[0]); } else if (isUnselected) { - log('() => isUnselected'); + log('-> isUnselected'); return await selectLocal(context, response); } else if (isSelected) { - log('() => isSelected'); + log('-> isSelected'); return await processProperty(context).then((v) async { if (v == true) return await LicenseRepositoryImpl().updateLicense(); return v; }); } else if (isAvailable) { - log('() => isAvailable'); + log('-> isAvailable'); return await processProperty(context).then((v) async { if (v == true) return await LicenseRepositoryImpl().updateLicense(); return v; }); } else { - if (!isUnique && !isActive) log('() => not unique and not active'); - if (!isUnique && isInactived) log('() => not unique and inactived'); - if (!isUnique && isPending) log('() => not unique and pending'); - if (!isUnique && isBlocked) log('() => not unique and blocked'); - log('() => else'); + if (!isUnique && !isActive) log('-> not unique and not active'); + if (!isUnique && isInactived) log('-> not unique and inactived'); + if (!isUnique && isPending) log('-> not unique and pending'); + if (!isUnique && isBlocked) log('-> not unique and blocked'); + log('-> else'); return await selectLocal(context, response); } } catch (e, s) { - log('() => stack: $s'); - log('() => catch: $e', stackTrace: s); + log('-> catch: $e', stackTrace: s); // return await selectLocal(context); // final String errorMsg = FFLocalizations.of(context).getVariableText( // enText: 'Error getting locals, verify your connection', @@ -189,7 +188,7 @@ class LocalsRemoteDataSourceImpl implements LocalsRemoteDataSource { return true; } } catch (e, s) { - log('() => error processData: $e', stackTrace: s); + log('-> error processData: $e', stackTrace: s); // final String errorMsg = FFLocalizations.of(context).getVariableText( // enText: 'Error getting data, verify your connection', // ptText: 'Erro ao obter dados, verifique sua conexão', diff --git a/lib/features/notification/deep_link_service.dart b/lib/features/notification/deep_link_service.dart index 77792519..aac0351c 100644 --- a/lib/features/notification/deep_link_service.dart +++ b/lib/features/notification/deep_link_service.dart @@ -21,7 +21,7 @@ class DeepLinkService { if (_isInitialized) return; try { _appLinks = AppLinks(); - print('initDeepLinks'); + log('initDeepLinks'); _linkSubscription = _appLinks.uriLinkStream.listen((uri) async { if (!StorageHelper().isRecovered) { await _handleDeepLink(uri); @@ -29,25 +29,25 @@ class DeepLinkService { }); _isInitialized = true; } catch (e) { - print('Error initializing deep links: $e'); + log('Error initializing deep links: $e'); } } Future _handleDeepLink(Uri uri) async { try { - print('Handling deep link: $uri'); + log('Handling deep link: $uri'); if (StorageHelper().isRecovered) return; StorageHelper().isRecovered = true; final email = uri.queryParameters['email'] ?? ''; final token = uri.queryParameters['token'] ?? ''; - print('email: $email, token: $token'); + log('email: $email, token: $token'); if (email.isNotEmpty && token.isNotEmpty) { await _showForgotPasswordScreen(email, token); } } catch (e, s) { - print('Error handling deep link: $e, $s'); + log('Error handling deep link: $e, $s'); } } @@ -72,11 +72,11 @@ class DeepLinkService { enableDrag: true, ).whenComplete(() { StorageHelper().isRecovered = false; - print('showModalBottomSheet completed'); + log('showModalBottomSheet completed'); }); }); } catch (e, s) { - print('Error showing forgot password screen: $e, $s'); + log('Error showing forgot password screen: $e, $s'); } } diff --git a/lib/features/storage/services/database_storage_service.dart b/lib/features/storage/services/database_storage_service.dart index 813e09e9..d27bb2cc 100644 --- a/lib/features/storage/services/database_storage_service.dart +++ b/lib/features/storage/services/database_storage_service.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:hub/features/module/index.dart'; import 'package:hub/features/storage/index.dart'; import 'package:sqflite/sqflite.dart'; @@ -33,29 +35,29 @@ class DatabaseService { onOpen: _onOpen, onConfigure: _onConfigure, ); - print('Database initialized'); + log('Database initialized'); await LicenseRepositoryImpl().updateLicense(); isInitialized = true; } Future _onConfigure(Database database) async { - print('Configuring database...'); + log('Configuring database...'); } Future _onOpen(Database database) async { - print('Opening database...'); + log('Opening database...'); await _executePragmas(database); } Future _onCreate(Database database, int version) async { - print('Creating database...'); + log('Creating database...'); await database.execute(createKeychainTable); await _onUpgrade(database, 1, _dbVersion); } Future _onUpgrade( Database database, int oldVersion, int newVersion) async { - print('Upgrading database from version $oldVersion to $newVersion...'); + log('Upgrading database from version $oldVersion to $newVersion...'); if (oldVersion < 2 && newVersion >= 2) { await database.execute(LicenseConstants.createLicenseTable); await database.execute(LicenseConstants.updatePetsHistoryTrigger); @@ -70,7 +72,7 @@ class DatabaseService { Future _onDowngrade( Database database, int oldVersion, int newVersion) async { - print('Downgrading database from version $oldVersion to $newVersion...'); + log('Downgrading database from version $oldVersion to $newVersion...'); if (oldVersion >= 2 && newVersion < 2) { await database.execute(LicenseConstants.deleteLicenseTable); await database.execute(LicenseConstants.dropPeopleDisplayTrigger); diff --git a/lib/flutter_flow/custom_functions.dart b/lib/flutter_flow/custom_functions.dart index 231eef12..c919577e 100644 --- a/lib/flutter_flow/custom_functions.dart +++ b/lib/flutter_flow/custom_functions.dart @@ -50,7 +50,7 @@ Future> stringToMap(String v) async { }), )); } catch (e) { - print('Error parsing string to map: $e'); + log('Error parsing string to map: $e'); return Future.value({}); } } diff --git a/lib/initialization.dart b/lib/initialization.dart index b4aee728..13a47538 100644 --- a/lib/initialization.dart +++ b/lib/initialization.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:app_tracking_transparency/app_tracking_transparency.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_crashlytics/firebase_crashlytics.dart'; @@ -25,68 +27,68 @@ Future initializeApp() async { } Future _initializeTracking() async { - print('Requesting tracking authorization...'); + log('Requesting tracking authorization...'); await AppTrackingTransparency.requestTrackingAuthorization(); - print('Tracking authorization requested'); + log('Tracking authorization requested'); } Future _initializeFirebase() async { - print('Initializing Firebase...'); + log('Initializing Firebase...'); await Firebase.initializeApp(); - print('Firebase initialized'); + log('Firebase initialized'); } Future _initializeNotificationService() async { - print('Initializing Notification Service...'); + log('Initializing Notification Service...'); await NotificationService.initialize(); - print('Notification Service initialized'); + log('Notification Service initialized'); } void _initializeUrlStrategy() { - print('Initializing URL Strategy...'); + log('Initializing URL Strategy...'); setUrlStrategy(PathUrlStrategy()); - print('URL Strategy initialized'); + log('URL Strategy initialized'); } Future _initializeSystemSettings() async { - print('Initializing System Settings...'); + log('Initializing System Settings...'); final crashlyticsInstance = FirebaseCrashlytics.instance; await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); if (kDebugMode) { - print('Debug mode'); + log('Debug mode'); } else { - print('Release mode'); + log('Release mode'); // bool unsentReports = // await FirebaseCrashlytics.instance.checkForUnsentReports(); // if (unsentReports) { // // Existem relatórios não enviados // await crashlyticsInstance.sendUnsentReports(); - // print('Existem relatórios de falhas não enviados.'); + // log('Existem relatórios de falhas não enviados.'); // } else { // // Não existem relatórios não enviados - // print('Todos os relatórios de falhas foram enviados.'); + // log('Todos os relatórios de falhas foram enviados.'); // } await crashlyticsInstance.setCrashlyticsCollectionEnabled(true); // if (crashlyticsInstance.isCrashlyticsCollectionEnabled) { FlutterError.onError = crashlyticsInstance.recordFlutterError; - print('Crashlytics enabled'); + log('Crashlytics enabled'); // } } } Future _initializeFlutterFlow() async { - print('Initializing FlutterFlow...'); + log('Initializing FlutterFlow...'); await FlutterFlowTheme.initialize(); await FFLocalizations.initialize(); - print('FlutterFlow initialized'); + log('FlutterFlow initialized'); } Future _initializeNav() async { - print('Initializing Nav...'); + log('Initializing Nav...'); GoRouter.optionURLReflectsImperativeAPIs = true; usePathUrlStrategy(); - print('Nav initialized'); + log('Nav initialized'); } diff --git a/lib/shared/mixins/pegeable_mixin.dart b/lib/shared/mixins/pegeable_mixin.dart index 20a3cfdb..9be57f32 100644 --- a/lib/shared/mixins/pegeable_mixin.dart +++ b/lib/shared/mixins/pegeable_mixin.dart @@ -125,7 +125,6 @@ // } // Widget buildLoadingIndicator(BuildContext context) { -// print('Loading'); // return Container( // padding: const EdgeInsets.symmetric(vertical: 15), // child: Center( diff --git a/lib/shared/widgets/enhanced_list_view.dart b/lib/shared/widgets/enhanced_list_view.dart index a24af23c..5dde8550 100644 --- a/lib/shared/widgets/enhanced_list_view.dart +++ b/lib/shared/widgets/enhanced_list_view.dart @@ -434,7 +434,6 @@ class EnhancedListViewBloc } void filterBodyItems(Q query) { - print('filterBodyItems Q: ${query == null}'); _$loadBodyItemsEvent.add((query: query, reset: true)); } diff --git a/lib/shared/widgets/list_view.dart b/lib/shared/widgets/list_view.dart index 7304de9f..e1074f79 100644 --- a/lib/shared/widgets/list_view.dart +++ b/lib/shared/widgets/list_view.dart @@ -218,7 +218,6 @@ part of 'widgets.dart'; // )); // widget.pagingController.refresh(); // } else if (data is Document) { -// log('filter: ${data.description}'); // safeSetState(() => query = data); // widget.pagingController.refresh(); diff --git a/lib/shared/widgets/page.dart b/lib/shared/widgets/page.dart index cf9a9b76..2acbca74 100644 --- a/lib/shared/widgets/page.dart +++ b/lib/shared/widgets/page.dart @@ -35,21 +35,15 @@ mixin Template { if (action == null) return []; return [ IconButton( - onPressed: () async => await showDialog( - context: context, - builder: (context) => AlertDialog( - title: Text('Info'), - content: Text('This is a sample app.'), - actions: [ - TextButton( - onPressed: () => Navigator.of(context).pop(), - child: Text('Close')) - ], - )), - icon: Icon( - Symbols.info_i_rounded, - color: Colors.black, - )) + onPressed: () async => await showDialog( + context: context, + builder: (context) => action(), + ), + icon: Icon( + Symbols.info_i_rounded, + color: Colors.black, + ), + ) ]; } From ecd9408f3529409b34d25e48ed5062317233f23b Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Tue, 18 Feb 2025 16:45:37 -0300 Subject: [PATCH 17/32] WIP --- integration_test/app_test.dart | 1 + .../data_sources/locals_remote_data_source.dart | 2 +- lib/features/notification/deep_link_service.dart | 1 + lib/flutter_flow/custom_functions.dart | 3 ++- lib/pages/pets_page/pets_page_model.dart | 5 +++-- lib/shared/widgets/read_view.dart | 14 +++++++++++++- 6 files changed, 21 insertions(+), 5 deletions(-) diff --git a/integration_test/app_test.dart b/integration_test/app_test.dart index c2b4bbb9..404fdbae 100644 --- a/integration_test/app_test.dart +++ b/integration_test/app_test.dart @@ -1,4 +1,5 @@ import 'dart:collection'; +import 'dart:developer'; import 'package:app_tracking_transparency/app_tracking_transparency.dart'; import 'package:firebase_core/firebase_core.dart'; diff --git a/lib/features/local/data/data_sources/locals_remote_data_source.dart b/lib/features/local/data/data_sources/locals_remote_data_source.dart index d105af49..39d4d166 100644 --- a/lib/features/local/data/data_sources/locals_remote_data_source.dart +++ b/lib/features/local/data/data_sources/locals_remote_data_source.dart @@ -70,7 +70,7 @@ class LocalsRemoteDataSourceImpl implements LocalsRemoteDataSource { @override Future processLocals(BuildContext context) async { - print('-> processLocals'); + log('-> processLocals'); try { final GetLocalsCall callback = FreAccessWSGlobal.getLocalsCall; final ApiCallResponse response = await callback.call(); diff --git a/lib/features/notification/deep_link_service.dart b/lib/features/notification/deep_link_service.dart index aac0351c..003f1903 100644 --- a/lib/features/notification/deep_link_service.dart +++ b/lib/features/notification/deep_link_service.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:developer'; import 'package:app_links/app_links.dart'; import 'package:flutter/material.dart'; import 'package:hub/features/storage/index.dart'; diff --git a/lib/flutter_flow/custom_functions.dart b/lib/flutter_flow/custom_functions.dart index c919577e..ed8fcd2d 100644 --- a/lib/flutter_flow/custom_functions.dart +++ b/lib/flutter_flow/custom_functions.dart @@ -1,5 +1,6 @@ import 'dart:convert'; import 'dart:math' as math; +import 'dart:developer' as dev; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; @@ -50,7 +51,7 @@ Future> stringToMap(String v) async { }), )); } catch (e) { - log('Error parsing string to map: $e'); + dev.log('Error parsing string to map: $e'); return Future.value({}); } } diff --git a/lib/pages/pets_page/pets_page_model.dart b/lib/pages/pets_page/pets_page_model.dart index 753455fc..4d277121 100644 --- a/lib/pages/pets_page/pets_page_model.dart +++ b/lib/pages/pets_page/pets_page_model.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:developer'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; @@ -165,7 +166,7 @@ class PetsPageModel extends FlutterFlowModel { (() async { final String url = 'https://freaccess.com.br/freaccess/getImage.php?devUUID=$devUUID&userUUID=$userUUID&cliID=$cliUUID&atividade=consultaFotoPet&petId=$petId'; - print('img: $url'); + log('img: $url'); Response response = await get(Uri.parse(url)); String base64 = base64Encode(response.bodyBytes); uploadedTempFile = await ImageUtils.convertToUploadFile(base64); @@ -282,7 +283,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'); + log('img: $url'); final response = await FreAccessWSGlobal.updatePet.call( petID: petId, image: img, diff --git a/lib/shared/widgets/read_view.dart b/lib/shared/widgets/read_view.dart index 0688906c..540431eb 100644 --- a/lib/shared/widgets/read_view.dart +++ b/lib/shared/widgets/read_view.dart @@ -87,7 +87,7 @@ class ReadViewState extends State { name: '${widget.title}.pdf', mimeType: 'application/pdf'); await Share.shareXFiles([xfile], text: 'Confira este PDF!'); } else { - print('Erro ao baixar o arquivo: ${response.statusCode}'); + log('Erro ao baixar o arquivo: ${response.statusCode}'); } } @@ -110,8 +110,20 @@ class ReadViewState extends State { future: _initializePdf(), builder: (context, snapshot) { if (!snapshot.hasData) return buildLoadingIndicator(context); + return PdfView( controller: snapshot.data!, + renderer: (PdfPage page) => page.render( + width: page.width * 2, + height: page.height * 2, + cropRect: Rect.fromLTWH( + 10.0, 10.0, page.width * 2, page.height * 2), + forPrint: false, + quality: 100, + removeTempFile: true, + format: PdfPageImageFormat.jpeg, + backgroundColor: '#ffffff', + ), scrollDirection: Axis.vertical, ); }), From f0350c1bd9f781762077b22e741eb2b87a92809d Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Tue, 18 Feb 2025 18:02:39 -0300 Subject: [PATCH 18/32] WIP --- .../documents/archive_item_component.dart | 1 - .../documents/category_item_component.dart | 1 - .../documents/document_item_component.dart | 1 - .../documents/document_page_bloc.dart | 1 - .../documents/document_page_model.dart | 1 - .../documents/document_page_widget.dart | 1 - .../documents/document_screen_manager.dart | 1 - .../documents/document_screen_viewer.dart | 1 - lib/features/documents/documents.dart | 278 +++++++++--------- lib/shared/widgets/enhanced_list_view.dart | 233 +++++++++++---- lib/shared/widgets/widgets.dart | 1 + lib/shared/widgets/widgets.rxb.g.dart | 156 +++++----- pubspec.yaml | 3 +- 13 files changed, 386 insertions(+), 293 deletions(-) delete mode 100644 lib/features/documents/archive_item_component.dart delete mode 100644 lib/features/documents/category_item_component.dart delete mode 100644 lib/features/documents/document_item_component.dart delete mode 100644 lib/features/documents/document_page_bloc.dart delete mode 100644 lib/features/documents/document_page_model.dart delete mode 100644 lib/features/documents/document_page_widget.dart delete mode 100644 lib/features/documents/document_screen_manager.dart delete mode 100644 lib/features/documents/document_screen_viewer.dart diff --git a/lib/features/documents/archive_item_component.dart b/lib/features/documents/archive_item_component.dart deleted file mode 100644 index 9ec58ac2..00000000 --- a/lib/features/documents/archive_item_component.dart +++ /dev/null @@ -1 +0,0 @@ -part of 'index.dart'; diff --git a/lib/features/documents/category_item_component.dart b/lib/features/documents/category_item_component.dart deleted file mode 100644 index 9ec58ac2..00000000 --- a/lib/features/documents/category_item_component.dart +++ /dev/null @@ -1 +0,0 @@ -part of 'index.dart'; diff --git a/lib/features/documents/document_item_component.dart b/lib/features/documents/document_item_component.dart deleted file mode 100644 index 9ec58ac2..00000000 --- a/lib/features/documents/document_item_component.dart +++ /dev/null @@ -1 +0,0 @@ -part of 'index.dart'; diff --git a/lib/features/documents/document_page_bloc.dart b/lib/features/documents/document_page_bloc.dart deleted file mode 100644 index 9ec58ac2..00000000 --- a/lib/features/documents/document_page_bloc.dart +++ /dev/null @@ -1 +0,0 @@ -part of 'index.dart'; diff --git a/lib/features/documents/document_page_model.dart b/lib/features/documents/document_page_model.dart deleted file mode 100644 index 9ec58ac2..00000000 --- a/lib/features/documents/document_page_model.dart +++ /dev/null @@ -1 +0,0 @@ -part of 'index.dart'; diff --git a/lib/features/documents/document_page_widget.dart b/lib/features/documents/document_page_widget.dart deleted file mode 100644 index 9ec58ac2..00000000 --- a/lib/features/documents/document_page_widget.dart +++ /dev/null @@ -1 +0,0 @@ -part of 'index.dart'; diff --git a/lib/features/documents/document_screen_manager.dart b/lib/features/documents/document_screen_manager.dart deleted file mode 100644 index 9ec58ac2..00000000 --- a/lib/features/documents/document_screen_manager.dart +++ /dev/null @@ -1 +0,0 @@ -part of 'index.dart'; diff --git a/lib/features/documents/document_screen_viewer.dart b/lib/features/documents/document_screen_viewer.dart deleted file mode 100644 index 9ec58ac2..00000000 --- a/lib/features/documents/document_screen_viewer.dart +++ /dev/null @@ -1 +0,0 @@ -part of 'index.dart'; diff --git a/lib/features/documents/documents.dart b/lib/features/documents/documents.dart index 68b244d1..c1983505 100644 --- a/lib/features/documents/documents.dart +++ b/lib/features/documents/documents.dart @@ -18,14 +18,13 @@ import 'package:rxdart/rxdart.dart' as rx; part 'documents.rxb.g.dart'; /// ----------------------------------------------- -/// [TypeDefs] +/// [TypeDefs] ----------------------------------- /// ----------------------------------------------- typedef DocumentKey = GlobalKey; /// ----------------------------------------------- - -/// [Page] +/// [Pages] --------------------------------------- /// ----------------------------------------------- class DocumentPage extends StatefulPage { @@ -79,9 +78,139 @@ class DocumentPageState extends PageState { } } +/// ----------------------------------------------- +/// [Screens] ------------------------------------ /// ----------------------------------------------- -/// [Model] +class DocumentManagerScreen extends StatelessScreen { + final DocumentPageModel model; + final DocumentPageState state; + + const DocumentManagerScreen({ + super.key, + required this.model, + required this.state, + }); + + @override + Widget build(BuildContext context) { + final String title = FFLocalizations.of(context).getVariableText( + enText: 'Documents', + ptText: 'Documentos', + ); + final theme = FlutterFlowTheme.of(context); + action() => Navigator.pop(context); + + return Scaffold( + backgroundColor: theme.primaryBackground, + appBar: buildAppBar(title, context, action), + body: buildBody(context), + ); + } + + Widget buildBody(BuildContext context) { + final SizedBox space = SizedBox(height: 30); + return Column( + children: [ + Expanded( + child: EnhancedListView( + key: model.vehicleScreenManager, + headerBuilder: model.itemHeaderBuilder, + headerItems: model.generateHeaderItems, + bodyBuilder: model.itemBodyBuilder, + bodyItems: model.generateBodyItems, + footerBuilder: model.itemFooterBuilder, + footerItems: model.generateFooterItems, + ), + ), + ] // + .addToStart(space) + .addToEnd(space), + ); + } +} + +class DocumentViewScreen extends StatefulScreen { + const DocumentViewScreen({ + super.key, + required this.doc, + required this.bloc, + }); + + final (Document, Uri) doc; + final DocumentPageBlocType bloc; + + @override + ScreenState createState() => _DocumentViewScreenState(); +} + +class _DocumentViewScreenState extends ScreenState { + @override + Widget build(BuildContext context) { + final String title = widget.doc.$1.description; + final theme = FlutterFlowTheme.of(context); + final locale = FFLocalizations.of(context); + + backAction() => widget.bloc.events.unselectDocument(); + infoAction() => DetailsComponentWidget( + buttons: [], + statusHashMap: [], + labelsHashMap: Map.from({ + locale.getVariableText( + enText: 'Description', + ptText: 'Descrição', + ): widget.doc.$1.description, + locale.getVariableText( + enText: 'Type', + ptText: 'Tipo', + ): widget.doc.$1.type, + locale.getVariableText( + enText: 'Category', + ptText: 'Categoria', + ): widget.doc.$1.category.title, + locale.getVariableText( + enText: 'Person', + ptText: 'Pessoa', + ): widget.doc.$1.person, + locale.getVariableText( + enText: 'Property', + ptText: 'Propriedade', + ): widget.doc.$1.property, + locale.getVariableText( + enText: 'Created At', + ptText: 'Criado em', + ): widget.doc.$1.createdAt, + locale.getVariableText( + enText: 'Updated At', + ptText: 'Atualizado em', + ): widget.doc.$1.updatedAt, + }), + ); + + return PopScope( + canPop: false, + onPopInvokedWithResult: (didPop, result) => backAction(), + child: Scaffold( + backgroundColor: theme.primaryBackground, + appBar: buildAppBar(title, context, backAction, infoAction), + body: buildBody(context), + ), + ); + } + + Widget buildBody(BuildContext context) { + // final PDFViewerKey _viewerKey = PDFViewerKey(); + + return ReadView( + // search: _viewerKey, + title: widget.doc.$1.description, + url: widget.doc.$2.toString(), + ); + } +} + +/// ----------------------------------------------- +/// [Models] -------------------------------------- /// ----------------------------------------------- class DocumentPageModel extends FlutterFlowModel { @@ -346,7 +475,7 @@ class DocumentPageModel extends FlutterFlowModel { } /// ----------------------------------------------- -/// [BLoC] +/// [BLoCs] --------------------------------------- /// ----------------------------------------------- extension RxdartStartWithExtension on Stream { @@ -390,142 +519,7 @@ class DocumentPageBloc extends $DocumentPageBloc { } /// ----------------------------------------------- -/// [Screens] -/// ----------------------------------------------- - -/// [DocumentManagerScreen] - -class DocumentManagerScreen extends StatelessScreen { - final DocumentPageModel model; - final DocumentPageState state; - - const DocumentManagerScreen({ - super.key, - required this.model, - required this.state, - }); - - @override - Widget build(BuildContext context) { - final String title = FFLocalizations.of(context).getVariableText( - enText: 'Documents', - ptText: 'Documentos', - ); - final theme = FlutterFlowTheme.of(context); - action() => Navigator.pop(context); - - return Scaffold( - backgroundColor: theme.primaryBackground, - appBar: buildAppBar(title, context, action), - body: buildBody(context), - ); - } - - Widget buildBody(BuildContext context) { - final SizedBox space = SizedBox(height: 30); - return Column( - children: [ - Expanded( - child: EnhancedListView( - key: model.vehicleScreenManager, - headerBuilder: model.itemHeaderBuilder, - headerItems: model.generateHeaderItems, - bodyBuilder: model.itemBodyBuilder, - bodyItems: model.generateBodyItems, - footerBuilder: model.itemFooterBuilder, - footerItems: model.generateFooterItems, - ), - ), - ] // - .addToStart(space) - .addToEnd(space), - ); - } -} - -/// [DocumentViewScreen] - -class DocumentViewScreen extends StatefulScreen { - const DocumentViewScreen({ - super.key, - required this.doc, - required this.bloc, - }); - - final (Document, Uri) doc; - final DocumentPageBlocType bloc; - - @override - ScreenState createState() => _DocumentViewScreenState(); -} - -class _DocumentViewScreenState extends ScreenState { - @override - Widget build(BuildContext context) { - final String title = widget.doc.$1.description; - final theme = FlutterFlowTheme.of(context); - final locale = FFLocalizations.of(context); - - backAction() => widget.bloc.events.unselectDocument(); - infoAction() => DetailsComponentWidget( - buttons: [], - statusHashMap: [], - labelsHashMap: Map.from({ - locale.getVariableText( - enText: 'Description', - ptText: 'Descrição', - ): widget.doc.$1.description, - locale.getVariableText( - enText: 'Type', - ptText: 'Tipo', - ): widget.doc.$1.type, - locale.getVariableText( - enText: 'Category', - ptText: 'Categoria', - ): widget.doc.$1.category.title, - locale.getVariableText( - enText: 'Person', - ptText: 'Pessoa', - ): widget.doc.$1.person, - locale.getVariableText( - enText: 'Property', - ptText: 'Propriedade', - ): widget.doc.$1.property, - locale.getVariableText( - enText: 'Created At', - ptText: 'Criado em', - ): widget.doc.$1.createdAt, - locale.getVariableText( - enText: 'Updated At', - ptText: 'Atualizado em', - ): widget.doc.$1.updatedAt, - }), - ); - - return PopScope( - canPop: false, - onPopInvokedWithResult: (didPop, result) => backAction(), - child: Scaffold( - backgroundColor: theme.primaryBackground, - appBar: buildAppBar(title, context, backAction, infoAction), - body: buildBody(context), - ), - ); - } - - Widget buildBody(BuildContext context) { - // final PDFViewerKey _viewerKey = PDFViewerKey(); - - return ReadView( - // search: _viewerKey, - title: widget.doc.$1.description, - url: widget.doc.$2.toString(), - ); - } -} - -/// ----------------------------------------------- -/// [Interfaces] +/// [Interfaces] --------------------------------- /// ----------------------------------------------- abstract interface class Archive extends Entity {} @@ -590,7 +584,7 @@ interface class Category extends Archive { } /// ----------------------------------------------- -/// [Widgets] +/// [Components] ------------------------------------- /// ----------------------------------------------- // ignore: must_be_immutable diff --git a/lib/shared/widgets/enhanced_list_view.dart b/lib/shared/widgets/enhanced_list_view.dart index 5dde8550..66e62fbe 100644 --- a/lib/shared/widgets/enhanced_list_view.dart +++ b/lib/shared/widgets/enhanced_list_view.dart @@ -1,25 +1,23 @@ part of 'widgets.dart'; -/// [TypeDefs] +/// [TypeDefs] ---------------------------------------------------- -typedef EnhancedListViewKey - = GlobalKey>; +typedef EnhancedListViewKey + = GlobalKey< + EnhancedListViewState>; +typedef PaginatedListViewHeaderBuilder = Widget Function( + Future> Function() headerItems); +typedef PaginatedListViewBodyBuilder = Widget Function( + BuildContext context, BodyType item, int index); +typedef PaginatedListViewFooterBuilder = Widget Function( + Future> Function() footerItems); +typedef Query = QueryType?; +typedef BodyItemsBuilder = Future> + Function(int page, int pageSize, QueryType query); +typedef HeaderItemsBuilder = Future> Function(); +typedef FooterItemsBuilder = Future> Function(); -typedef PaginatedListViewHeaderBuilder = Widget Function( - Future> Function() headerItems); -typedef PaginatedListViewBodyBuilder = Widget Function( - BuildContext context, T item, int index); -typedef PaginatedListViewFooterBuilder = Widget Function( - Future> Function() footerItems); - -typedef Query = T?; - -typedef BodyItemsBuilder = Future> Function( - int page, int pageSize, Q query); -typedef HeaderItemsBuilder = Future> Function(); -typedef FooterItemsBuilder = Future> Function(); - -/// [Extensions] +/// [Extensions] ---------------------------------------------------- extension PaginatedListMergeExtensions on Stream>> { Stream> mergeWithPaginatedList( @@ -72,9 +70,7 @@ extension QueryBlocStreamExtensions on Stream { }); } -/// [Widgets] - -/// [EnhancedListView] +/// [Interfaces] ---------------------------------------------------- interface class EnhancedPaginatedList extends PaginatedList { @override @@ -145,13 +141,29 @@ interface class EnhancedPaginatedList extends PaginatedList { Future awaitLoad() async => Future.value(); } -abstract interface class EnhancedListViewBase - extends StatefulWidget { +abstract interface class EnhancedListViewBase extends StatefulWidget { const EnhancedListViewBase({super.key}); } -abstract interface class EnhancedListViewBaseState - extends State {} +abstract interface class EnhancedListViewBaseState + extends State< + EnhancedListViewBase> {} + +/// [Mixins] ---------------------------------------------------- + +mixin EnhancedListViewMixin { + late EnhancedListViewBloc bloc; + + void filterBodyItems(QueryType query) => bloc.filterBodyItems(query); + + void filterHeaderItems(QueryType query) => bloc.filterHeaderItems(query); + + void filterFooterItems(QueryType query) => bloc.filterFooterItems(query); +} + +/// [Widgets] ---------------------------------------------------- class EnhancedListView extends EnhancedListViewBase { @@ -162,7 +174,7 @@ class EnhancedListView final FooterItemsBuilder? footerItems; final PaginatedListViewFooterBuilder? footerBuilder; - EnhancedListView({ + const EnhancedListView({ Key? key, required this.bodyItems, required this.bodyBuilder, @@ -179,10 +191,8 @@ class EnhancedListView } class EnhancedListViewState - extends State< - EnhancedListView> { - late EnhancedListViewBloc bloc; - + extends State> + with EnhancedListViewMixin { @override void initState() { super.initState(); @@ -197,12 +207,6 @@ class EnhancedListViewState bloc.events.loadFooterItems(); } - void filterBodyItems(Query query) => bloc.filterBodyItems(query); - - void filterHeaderItems(Query query) => bloc.filterHeaderItems(query); - - void filterFooterItems(Query query) => bloc.filterFooterItems(query); - @override Widget build(BuildContext context) { final header = StreamBuilder>( @@ -272,7 +276,7 @@ class EnhancedListViewState } } -/// [Utils] +/// [Utils] ---------------------------------------------------- class EnhancedListTile extends StatelessWidget { const EnhancedListTile( @@ -336,7 +340,7 @@ class AuthorizationError implements Exception { AuthorizationError(this.message); } -/// [State Management] +/// [Blocs] ---------------------------------------------------- Stream get loadingState => Stream.empty(); Stream get errorState => Stream.empty(); @@ -345,23 +349,25 @@ abstract class EnhancedListViewRepository { int page, int pageSize, PaginatedListViewBodyBuilder builder); } -abstract class EnhancedListViewEvents { +abstract class EnhancedListViewEvents { void loadBodyItems({bool reset = false, dynamic query = null}); void loadHeaderItems(); void loadFooterItems(); } -abstract class EnhancedListViewStates { - Stream> get bodyItems; - Stream> get headerItems; - Stream> get footerItems; +abstract class EnhancedListViewStates { + Stream> get bodyItems; + Stream> get headerItems; + Stream> get footerItems; Stream get isLoading; Stream get errors; } -@RxBloc() -class EnhancedListViewBloc - extends $EnhancedListViewBloc { +class EnhancedListViewBloc + extends $EnhancedListViewBloc { EnhancedListViewBloc({ required this.bodyItemsBuilder, required this.headerItemsBuilder, @@ -384,24 +390,24 @@ class EnhancedListViewBloc .addTo(_compositeSubscription); } - final BodyItemsBuilder bodyItemsBuilder; - final HeaderItemsBuilder headerItemsBuilder; - final FooterItemsBuilder footerItemsBuilder; + final BodyItemsBuilder bodyItemsBuilder; + final HeaderItemsBuilder headerItemsBuilder; + final FooterItemsBuilder footerItemsBuilder; - final _bodyItems = BehaviorSubject>.seeded([]); + final _bodyItems = BehaviorSubject>.seeded([]); - final _headerItems = BehaviorSubject>.seeded([]); + final _headerItems = BehaviorSubject>.seeded([]); - final _footerItems = BehaviorSubject>.seeded([]); + final _footerItems = BehaviorSubject>.seeded([]); final _isLoading = BehaviorSubject.seeded(false); final _errors = BehaviorSubject(); - Stream> _fetchBodyItems(bool reset, Q? query) async* { + Stream> _fetchBodyItems(bool reset, QueryType? query) async* { try { _isLoading.add(true); final items = await bodyItemsBuilder(1, 10, query); - yield items.whereType().toList(); + yield items.whereType().toList(); } catch (e) { _errors.add(e.toString()); } finally { @@ -409,11 +415,11 @@ class EnhancedListViewBloc } } - Stream> _fetchHeaderItems() async* { + Stream> _fetchHeaderItems() async* { try { _isLoading.add(true); final items = await headerItemsBuilder(); - yield items.whereType().toList(); + yield items.whereType().toList(); } catch (e) { _errors.add(e.toString()); } finally { @@ -421,11 +427,11 @@ class EnhancedListViewBloc } } - Stream> _fetchFooterItems() async* { + Stream> _fetchFooterItems() async* { try { _isLoading.add(true); final items = await footerItemsBuilder(); - yield items.whereType().toList(); + yield items.whereType().toList(); } catch (e) { _errors.add(e.toString()); } finally { @@ -433,15 +439,15 @@ class EnhancedListViewBloc } } - void filterBodyItems(Q query) { + void filterBodyItems(QueryType query) { _$loadBodyItemsEvent.add((query: query, reset: true)); } - void filterHeaderItems(Q query) { + void filterHeaderItems(QueryType query) { _$loadHeaderItemsEvent.add(null); } - void filterFooterItems(Q query) { + void filterFooterItems(QueryType query) { _$loadFooterItemsEvent.add(null); } @@ -456,13 +462,112 @@ class EnhancedListViewBloc } @override - Stream> _mapToBodyItemsState() => _bodyItems.stream; + Stream> _mapToBodyItemsState() => _bodyItems.stream; @override Stream _mapToErrorsState() => _errors.stream; @override - Stream> _mapToFooterItemsState() => _footerItems.stream; + Stream> _mapToFooterItemsState() => _footerItems.stream; @override - Stream> _mapToHeaderItemsState() => _headerItems.stream; + Stream> _mapToHeaderItemsState() => _headerItems.stream; @override Stream _mapToIsLoadingState() => _isLoading.stream; } + +abstract class EnhancedListViewBlocType extends RxBlocTypeBase { + EnhancedListViewEvents get events; + EnhancedListViewStates get states; +} + +abstract class $EnhancedListViewBloc extends RxBlocBase + implements + EnhancedListViewEvents, + EnhancedListViewStates, + EnhancedListViewBlocType { + final _compositeSubscription = CompositeSubscription(); + + /// Тhe [Subject] where events sink to by calling [loadBodyItems] + final _$loadBodyItemsEvent = PublishSubject<({bool reset, dynamic query})>(); + + /// Тhe [Subject] where events sink to by calling [loadHeaderItems] + final _$loadHeaderItemsEvent = PublishSubject(); + + /// Тhe [Subject] where events sink to by calling [loadFooterItems] + final _$loadFooterItemsEvent = PublishSubject(); + + /// The state of [bodyItems] implemented in [_mapToBodyItemsState] + late final Stream> _bodyItemsState = _mapToBodyItemsState(); + + /// The state of [headerItems] implemented in [_mapToHeaderItemsState] + late final Stream> _headerItemsState = + _mapToHeaderItemsState(); + + /// The state of [footerItems] implemented in [_mapToFooterItemsState] + late final Stream> _footerItemsState = + _mapToFooterItemsState(); + + /// The state of [isLoading] implemented in [_mapToIsLoadingState] + late final Stream _isLoadingState = _mapToIsLoadingState(); + + /// The state of [errors] implemented in [_mapToErrorsState] + late final Stream _errorsState = _mapToErrorsState(); + + @override + void loadBodyItems({ + bool reset = false, + dynamic query = null, + }) => + _$loadBodyItemsEvent.add(( + reset: reset, + query: query, + )); + + @override + void loadHeaderItems() => _$loadHeaderItemsEvent.add(null); + + @override + void loadFooterItems() => _$loadFooterItemsEvent.add(null); + + @override + Stream> get bodyItems => _bodyItemsState; + + @override + Stream> get headerItems => _headerItemsState; + + @override + Stream> get footerItems => _footerItemsState; + + @override + Stream get isLoading => _isLoadingState; + + @override + Stream get errors => _errorsState; + + Stream> _mapToBodyItemsState(); + + Stream> _mapToHeaderItemsState(); + + Stream> _mapToFooterItemsState(); + + Stream _mapToIsLoadingState(); + + Stream _mapToErrorsState(); + + @override + EnhancedListViewEvents get events => this; + + @override + EnhancedListViewStates get states => this; + + @override + void dispose() { + _$loadBodyItemsEvent.close(); + _$loadHeaderItemsEvent.close(); + _$loadFooterItemsEvent.close(); + _compositeSubscription.dispose(); + super.dispose(); + } +} + +// ignore: unused_element +typedef _LoadBodyItemsEventArgs = ({bool reset, dynamic query}); diff --git a/lib/shared/widgets/widgets.dart b/lib/shared/widgets/widgets.dart index 4237da5a..05fb8a12 100644 --- a/lib/shared/widgets/widgets.dart +++ b/lib/shared/widgets/widgets.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_pdfview/flutter_pdfview.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; import 'package:hub/flutter_flow/index.dart'; diff --git a/lib/shared/widgets/widgets.rxb.g.dart b/lib/shared/widgets/widgets.rxb.g.dart index 44258652..4dd0c665 100644 --- a/lib/shared/widgets/widgets.rxb.g.dart +++ b/lib/shared/widgets/widgets.rxb.g.dart @@ -1,108 +1,108 @@ -// dart format width=80 -// GENERATED CODE - DO NOT MODIFY BY HAND +// // dart format width=80 +// // GENERATED CODE - DO NOT MODIFY BY HAND -// ************************************************************************** -// Generator: RxBlocGeneratorForAnnotation -// ************************************************************************** +// // ************************************************************************** +// // Generator: RxBlocGeneratorForAnnotation +// // ************************************************************************** part of 'widgets.dart'; -/// Used as a contractor for the bloc, events and states classes -/// @nodoc -abstract class EnhancedListViewBlocType extends RxBlocTypeBase { - EnhancedListViewEvents get events; - EnhancedListViewStates get states; -} +// /// Used as a contractor for the bloc, events and states classes +// /// @nodoc +// abstract class EnhancedListViewBlocType extends RxBlocTypeBase { +// EnhancedListViewEvents get events; +// EnhancedListViewStates get states; +// } -/// [$EnhancedListViewBloc] extended by the [EnhancedListViewBloc] -/// @nodoc -abstract class $EnhancedListViewBloc extends RxBlocBase - implements - EnhancedListViewEvents, - EnhancedListViewStates, - EnhancedListViewBlocType { - final _compositeSubscription = CompositeSubscription(); +// /// [$EnhancedListViewBloc] extended by the [EnhancedListViewBloc] +// /// @nodoc +// abstract class $EnhancedListViewBloc extends RxBlocBase +// implements +// EnhancedListViewEvents, +// EnhancedListViewStates, +// EnhancedListViewBlocType { +// final _compositeSubscription = CompositeSubscription(); - /// Тhe [Subject] where events sink to by calling [loadBodyItems] - final _$loadBodyItemsEvent = PublishSubject<({bool reset, dynamic query})>(); +// /// Тhe [Subject] where events sink to by calling [loadBodyItems] +// final _$loadBodyItemsEvent = PublishSubject<({bool reset, dynamic query})>(); - /// Тhe [Subject] where events sink to by calling [loadHeaderItems] - final _$loadHeaderItemsEvent = PublishSubject(); +// /// Тhe [Subject] where events sink to by calling [loadHeaderItems] +// final _$loadHeaderItemsEvent = PublishSubject(); - /// Тhe [Subject] where events sink to by calling [loadFooterItems] - final _$loadFooterItemsEvent = PublishSubject(); +// /// Тhe [Subject] where events sink to by calling [loadFooterItems] +// final _$loadFooterItemsEvent = PublishSubject(); - /// The state of [bodyItems] implemented in [_mapToBodyItemsState] - late final Stream> _bodyItemsState = _mapToBodyItemsState(); +// /// The state of [bodyItems] implemented in [_mapToBodyItemsState] +// late final Stream> _bodyItemsState = _mapToBodyItemsState(); - /// The state of [headerItems] implemented in [_mapToHeaderItemsState] - late final Stream> _headerItemsState = _mapToHeaderItemsState(); +// /// The state of [headerItems] implemented in [_mapToHeaderItemsState] +// late final Stream> _headerItemsState = _mapToHeaderItemsState(); - /// The state of [footerItems] implemented in [_mapToFooterItemsState] - late final Stream> _footerItemsState = _mapToFooterItemsState(); +// /// The state of [footerItems] implemented in [_mapToFooterItemsState] +// late final Stream> _footerItemsState = _mapToFooterItemsState(); - /// The state of [isLoading] implemented in [_mapToIsLoadingState] - late final Stream _isLoadingState = _mapToIsLoadingState(); +// /// The state of [isLoading] implemented in [_mapToIsLoadingState] +// late final Stream _isLoadingState = _mapToIsLoadingState(); - /// The state of [errors] implemented in [_mapToErrorsState] - late final Stream _errorsState = _mapToErrorsState(); +// /// The state of [errors] implemented in [_mapToErrorsState] +// late final Stream _errorsState = _mapToErrorsState(); - @override - void loadBodyItems({ - bool reset = false, - dynamic query = null, - }) => - _$loadBodyItemsEvent.add(( - reset: reset, - query: query, - )); +// @override +// void loadBodyItems({ +// bool reset = false, +// dynamic query = null, +// }) => +// _$loadBodyItemsEvent.add(( +// reset: reset, +// query: query, +// )); - @override - void loadHeaderItems() => _$loadHeaderItemsEvent.add(null); +// @override +// void loadHeaderItems() => _$loadHeaderItemsEvent.add(null); - @override - void loadFooterItems() => _$loadFooterItemsEvent.add(null); +// @override +// void loadFooterItems() => _$loadFooterItemsEvent.add(null); - @override - Stream> get bodyItems => _bodyItemsState; +// @override +// Stream> get bodyItems => _bodyItemsState; - @override - Stream> get headerItems => _headerItemsState; +// @override +// Stream> get headerItems => _headerItemsState; - @override - Stream> get footerItems => _footerItemsState; +// @override +// Stream> get footerItems => _footerItemsState; - @override - Stream get isLoading => _isLoadingState; +// @override +// Stream get isLoading => _isLoadingState; - @override - Stream get errors => _errorsState; +// @override +// Stream get errors => _errorsState; - Stream> _mapToBodyItemsState(); +// Stream> _mapToBodyItemsState(); - Stream> _mapToHeaderItemsState(); +// Stream> _mapToHeaderItemsState(); - Stream> _mapToFooterItemsState(); +// Stream> _mapToFooterItemsState(); - Stream _mapToIsLoadingState(); +// Stream _mapToIsLoadingState(); - Stream _mapToErrorsState(); +// Stream _mapToErrorsState(); - @override - EnhancedListViewEvents get events => this; +// @override +// EnhancedListViewEvents get events => this; - @override - EnhancedListViewStates get states => this; +// @override +// EnhancedListViewStates get states => this; - @override - void dispose() { - _$loadBodyItemsEvent.close(); - _$loadHeaderItemsEvent.close(); - _$loadFooterItemsEvent.close(); - _compositeSubscription.dispose(); - super.dispose(); - } -} +// @override +// void dispose() { +// _$loadBodyItemsEvent.close(); +// _$loadHeaderItemsEvent.close(); +// _$loadFooterItemsEvent.close(); +// _compositeSubscription.dispose(); +// super.dispose(); +// } +// } -// ignore: unused_element -typedef _LoadBodyItemsEventArgs = ({bool reset, dynamic query}); +// // ignore: unused_element +// typedef _LoadBodyItemsEventArgs = ({bool reset, dynamic query}); diff --git a/pubspec.yaml b/pubspec.yaml index 7283c1df..3badfc91 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -37,6 +37,8 @@ dependencies: fpdart: ^1.1.1 # Pacotes de UI + flutter_pdfview: ^1.4.0 + pdfx: ^2.8.0 auto_size_text: 3.0.0 barcode_widget: ^2.0.4 infinite_scroll_pagination: ^4.1.0 @@ -55,7 +57,6 @@ dependencies: percent_indicator: ^4.2.3 page_transition: ^2.2.1 share_plus: ^10.1.4 - pdfx: ^2.8.0 dropdown_button2: ^2.3.9 # Firebase From e1772649ec369e450ada34aee2edd42a7f6057f1 Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Wed, 19 Feb 2025 08:15:59 -0300 Subject: [PATCH 19/32] WIP --- lib/features/documents/documents.dart | 21 ++++---- lib/shared/mixins/template_mixin.dart | 70 +++++++++++++++++++++++++++ lib/shared/widgets/page.dart | 66 ------------------------- lib/shared/widgets/widgets.dart | 4 +- 4 files changed, 82 insertions(+), 79 deletions(-) create mode 100644 lib/shared/mixins/template_mixin.dart diff --git a/lib/features/documents/documents.dart b/lib/features/documents/documents.dart index c1983505..706e17df 100644 --- a/lib/features/documents/documents.dart +++ b/lib/features/documents/documents.dart @@ -68,7 +68,7 @@ class DocumentPageState extends PageState { Widget _buildDocumentViewScreen( AsyncSnapshot<(Document, Uri)?> snapshot, DocumentPageBlocType bloc) { if (snapshot.hasData) { - return DocumentViewScreen( + return DocumentViewerScreen( doc: snapshot.data!, bloc: bloc, ); @@ -83,7 +83,7 @@ class DocumentPageState extends PageState { /// ----------------------------------------------- class DocumentManagerScreen extends StatelessScreen { - final DocumentPageModel model; + final DocumentModel model; final DocumentPageState state; const DocumentManagerScreen({ @@ -130,8 +130,8 @@ class DocumentManagerScreen extends StatelessScreen { } } -class DocumentViewScreen extends StatefulScreen { - const DocumentViewScreen({ +class DocumentViewerScreen extends StatefulScreen { + const DocumentViewerScreen({ super.key, required this.doc, required this.bloc, @@ -141,10 +141,11 @@ class DocumentViewScreen extends StatefulScreen { final DocumentPageBlocType bloc; @override - ScreenState createState() => _DocumentViewScreenState(); + ScreenState createState() => + _DocumentViewerScreenState(); } -class _DocumentViewScreenState extends ScreenState { +class _DocumentViewerScreenState extends ScreenState { @override Widget build(BuildContext context) { final String title = widget.doc.$1.description; @@ -213,9 +214,9 @@ class _DocumentViewScreenState extends ScreenState { /// [Models] -------------------------------------- /// ----------------------------------------------- -class DocumentPageModel extends FlutterFlowModel { +class DocumentModel extends FlutterFlowModel { final DocumentPageBlocType bloc; - DocumentPageModel(this.bloc); + DocumentModel(this.bloc); late EnhancedListViewKey vehicleScreenManager; @@ -495,10 +496,10 @@ abstract class DocumentPageBlocStates { @RxBloc() class DocumentPageBloc extends $DocumentPageBloc { - late final DocumentPageModel model; + late final DocumentModel model; DocumentPageBloc(BuildContext context) { - model = DocumentPageModel(this); + model = DocumentModel(this); model.initState(context); } diff --git a/lib/shared/mixins/template_mixin.dart b/lib/shared/mixins/template_mixin.dart new file mode 100644 index 00000000..f3184e56 --- /dev/null +++ b/lib/shared/mixins/template_mixin.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:hub/flutter_flow/index.dart'; +import 'package:material_symbols_icons/material_symbols_icons.dart'; + +mixin Template { + PreferredSizeWidget buildAppBar( + String title, + BuildContext context, [ + dynamic Function()? backAction, + dynamic Function()? frontAction, + ]) { + final theme = FlutterFlowTheme.of(context); + return AppBar( + backgroundColor: FlutterFlowTheme.of(context).primaryBackground, + automaticallyImplyLeading: false, + title: Text( + title, + style: FlutterFlowTheme.of(context).headlineMedium.override( + fontFamily: FlutterFlowTheme.of(context).headlineMediumFamily, + color: FlutterFlowTheme.of(context).primaryText, + fontSize: 16.0, + fontWeight: FontWeight.bold, + letterSpacing: 0.0, + useGoogleFonts: GoogleFonts.asMap().containsKey( + FlutterFlowTheme.of(context).headlineMediumFamily), + ), + ), + leading: _backButton(context, theme, backAction), + centerTitle: true, + elevation: 0.0, + actions: _frontButton(context, theme, frontAction), + ); + } + + List _frontButton(BuildContext context, FlutterFlowTheme theme, + dynamic Function()? action) { + if (action == null) return []; + return [ + IconButton( + onPressed: () async => await showDialog( + context: context, + builder: (context) => action(), + ), + icon: Icon( + Symbols.info_i_rounded, + color: Colors.black, + ), + ) + ]; + } + + Widget? _backButton(BuildContext context, FlutterFlowTheme theme, + dynamic Function()? onPressed) { + if (onPressed == null) return null; + return FlutterFlowIconButton( + key: ValueKey('BackNavigationAppBar'), + borderColor: Colors.transparent, + borderRadius: 30.0, + borderWidth: 1.0, + buttonSize: 60.0, + icon: Icon( + Icons.keyboard_arrow_left, + color: theme.primaryText, + size: 30.0, + ), + onPressed: onPressed, + ); + } +} diff --git a/lib/shared/widgets/page.dart b/lib/shared/widgets/page.dart index 2acbca74..8fda7fad 100644 --- a/lib/shared/widgets/page.dart +++ b/lib/shared/widgets/page.dart @@ -1,71 +1,5 @@ part of 'widgets.dart'; -mixin Template { - PreferredSizeWidget buildAppBar( - String title, - BuildContext context, [ - dynamic Function()? backAction, - dynamic Function()? frontAction, - ]) { - final theme = FlutterFlowTheme.of(context); - return AppBar( - backgroundColor: FlutterFlowTheme.of(context).primaryBackground, - automaticallyImplyLeading: false, - title: Text( - title, - style: FlutterFlowTheme.of(context).headlineMedium.override( - fontFamily: FlutterFlowTheme.of(context).headlineMediumFamily, - color: FlutterFlowTheme.of(context).primaryText, - fontSize: 16.0, - fontWeight: FontWeight.bold, - letterSpacing: 0.0, - useGoogleFonts: GoogleFonts.asMap().containsKey( - FlutterFlowTheme.of(context).headlineMediumFamily), - ), - ), - leading: _backButton(context, theme, backAction), - centerTitle: true, - elevation: 0.0, - actions: _frontButton(context, theme, frontAction), - ); - } - - List _frontButton(BuildContext context, FlutterFlowTheme theme, - dynamic Function()? action) { - if (action == null) return []; - return [ - IconButton( - onPressed: () async => await showDialog( - context: context, - builder: (context) => action(), - ), - icon: Icon( - Symbols.info_i_rounded, - color: Colors.black, - ), - ) - ]; - } - - Widget? _backButton(BuildContext context, FlutterFlowTheme theme, - dynamic Function()? onPressed) { - if (onPressed == null) return null; - return FlutterFlowIconButton( - key: ValueKey('BackNavigationAppBar'), - borderColor: Colors.transparent, - borderRadius: 30.0, - borderWidth: 1.0, - buttonSize: 60.0, - icon: Icon( - Icons.keyboard_arrow_left, - color: theme.primaryText, - size: 30.0, - ), - onPressed: onPressed, - ); - } -} - /// [PageWidget] abstract class PageWidget extends Widget { diff --git a/lib/shared/widgets/widgets.dart b/lib/shared/widgets/widgets.dart index 05fb8a12..ae0c5ff7 100644 --- a/lib/shared/widgets/widgets.dart +++ b/lib/shared/widgets/widgets.dart @@ -3,11 +3,9 @@ import 'dart:io'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_pdfview/flutter_pdfview.dart'; -import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; import 'package:hub/flutter_flow/index.dart'; -import 'package:material_symbols_icons/symbols.dart'; +import 'package:hub/shared/mixins/template_mixin.dart'; import 'package:path_provider/path_provider.dart'; import 'package:pdfx/pdfx.dart'; import 'package:share_plus/share_plus.dart'; From 23a6b6d1d9682aef2e41325e575a097b68f027e3 Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Wed, 19 Feb 2025 09:53:26 -0300 Subject: [PATCH 20/32] WIP --- .../details_component_widget.dart | 415 +++++++++--------- lib/features/documents/documents.dart | 76 ++-- lib/shared/mixins/template_mixin.dart | 2 +- lib/shared/widgets/read_view.dart | 20 +- 4 files changed, 262 insertions(+), 251 deletions(-) diff --git a/lib/components/templates_components/details_component/details_component_widget.dart b/lib/components/templates_components/details_component/details_component_widget.dart index 231092f6..9754639d 100644 --- a/lib/components/templates_components/details_component/details_component_widget.dart +++ b/lib/components/templates_components/details_component/details_component_widget.dart @@ -64,218 +64,225 @@ class _DetailsComponentWidgetState extends State { // CachedNetworkImage.evictFromCache(widget.imagePath ?? ''); final double limitedBodyFontSize = LimitedFontSizeUtil.getBodyFontSize(context); - return Container( - constraints: BoxConstraints( - maxWidth: MediaQuery.of(context).size.width, - maxHeight: MediaQuery.of(context).size.height, - ), - decoration: BoxDecoration( - color: FlutterFlowTheme.of(context).primaryBackground, - borderRadius: const BorderRadius.all(Radius.circular(25.0)), - ), - child: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - SizedBox(height: MediaQuery.of(context).size.height * 0.02), - if (widget.imagePath != null && widget.imagePath != '') - Container( - width: MediaQuery.of(context).size.width * 0.3, - height: MediaQuery.of(context).size.width * 0.3, - clipBehavior: Clip.antiAlias, - decoration: const BoxDecoration( - shape: BoxShape.circle, + return Material( + type: MaterialType.transparency, + child: Container( + constraints: BoxConstraints( + maxWidth: MediaQuery.of(context).size.width, + maxHeight: MediaQuery.of(context).size.height, + ), + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context).primaryBackground, + borderRadius: const BorderRadius.all(Radius.circular(25.0)), + ), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SizedBox(height: MediaQuery.of(context).size.height * 0.02), + if (widget.imagePath != null && widget.imagePath != '') + Container( + width: MediaQuery.of(context).size.width * 0.3, + height: MediaQuery.of(context).size.width * 0.3, + clipBehavior: Clip.antiAlias, + decoration: const BoxDecoration( + shape: BoxShape.circle, + ), + child: CachedNetworkImage( + fadeInDuration: const Duration(milliseconds: 100), + fadeOutDuration: const Duration(milliseconds: 100), + imageUrl: widget.imagePath!, + fit: BoxFit.cover, + useOldImageOnUrlChange: true, + ), ), - child: CachedNetworkImage( - fadeInDuration: const Duration(milliseconds: 100), - fadeOutDuration: const Duration(milliseconds: 100), - imageUrl: widget.imagePath!, - fit: BoxFit.cover, - useOldImageOnUrlChange: true, - ), - ), - SizedBox(height: MediaQuery.of(context).size.height * 0.03), - Row( - children: statusLinkedHashMap.expand((linkedHashMap) { - return linkedHashMap.entries - .map((MapEntry item) { - return Expanded( - child: Padding( - padding: EdgeInsets.symmetric( - horizontal: MediaQuery.of(context).size.width * 0.05, - ), - child: TextFormField( - autofocus: false, - canRequestFocus: false, - readOnly: true, - obscureText: false, - decoration: InputDecoration( - isDense: true, - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10.0), - borderSide: BorderSide( - color: item.value, - ), - ), - filled: true, - fillColor: item.value, - labelText: item.key, - labelStyle: FlutterFlowTheme.of(context) - .labelMedium - .override( - fontFamily: FlutterFlowTheme.of(context) - .labelMediumFamily, - fontWeight: FontWeight.bold, - color: FlutterFlowTheme.of(context).info, - letterSpacing: 0.0, - useGoogleFonts: GoogleFonts.asMap().containsKey( - FlutterFlowTheme.of(context) - .labelMediumFamily, - ), - fontSize: limitedBodyFontSize, - ), - hintStyle: FlutterFlowTheme.of(context) - .labelMedium - .override( - fontFamily: FlutterFlowTheme.of(context) - .labelMediumFamily, - color: FlutterFlowTheme.of(context).info, - letterSpacing: 0.0, - useGoogleFonts: GoogleFonts.asMap().containsKey( - FlutterFlowTheme.of(context) - .labelMediumFamily, - ), - fontSize: limitedBodyFontSize, - ), - focusedBorder: InputBorder.none, - errorBorder: InputBorder.none, - focusedErrorBorder: InputBorder.none, - suffixIcon: Icon( - Icons.info, - color: FlutterFlowTheme.of(context).info, - ), + SizedBox(height: MediaQuery.of(context).size.height * 0.03), + Row( + children: statusLinkedHashMap.expand((linkedHashMap) { + return linkedHashMap.entries + .map((MapEntry item) { + return Expanded( + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.05, ), - style: FlutterFlowTheme.of(context).bodyMedium.override( - fontFamily: - FlutterFlowTheme.of(context).bodyMediumFamily, - color: FlutterFlowTheme.of(context).info, - letterSpacing: 0.0, - useGoogleFonts: GoogleFonts.asMap().containsKey( - FlutterFlowTheme.of(context).bodyMediumFamily, + child: TextFormField( + autofocus: false, + canRequestFocus: false, + readOnly: true, + obscureText: false, + decoration: InputDecoration( + isDense: true, + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10.0), + borderSide: BorderSide( + color: item.value, ), - fontSize: limitedBodyFontSize, ), - textAlign: TextAlign.center, - maxLines: null, - keyboardType: TextInputType.name, - validator: _model.textController1Validator - .asValidator(context), + filled: true, + fillColor: item.value, + labelText: item.key, + labelStyle: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: FlutterFlowTheme.of(context) + .labelMediumFamily, + fontWeight: FontWeight.bold, + color: FlutterFlowTheme.of(context).info, + letterSpacing: 0.0, + useGoogleFonts: + GoogleFonts.asMap().containsKey( + FlutterFlowTheme.of(context) + .labelMediumFamily, + ), + fontSize: limitedBodyFontSize, + ), + hintStyle: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: FlutterFlowTheme.of(context) + .labelMediumFamily, + color: FlutterFlowTheme.of(context).info, + letterSpacing: 0.0, + useGoogleFonts: + GoogleFonts.asMap().containsKey( + FlutterFlowTheme.of(context) + .labelMediumFamily, + ), + fontSize: limitedBodyFontSize, + ), + focusedBorder: InputBorder.none, + errorBorder: InputBorder.none, + focusedErrorBorder: InputBorder.none, + suffixIcon: Icon( + Icons.info, + color: FlutterFlowTheme.of(context).info, + ), + ), + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: FlutterFlowTheme.of(context) + .bodyMediumFamily, + color: FlutterFlowTheme.of(context).info, + letterSpacing: 0.0, + useGoogleFonts: GoogleFonts.asMap().containsKey( + FlutterFlowTheme.of(context).bodyMediumFamily, + ), + fontSize: limitedBodyFontSize, + ), + textAlign: TextAlign.center, + maxLines: null, + keyboardType: TextInputType.name, + validator: _model.textController1Validator + .asValidator(context), + ), + ), + ); + }).toList(); + }).toList(), + ), + SizedBox(height: MediaQuery.of(context).size.height * 0.03), + ListView.builder( + shrinkWrap: true, + itemCount: labelsLinkedHashMap.length, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + String key = labelsLinkedHashMap.keys.elementAt(index); + String value = labelsLinkedHashMap[key]!; + // return Text('key: $key, value: $value'); + return TextFormField( + readOnly: true, + initialValue: value, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: + FlutterFlowTheme.of(context).bodyMediumFamily, + color: FlutterFlowTheme.of(context).primaryText, + letterSpacing: 0.0, + useGoogleFonts: GoogleFonts.asMap().containsKey( + FlutterFlowTheme.of(context).bodyMediumFamily, + ), + fontSize: limitedBodyFontSize, + ), + decoration: InputDecoration( + labelText: key, + filled: true, + fillColor: FlutterFlowTheme.of(context).primaryBackground, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10.0), + borderSide: BorderSide( + color: FlutterFlowTheme.of(context) + .primaryBackground, // Change border color here + ), + ), + labelStyle: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: + FlutterFlowTheme.of(context).labelMediumFamily, + color: FlutterFlowTheme.of(context).primaryText, + letterSpacing: 0.0, + useGoogleFonts: GoogleFonts.asMap().containsKey( + FlutterFlowTheme.of(context).labelMediumFamily, + ), + ), + hintStyle: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: + FlutterFlowTheme.of(context).labelMediumFamily, + color: FlutterFlowTheme.of(context).primaryText, + letterSpacing: 0.0, + useGoogleFonts: GoogleFonts.asMap().containsKey( + FlutterFlowTheme.of(context).labelMediumFamily, + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10.0), + borderSide: BorderSide( + color: FlutterFlowTheme.of(context) + .primaryBackground, // Change border color here + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10.0), + borderSide: BorderSide( + color: FlutterFlowTheme.of(context) + .primaryBackground, // Change border color here + ), + ), + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10.0), + borderSide: BorderSide( + color: FlutterFlowTheme.of(context) + .primaryBackground, // Change border color here + ), + ), + focusedErrorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10.0), + borderSide: BorderSide( + color: FlutterFlowTheme.of(context) + .primaryBackground, // Change border color here + ), ), ), ); - }).toList(); - }).toList(), - ), - SizedBox(height: MediaQuery.of(context).size.height * 0.03), - ListView.builder( - shrinkWrap: true, - itemCount: labelsLinkedHashMap.length, - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (context, index) { - String key = labelsLinkedHashMap.keys.elementAt(index); - String value = labelsLinkedHashMap[key]!; - // return Text('key: $key, value: $value'); - return TextFormField( - readOnly: true, - initialValue: value, - style: FlutterFlowTheme.of(context).bodyMedium.override( - fontFamily: - FlutterFlowTheme.of(context).bodyMediumFamily, - color: FlutterFlowTheme.of(context).primaryText, - letterSpacing: 0.0, - useGoogleFonts: GoogleFonts.asMap().containsKey( - FlutterFlowTheme.of(context).bodyMediumFamily, - ), - fontSize: limitedBodyFontSize, - ), - decoration: InputDecoration( - labelText: key, - filled: true, - fillColor: FlutterFlowTheme.of(context).primaryBackground, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10.0), - borderSide: BorderSide( - color: FlutterFlowTheme.of(context) - .primaryBackground, // Change border color here - ), - ), - labelStyle: FlutterFlowTheme.of(context) - .labelMedium - .override( - fontFamily: - FlutterFlowTheme.of(context).labelMediumFamily, - color: FlutterFlowTheme.of(context).primaryText, - letterSpacing: 0.0, - useGoogleFonts: GoogleFonts.asMap().containsKey( - FlutterFlowTheme.of(context).labelMediumFamily, - ), - ), - hintStyle: FlutterFlowTheme.of(context) - .labelMedium - .override( - fontFamily: - FlutterFlowTheme.of(context).labelMediumFamily, - color: FlutterFlowTheme.of(context).primaryText, - letterSpacing: 0.0, - useGoogleFonts: GoogleFonts.asMap().containsKey( - FlutterFlowTheme.of(context).labelMediumFamily, - ), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10.0), - borderSide: BorderSide( - color: FlutterFlowTheme.of(context) - .primaryBackground, // Change border color here - ), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10.0), - borderSide: BorderSide( - color: FlutterFlowTheme.of(context) - .primaryBackground, // Change border color here - ), - ), - errorBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10.0), - borderSide: BorderSide( - color: FlutterFlowTheme.of(context) - .primaryBackground, // Change border color here - ), - ), - focusedErrorBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10.0), - borderSide: BorderSide( - color: FlutterFlowTheme.of(context) - .primaryBackground, // Change border color here - ), - ), - ), - ); - }, - ), - SizedBox(height: MediaQuery.of(context).size.height * 0.02), - if (widget.buttons!.isNotEmpty || widget.buttons != null) - OverflowBar( - overflowAlignment: OverflowBarAlignment.center, - alignment: MainAxisAlignment.center, - overflowSpacing: 2, - spacing: 2, - // mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: widget.buttons!, + }, ), - SizedBox(height: MediaQuery.of(context).size.height * 0.02), - ], + SizedBox(height: MediaQuery.of(context).size.height * 0.02), + if (widget.buttons!.isNotEmpty || widget.buttons != null) + OverflowBar( + overflowAlignment: OverflowBarAlignment.center, + alignment: MainAxisAlignment.center, + overflowSpacing: 2, + spacing: 2, + // mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: widget.buttons!, + ), + SizedBox(height: MediaQuery.of(context).size.height * 0.02), + ], + ), ), ), ); diff --git a/lib/features/documents/documents.dart b/lib/features/documents/documents.dart index 706e17df..59bf9b22 100644 --- a/lib/features/documents/documents.dart +++ b/lib/features/documents/documents.dart @@ -23,6 +23,15 @@ part 'documents.rxb.g.dart'; typedef DocumentKey = GlobalKey; +/// ----------------------------------------------- +/// [Extensions] --------------------------------- +/// ----------------------------------------------- + +extension ExplicitRxdartStartWithExtension on Stream { + Stream rxdartStartWith(T? value) => + rx.StartWithExtension(this).startWith(value); +} + /// ----------------------------------------------- /// [Pages] --------------------------------------- /// ----------------------------------------------- @@ -146,45 +155,56 @@ class DocumentViewerScreen extends StatefulScreen { } class _DocumentViewerScreenState extends ScreenState { + @override + void initState() { + super.initState(); + } + @override Widget build(BuildContext context) { final String title = widget.doc.$1.description; final theme = FlutterFlowTheme.of(context); final locale = FFLocalizations.of(context); + final Color color = widget.doc.$1.category.color; + ; backAction() => widget.bloc.events.unselectDocument(); infoAction() => DetailsComponentWidget( buttons: [], - statusHashMap: [], + statusHashMap: [ + Map.from({ + widget.doc.$1.description: widget.doc.$1.category.color, + }) + ], labelsHashMap: Map.from({ locale.getVariableText( enText: 'Description', ptText: 'Descrição', ): widget.doc.$1.description, - locale.getVariableText( - enText: 'Type', - ptText: 'Tipo', - ): widget.doc.$1.type, locale.getVariableText( enText: 'Category', ptText: 'Categoria', ): widget.doc.$1.category.title, - locale.getVariableText( - enText: 'Person', - ptText: 'Pessoa', - ): widget.doc.$1.person, - locale.getVariableText( - enText: 'Property', - ptText: 'Propriedade', - ): widget.doc.$1.property, + if (widget.doc.$1.person.isNotEmpty) + locale.getVariableText( + enText: 'Person', + ptText: 'Pessoa', + ): widget.doc.$1.person, + if (widget.doc.$1.property.isNotEmpty) + locale.getVariableText( + enText: 'Property', + ptText: 'Propriedade', + ): widget.doc.$1.property, locale.getVariableText( enText: 'Created At', ptText: 'Criado em', - ): widget.doc.$1.createdAt, + ): ValidatorUtil.toLocalDateTime( + 'yyyy-MM-dd', widget.doc.$1.createdAt), locale.getVariableText( enText: 'Updated At', ptText: 'Atualizado em', - ): widget.doc.$1.updatedAt, + ): ValidatorUtil.toLocalDateTime( + 'yyyy-MM-dd', widget.doc.$1.updatedAt), }), ); @@ -200,10 +220,7 @@ class _DocumentViewerScreenState extends ScreenState { } Widget buildBody(BuildContext context) { - // final PDFViewerKey _viewerKey = PDFViewerKey(); - return ReadView( - // search: _viewerKey, title: widget.doc.$1.description, url: widget.doc.$2.toString(), ); @@ -256,7 +273,7 @@ class DocumentModel extends FlutterFlowModel { BuildContext context, T item, int index) { log('ItemBuilder -> $index'); - return DocumentItem( + return DocumentComponent( document: item, onPressed: onView, ); @@ -311,8 +328,8 @@ class DocumentModel extends FlutterFlowModel { /// [Footer] - CategoryItem categoryItemBuilder(T? item) { - return CategoryItem(category: item! as Category); + CategoryComponent categoryItemBuilder(T? item) { + return CategoryComponent(category: item! as Category); } Widget itemFooterBuilder(Future> Function() gen) => @@ -479,11 +496,6 @@ class DocumentModel extends FlutterFlowModel { /// [BLoCs] --------------------------------------- /// ----------------------------------------------- -extension RxdartStartWithExtension on Stream { - Stream rxdartStartWith(T? value) => - rx.StartWithExtension(this).startWith(value); -} - abstract class DocumentPageBlocEvents { void selectDocument(Document document); void unselectDocument(); @@ -589,11 +601,11 @@ interface class Category extends Archive { /// ----------------------------------------------- // ignore: must_be_immutable -class DocumentItem extends StatelessComponent { +class DocumentComponent extends StatelessComponent { final Document document; void Function(Document, BuildContext) onPressed; - DocumentItem({ + DocumentComponent({ super.key, required this.document, required this.onPressed, @@ -721,20 +733,20 @@ class DocumentItem extends StatelessComponent { ); } - DocumentItem copyWith({ + DocumentComponent copyWith({ Document? document, }) { - return DocumentItem( + return DocumentComponent( document: document ?? this.document, onPressed: onPressed, ); } } -class CategoryItem extends StatelessComponent { +class CategoryComponent extends StatelessComponent { final Category category; - const CategoryItem({ + const CategoryComponent({ super.key, required this.category, }); diff --git a/lib/shared/mixins/template_mixin.dart b/lib/shared/mixins/template_mixin.dart index f3184e56..2e3611d0 100644 --- a/lib/shared/mixins/template_mixin.dart +++ b/lib/shared/mixins/template_mixin.dart @@ -38,7 +38,7 @@ mixin Template { if (action == null) return []; return [ IconButton( - onPressed: () async => await showDialog( + onPressed: () async => await showModalBottomSheet( context: context, builder: (context) => action(), ), diff --git a/lib/shared/widgets/read_view.dart b/lib/shared/widgets/read_view.dart index 540431eb..0b94b234 100644 --- a/lib/shared/widgets/read_view.dart +++ b/lib/shared/widgets/read_view.dart @@ -1,6 +1,8 @@ part of 'widgets.dart'; // typedef PDFViewerKey = GlobalKey; +typedef ReadViewController = PdfController; +typedef Document = PdfDocument; abstract interface class Viewer extends StatelessComponent { final String src; @@ -33,12 +35,10 @@ class ReadView extends StatefulWidget { } class ReadViewState extends State { - late PdfController _pdfController; - - Future _initializePdf() async { + Future _initializePdf() async { final file = await downloadPdf(widget.url); - final Future document = PdfDocument.openFile(file.path); - return PdfController(document: document); + final Future document = Document.openFile(file.path); + return ReadViewController(document: document); } Future downloadPdf(String url) async { @@ -106,11 +106,10 @@ class ReadViewState extends State { Widget _buildPDFViewer() => Padding( padding: EdgeInsets.all(10), - child: FutureBuilder( + child: FutureBuilder( future: _initializePdf(), builder: (context, snapshot) { if (!snapshot.hasData) return buildLoadingIndicator(context); - return PdfView( controller: snapshot.data!, renderer: (PdfPage page) => page.render( @@ -120,7 +119,6 @@ class ReadViewState extends State { 10.0, 10.0, page.width * 2, page.height * 2), forPrint: false, quality: 100, - removeTempFile: true, format: PdfPageImageFormat.jpeg, backgroundColor: '#ffffff', ), @@ -130,10 +128,4 @@ class ReadViewState extends State { ); // Widget get progressIndicator => LoadingUtil.buildLoadingIndicator(context); - - @override - void dispose() { - _pdfController.dispose(); - super.dispose(); - } } From 8b8950a992741e13d634df750c03fe0c78f35942 Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Wed, 19 Feb 2025 10:43:20 -0300 Subject: [PATCH 21/32] WIP --- assets/files/document.pdf | Bin 35523 -> 0 bytes lib/features/documents/documents.dart | 37 +++--- lib/shared/widgets/enhanced_list_view.dart | 128 ++++++++++++------- lib/shared/widgets/enhanced_search_view.dart | 76 +++++++++++ 4 files changed, 177 insertions(+), 64 deletions(-) delete mode 100644 assets/files/document.pdf create mode 100644 lib/shared/widgets/enhanced_search_view.dart diff --git a/assets/files/document.pdf b/assets/files/document.pdf deleted file mode 100644 index 9a84c2fc28b6e3e4d6d04277044d9f10eee8d5af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35523 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 diff --git a/lib/features/documents/documents.dart b/lib/features/documents/documents.dart index 59bf9b22..f3c7ce6c 100644 --- a/lib/features/documents/documents.dart +++ b/lib/features/documents/documents.dart @@ -118,18 +118,25 @@ class DocumentManagerScreen extends StatelessScreen { } Widget buildBody(BuildContext context) { - final SizedBox space = SizedBox(height: 30); + const SizedBox space = SizedBox(height: 10); + final controller = EnhancedListViewController( + headerBuilder: model.itemHeaderBuilder, + bodyBuilder: model.itemBodyBuilder, + footerBuilder: model.itemFooterBuilder, + ); + final repository = EnhancedListViewRepository( + fetchHeader: model.generateHeaderItems, + fetchBody: model.generateBodyItems, + fetchFooter: model.generateFooterItems, + ); + return Column( children: [ Expanded( - child: EnhancedListView( + child: EnhancedListView( key: model.vehicleScreenManager, - headerBuilder: model.itemHeaderBuilder, - headerItems: model.generateHeaderItems, - bodyBuilder: model.itemBodyBuilder, - bodyItems: model.generateBodyItems, - footerBuilder: model.itemFooterBuilder, - footerItems: model.generateFooterItems, + controller: controller, + repository: repository, ), ), ] // @@ -235,7 +242,7 @@ class DocumentModel extends FlutterFlowModel { final DocumentPageBlocType bloc; DocumentModel(this.bloc); - late EnhancedListViewKey + late EnhancedListViewKey vehicleScreenManager; late DocumentKey vehicleScreenViewer; late PagingController _pagingController; @@ -247,7 +254,7 @@ class DocumentModel extends FlutterFlowModel { @override void initState(BuildContext context) { vehicleScreenManager = - EnhancedListViewKey(); + EnhancedListViewKey(); vehicleScreenViewer = DocumentKey(); _pagingController = PagingController(firstPageKey: 1); @@ -388,7 +395,7 @@ class DocumentModel extends FlutterFlowModel { /// [Header] - Widget itemHeaderBuilder( + Widget itemHeaderBuilder( Future> Function() generateHeaderItems) { return Builder(builder: (context) { final theme = FlutterFlowTheme.of(context); @@ -462,8 +469,8 @@ class DocumentModel extends FlutterFlowModel { }); } - Future> generateHeaderItems() async { - final Search item = Search(); + Future> generateHeaderItems() async { + final SearchField item = SearchField(); return [item] as List; } @@ -537,8 +544,8 @@ class DocumentPageBloc extends $DocumentPageBloc { abstract interface class Archive extends Entity {} -interface class Search extends Archive { - Search(); +interface class SearchField extends Archive { + SearchField(); } interface class Document extends Archive { diff --git a/lib/shared/widgets/enhanced_list_view.dart b/lib/shared/widgets/enhanced_list_view.dart index 66e62fbe..79f01caf 100644 --- a/lib/shared/widgets/enhanced_list_view.dart +++ b/lib/shared/widgets/enhanced_list_view.dart @@ -5,17 +5,19 @@ part of 'widgets.dart'; typedef EnhancedListViewKey = GlobalKey< EnhancedListViewState>; -typedef PaginatedListViewHeaderBuilder = Widget Function( +typedef HeaderTileBuilder = Widget Function( Future> Function() headerItems); -typedef PaginatedListViewBodyBuilder = Widget Function( +typedef BodyTileBuilder = Widget Function( BuildContext context, BodyType item, int index); -typedef PaginatedListViewFooterBuilder = Widget Function( +typedef FooterTileBuilder = Widget Function( Future> Function() footerItems); typedef Query = QueryType?; -typedef BodyItemsBuilder = Future> +typedef BodyRetrievalUseCase = Future> Function(int page, int pageSize, QueryType query); -typedef HeaderItemsBuilder = Future> Function(); -typedef FooterItemsBuilder = Future> Function(); +typedef HeaderRetrievalUseCase = Future> + Function(); +typedef FooterRetrievalUseCase = Future> + Function(); /// [Extensions] ---------------------------------------------------- extension PaginatedListMergeExtensions @@ -54,20 +56,20 @@ extension StreamStartWithExtension on Stream { } extension QueryBlocStreamExtensions on Stream { - Stream>> fetchData( - EnhancedListViewRepository repository, - PaginatedListViewBodyBuilder builder, - BehaviorSubject> paginatedList, - ) => - switchMap((reset) { - if (reset) { - paginatedList.add(paginatedList.value.resetAll()); - } - final nextPage = paginatedList.value.currentPage + 1; - return repository - .fetchPage(nextPage, paginatedList.value.pageSize, builder) - .asResultStream(); - }); + // Stream>> fetchData( + // EnhancedListViewRepository repository, + // PaginatedListViewBodyBuilder builder, + // BehaviorSubject> paginatedList, + // ) => + // switchMap((reset) { + // if (reset) { + // paginatedList.add(paginatedList.value.resetAll()); + // } + // final nextPage = paginatedList.value.currentPage + 1; + // return repository + // .fetchPage(nextPage, paginatedList.value.pageSize, builder) + // .asResultStream(); + // }); } /// [Interfaces] ---------------------------------------------------- @@ -167,21 +169,15 @@ mixin EnhancedListViewMixin { class EnhancedListView extends EnhancedListViewBase { - final BodyItemsBuilder bodyItems; - final PaginatedListViewBodyBuilder bodyBuilder; - final HeaderItemsBuilder? headerItems; - final PaginatedListViewHeaderBuilder? headerBuilder; - final FooterItemsBuilder? footerItems; - final PaginatedListViewFooterBuilder? footerBuilder; + final EnhancedListViewRepository + repository; + final EnhancedListViewController + controller; const EnhancedListView({ Key? key, - required this.bodyItems, - required this.bodyBuilder, - this.headerItems, - this.headerBuilder, - this.footerItems, - this.footerBuilder, + required this.repository, + required this.controller, }) : super(key: key); @override @@ -198,9 +194,9 @@ class EnhancedListViewState super.initState(); bloc = EnhancedListViewBloc( - bodyItemsBuilder: widget.bodyItems, - headerItemsBuilder: widget.headerItems ?? () async => [], - footerItemsBuilder: widget.footerItems ?? () async => [], + bodyItemsBuilder: widget.repository.fetchBody, + headerItemsBuilder: widget.repository.fetchHeader ?? () async => [], + footerItemsBuilder: widget.repository.fetchFooter ?? () async => [], ); bloc.events.loadBodyItems(); bloc.events.loadHeaderItems(); @@ -219,7 +215,8 @@ class EnhancedListViewState } else if (!headerSnapshot.hasData || headerSnapshot.data!.isEmpty) { return const SizedBox.shrink(); } else { - return widget.headerBuilder!(() async => headerSnapshot.data!); + return widget + .controller.headerBuilder!(() async => headerSnapshot.data!); } }, ); @@ -233,7 +230,8 @@ class EnhancedListViewState } else if (!footerSnapshot.hasData || footerSnapshot.data!.isEmpty) { return const SizedBox.shrink(); } else { - return widget.footerBuilder!(() async => footerSnapshot.data!); + return widget + .controller.footerBuilder!(() async => footerSnapshot.data!); } }, ); @@ -251,8 +249,8 @@ class EnhancedListViewState return ListView.builder( itemCount: bodySnapshot.data?.length ?? 0, itemBuilder: (context, index) { - return widget.bodyBuilder( - context, bodySnapshot.data![index], index); + return widget.controller + .bodyBuilder(context, bodySnapshot.data![index], index); }, ); } @@ -262,9 +260,9 @@ class EnhancedListViewState return Column( children: [ - if (widget.headerBuilder != null) header, + if (widget.controller.headerBuilder != null) header, body, - if (widget.footerBuilder != null) footer, + if (widget.controller.footerBuilder != null) footer, ], ); } @@ -340,15 +338,47 @@ class AuthorizationError implements Exception { AuthorizationError(this.message); } +/// [Repositories] ---------------------------------------------------- + +class EnhancedListViewRepository { + final BodyRetrievalUseCase fetchBody; + final HeaderRetrievalUseCase? fetchHeader; + final FooterRetrievalUseCase? fetchFooter; + + const EnhancedListViewRepository({ + required this.fetchBody, + this.fetchHeader, + this.fetchFooter, + }); +} + +// class HeaderRepository {} +// class BodyRepository {} +// class FooterRepository {} + +/// [Controllers] ---------------------------------------------------- + +class EnhancedListViewController { + final BodyTileBuilder bodyBuilder; + final HeaderTileBuilder? headerBuilder; + final FooterTileBuilder? footerBuilder; + + const EnhancedListViewController({ + required this.bodyBuilder, + this.headerBuilder, + this.footerBuilder, + }); +} + +// class HeaderController {} +// class BodyController {} +// class FooterController {} + /// [Blocs] ---------------------------------------------------- + Stream get loadingState => Stream.empty(); Stream get errorState => Stream.empty(); -abstract class EnhancedListViewRepository { - Future> fetchPage( - int page, int pageSize, PaginatedListViewBodyBuilder builder); -} - abstract class EnhancedListViewEvents { void loadBodyItems({bool reset = false, dynamic query = null}); @@ -390,9 +420,9 @@ class EnhancedListViewBloc .addTo(_compositeSubscription); } - final BodyItemsBuilder bodyItemsBuilder; - final HeaderItemsBuilder headerItemsBuilder; - final FooterItemsBuilder footerItemsBuilder; + final BodyRetrievalUseCase bodyItemsBuilder; + final HeaderRetrievalUseCase headerItemsBuilder; + final FooterRetrievalUseCase footerItemsBuilder; final _bodyItems = BehaviorSubject>.seeded([]); diff --git a/lib/shared/widgets/enhanced_search_view.dart b/lib/shared/widgets/enhanced_search_view.dart new file mode 100644 index 00000000..8733530a --- /dev/null +++ b/lib/shared/widgets/enhanced_search_view.dart @@ -0,0 +1,76 @@ +import 'package:flutter/material.dart'; + +class EnhancedSearchView extends StatelessWidget { + const EnhancedSearchView({super.key}); + + @override + Widget build(BuildContext context) { + final theme = FlutterFlowTheme.of(context); + final locale = FFLocalizations.of(context); + TextEditingController editingController = TextEditingController(); + return TextFormField( + controller: editingController, + onChanged: (value) => EasyDebounce.debounce( + '_model.keyTextFieldTextController', + const Duration(milliseconds: 500), + () => filterBySearchBar(Document.fromDesc(value), context), + ), + cursorColor: theme.primaryText, + showCursor: false, + cursorWidth: 2.0, + cursorRadius: Radius.circular(100), + style: TextStyle( + color: theme.primaryText, + fontSize: 16.0, + decorationColor: Colors.amber, + ), + keyboardType: TextInputType.text, + textInputAction: TextInputAction.search, + autocorrect: true, + textCapitalization: TextCapitalization.sentences, + decoration: InputDecoration( + prefixIcon: Icon(Icons.search, color: theme.primary), + labelText: locale.getVariableText( + ptText: 'Pesquisar', + enText: 'Search', + ), + labelStyle: TextStyle( + color: theme.primaryText, + fontSize: 16.0, + ), + hintText: locale.getVariableText( + ptText: 'Digite sua pesquisa', + enText: 'Enter your search', + ), + hintStyle: TextStyle( + color: theme.accent2, + fontSize: 14.0, + ), + filled: true, + fillColor: Colors.transparent, + helperStyle: TextStyle( + color: theme.primaryText, + decorationColor: theme.primaryText, + ), + focusColor: theme.primaryText, + contentPadding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 15.0), + enabledBorder: UnderlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(15.0)), + borderSide: BorderSide(color: theme.primaryText), + ), + focusedBorder: UnderlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(15.0)), + borderSide: BorderSide(color: theme.primaryText), + ), + errorBorder: UnderlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(15.0)), + borderSide: BorderSide(color: theme.primaryText), + ), + focusedErrorBorder: UnderlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(15.0)), + borderSide: BorderSide(color: theme.primaryText, width: 2.0), + ), + ), + ); + } +} From e08dcbaea58814e64fb3419e2265b565600a39e2 Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Wed, 19 Feb 2025 11:07:54 -0300 Subject: [PATCH 22/32] WIP --- lib/shared/widgets/enhanced_search_view.dart | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/shared/widgets/enhanced_search_view.dart b/lib/shared/widgets/enhanced_search_view.dart index 8733530a..bcc3b405 100644 --- a/lib/shared/widgets/enhanced_search_view.dart +++ b/lib/shared/widgets/enhanced_search_view.dart @@ -1,7 +1,12 @@ import 'package:flutter/material.dart'; class EnhancedSearchView extends StatelessWidget { - const EnhancedSearchView({super.key}); + const EnhancedSearchView({ + super.key, + required this.filterBySearchBar, + }); + + final void Function(T query, BuildContext context) filterBySearchBar; @override Widget build(BuildContext context) { @@ -13,7 +18,7 @@ class EnhancedSearchView extends StatelessWidget { onChanged: (value) => EasyDebounce.debounce( '_model.keyTextFieldTextController', const Duration(milliseconds: 500), - () => filterBySearchBar(Document.fromDesc(value), context), + () => filterBySearchBar.call(Document.fromDesc(value), context), ), cursorColor: theme.primaryText, showCursor: false, From 5a481f8ab7b3c5bbb1b6e442510141c6c5892191 Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Wed, 19 Feb 2025 11:10:31 -0300 Subject: [PATCH 23/32] WIP --- lib/shared/widgets/enhanced_search_view.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/shared/widgets/enhanced_search_view.dart b/lib/shared/widgets/enhanced_search_view.dart index bcc3b405..0387e5d1 100644 --- a/lib/shared/widgets/enhanced_search_view.dart +++ b/lib/shared/widgets/enhanced_search_view.dart @@ -3,22 +3,22 @@ import 'package:flutter/material.dart'; class EnhancedSearchView extends StatelessWidget { const EnhancedSearchView({ super.key, - required this.filterBySearchBar, + required this.filter, }); - final void Function(T query, BuildContext context) filterBySearchBar; + final void Function(T query, BuildContext context) filter; @override Widget build(BuildContext context) { final theme = FlutterFlowTheme.of(context); final locale = FFLocalizations.of(context); - TextEditingController editingController = TextEditingController(); + TextEditingController controller = TextEditingController(); return TextFormField( - controller: editingController, + controller: controller, onChanged: (value) => EasyDebounce.debounce( '_model.keyTextFieldTextController', const Duration(milliseconds: 500), - () => filterBySearchBar.call(Document.fromDesc(value), context), + () => filter.call(Document.fromDesc(value), context), ), cursorColor: theme.primaryText, showCursor: false, From ae24dd2d7cb3e4947f912accf5cdb3823ee9e8b9 Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Wed, 19 Feb 2025 11:46:24 -0300 Subject: [PATCH 24/32] WIP --- lib/features/documents/documents.dart | 4 +- lib/shared/widgets/enhanced_search_view.dart | 158 ++++++++++--------- lib/shared/widgets/read_view.dart | 4 +- lib/shared/widgets/widgets.dart | 3 + 4 files changed, 87 insertions(+), 82 deletions(-) diff --git a/lib/features/documents/documents.dart b/lib/features/documents/documents.dart index f3c7ce6c..fdf2bf6d 100644 --- a/lib/features/documents/documents.dart +++ b/lib/features/documents/documents.dart @@ -406,7 +406,7 @@ class DocumentModel extends FlutterFlowModel { onChanged: (value) => EasyDebounce.debounce( '_model.keyTextFieldTextController', const Duration(milliseconds: 500), - () => filterBySearchBar(Document.fromDesc(value), context), + () => filterBySearchBar(Document.from(value), context), ), cursorColor: theme.primaryText, showCursor: false, @@ -569,7 +569,7 @@ interface class Document extends Archive { required this.updatedAt, }); - factory Document.fromDesc(String desc) => Document( + factory Document.from(String desc) => Document( id: 0, description: desc, type: '', diff --git a/lib/shared/widgets/enhanced_search_view.dart b/lib/shared/widgets/enhanced_search_view.dart index 0387e5d1..fb18038a 100644 --- a/lib/shared/widgets/enhanced_search_view.dart +++ b/lib/shared/widgets/enhanced_search_view.dart @@ -1,81 +1,83 @@ -import 'package:flutter/material.dart'; +part of 'widgets.dart'; -class EnhancedSearchView extends StatelessWidget { - const EnhancedSearchView({ - super.key, - required this.filter, - }); +// class EnhancedSearchView extends StatelessWidget { +// const EnhancedSearchView({ +// super.key, +// required this.filter, +// required this.cast, +// }); - final void Function(T query, BuildContext context) filter; +// final void Function(QueryType query) filter; +// final QueryType Function(String query) cast; - @override - Widget build(BuildContext context) { - final theme = FlutterFlowTheme.of(context); - final locale = FFLocalizations.of(context); - TextEditingController controller = TextEditingController(); - return TextFormField( - controller: controller, - onChanged: (value) => EasyDebounce.debounce( - '_model.keyTextFieldTextController', - const Duration(milliseconds: 500), - () => filter.call(Document.fromDesc(value), context), - ), - cursorColor: theme.primaryText, - showCursor: false, - cursorWidth: 2.0, - cursorRadius: Radius.circular(100), - style: TextStyle( - color: theme.primaryText, - fontSize: 16.0, - decorationColor: Colors.amber, - ), - keyboardType: TextInputType.text, - textInputAction: TextInputAction.search, - autocorrect: true, - textCapitalization: TextCapitalization.sentences, - decoration: InputDecoration( - prefixIcon: Icon(Icons.search, color: theme.primary), - labelText: locale.getVariableText( - ptText: 'Pesquisar', - enText: 'Search', - ), - labelStyle: TextStyle( - color: theme.primaryText, - fontSize: 16.0, - ), - hintText: locale.getVariableText( - ptText: 'Digite sua pesquisa', - enText: 'Enter your search', - ), - hintStyle: TextStyle( - color: theme.accent2, - fontSize: 14.0, - ), - filled: true, - fillColor: Colors.transparent, - helperStyle: TextStyle( - color: theme.primaryText, - decorationColor: theme.primaryText, - ), - focusColor: theme.primaryText, - contentPadding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 15.0), - enabledBorder: UnderlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(15.0)), - borderSide: BorderSide(color: theme.primaryText), - ), - focusedBorder: UnderlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(15.0)), - borderSide: BorderSide(color: theme.primaryText), - ), - errorBorder: UnderlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(15.0)), - borderSide: BorderSide(color: theme.primaryText), - ), - focusedErrorBorder: UnderlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(15.0)), - borderSide: BorderSide(color: theme.primaryText, width: 2.0), - ), - ), - ); - } -} +// @override +// Widget build(BuildContext context) { +// final theme = FlutterFlowTheme.of(context); +// final locale = FFLocalizations.of(context); +// TextEditingController controller = TextEditingController(); +// return TextFormField( +// controller: controller, +// onChanged: (value) => EasyDebounce.debounce( +// '_model.keyTextFieldTextController', +// const Duration(milliseconds: 500), +// () => filter.call(cast.call(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( +// 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), +// ), +// ), +// ); +// } +// } diff --git a/lib/shared/widgets/read_view.dart b/lib/shared/widgets/read_view.dart index 0b94b234..2e5208e0 100644 --- a/lib/shared/widgets/read_view.dart +++ b/lib/shared/widgets/read_view.dart @@ -2,7 +2,7 @@ part of 'widgets.dart'; // typedef PDFViewerKey = GlobalKey; typedef ReadViewController = PdfController; -typedef Document = PdfDocument; +typedef DocumentType = PdfDocument; abstract interface class Viewer extends StatelessComponent { final String src; @@ -37,7 +37,7 @@ class ReadView extends StatefulWidget { class ReadViewState extends State { Future _initializePdf() async { final file = await downloadPdf(widget.url); - final Future document = Document.openFile(file.path); + final Future document = DocumentType.openFile(file.path); return ReadViewController(document: document); } diff --git a/lib/shared/widgets/widgets.dart b/lib/shared/widgets/widgets.dart index ae0c5ff7..67f29a38 100644 --- a/lib/shared/widgets/widgets.dart +++ b/lib/shared/widgets/widgets.dart @@ -1,7 +1,9 @@ import 'dart:developer'; import 'dart:io'; +import 'dart:nativewrappers/_internal/vm/lib/ffi_allocation_patch.dart'; import 'package:auto_size_text/auto_size_text.dart'; +import 'package:easy_debounce/easy_debounce.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:hub/flutter_flow/index.dart'; @@ -27,6 +29,7 @@ part 'list_view.dart'; part 'carousel_view.dart'; part 'read_view.dart'; part 'enhanced_list_view.dart'; +part 'enhanced_search_view.dart'; /// [Component]'s part 'text.dart'; From 10bf8f523c94c4ca79940738545cdb91db1d8e9f Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Wed, 19 Feb 2025 17:51:12 -0300 Subject: [PATCH 25/32] WIP --- lib/features/documents/documents.dart | 74 ++++++++----------- lib/shared/widgets/carousel_view.dart | 32 -------- .../widgets/enhanced_carousel_view.dart | 69 +++++++++++++++++ lib/shared/widgets/enhanced_list_view.dart | 1 + lib/shared/widgets/widgets.dart | 4 +- 5 files changed, 103 insertions(+), 77 deletions(-) delete mode 100644 lib/shared/widgets/carousel_view.dart create mode 100644 lib/shared/widgets/enhanced_carousel_view.dart diff --git a/lib/features/documents/documents.dart b/lib/features/documents/documents.dart index fdf2bf6d..ba712d91 100644 --- a/lib/features/documents/documents.dart +++ b/lib/features/documents/documents.dart @@ -286,7 +286,7 @@ class DocumentModel extends FlutterFlowModel { ); } - Future> generateBodyItems( + Future> generateBodyItems( int pageKey, int pageSize, Q query) async { final List error = [null]; log('Query: ${query is Document}'); @@ -335,40 +335,21 @@ class DocumentModel extends FlutterFlowModel { /// [Footer] - CategoryComponent categoryItemBuilder(T? item) { - return CategoryComponent(category: item! as Category); - } - - Widget itemFooterBuilder(Future> Function() gen) => + Widget itemFooterBuilder( + Future> Function() fetchData) => Builder(builder: (context) { - return Column( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(15, 0, 50, 0), - child: Text( - FFLocalizations.of(context).getVariableText( - ptText: 'Suas Categorias', - enText: 'Your Categories', - ), - style: TextStyle( - color: FlutterFlowTheme.of(context).primaryText, - fontSize: LimitedFontSizeUtil.getHeaderFontSize(context), - ), - ), - ), - EnhancedCarouselView( - generateItems: gen, - itemBuilder: categoryItemBuilder, - filter: filterByCategory, - ), - ], + CategoryComponent categoryItemBuilder(T? item) { + return CategoryComponent(category: item! as Category); + } + + return EnhancedCarouselView( + dataProvider: fetchData, + itemBuilder: categoryItemBuilder, + filter: filterByCategory, ); }); - Future> generateFooterItems() async { + Future> generateFooterItems() async { final List error = [null]; final GetCategories getCategories = FreAccessWSGlobal.getCategories; @@ -674,6 +655,13 @@ class DocumentComponent extends StatelessComponent { final double boxHeight = constraints.maxHeight > 350 ? MediaQuery.of(context).size.height * 0.07 : MediaQuery.of(context).size.height * 2; + final color = document.category.color; + // final color = FlutterFlowTheme.of(context).primary; + final icon = Icons.description; + const space = SizedBox(width: 10); + final description = document.description; + final title = document.category.title; + const double size = 20; return InkWell( onTap: () => onPressed(document, context), @@ -684,26 +672,29 @@ class DocumentComponent extends StatelessComponent { height: boxHeight, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: size, children: [ // const SizedBox(width: 10), - Icon(Icons.description, color: document.category.color), - const SizedBox(width: 10), + Icon(icon, color: color), + Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ Tooltip( - message: document.description, + message: description, child: AutoText( - document.description, + description, style: textStyleMajor, overflow: TextOverflow.ellipsis, ), ), AutoText( ValidatorUtil.toLocalDateTime( - 'yyyy-MM-dd', document.updatedAt), + 'yyyy-MM-dd', + document.updatedAt, + ), style: textStyleMinor, overflow: TextOverflow.ellipsis, ), @@ -715,12 +706,7 @@ class DocumentComponent extends StatelessComponent { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.end, children: [ - _buildTooltip( - document.category.title, - document.category.color, - context, - constraints, - ), + _buildTooltip(title, color, context, constraints), ], ), ), @@ -731,7 +717,9 @@ class DocumentComponent extends StatelessComponent { color: primaryText, ), ), - ], + ] // + .addToStart(space) + .addToEnd(space), ), ), ); diff --git a/lib/shared/widgets/carousel_view.dart b/lib/shared/widgets/carousel_view.dart deleted file mode 100644 index cce63a45..00000000 --- a/lib/shared/widgets/carousel_view.dart +++ /dev/null @@ -1,32 +0,0 @@ -part of 'widgets.dart'; - -class EnhancedCarouselView extends StatelessWidget { - final Future> Function() generateItems; - final void Function(T, BuildContext) filter; - final Widget Function(T? item) itemBuilder; - - const EnhancedCarouselView({ - super.key, - required this.generateItems, - required this.filter, - required this.itemBuilder, - }); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 120, - child: FutureBuilder>( - future: generateItems(), - builder: (context, snapshot) { - if (!snapshot.hasData) return SizedBox(); - return CarouselView( - itemExtent: 100, - onTap: (index) => filter(snapshot.data![index] as T, context), - children: - snapshot.data!.map((item) => itemBuilder(item)).toList(), - ); - }), - ); - } -} diff --git a/lib/shared/widgets/enhanced_carousel_view.dart b/lib/shared/widgets/enhanced_carousel_view.dart new file mode 100644 index 00000000..7338dfb0 --- /dev/null +++ b/lib/shared/widgets/enhanced_carousel_view.dart @@ -0,0 +1,69 @@ +part of 'widgets.dart'; + +class EnhancedCarouselView extends StatelessWidget { + final Future> Function() dataProvider; + final void Function(T, BuildContext) filter; + final Widget Function(T? item) itemBuilder; + + const EnhancedCarouselView({ + super.key, + required this.dataProvider, + required this.filter, + required this.itemBuilder, + }); + + @override + Widget build(BuildContext context) { + final theme = FlutterFlowTheme.of(context); + final backgroundColor = theme.primary; + final overlayColor = WidgetStateProperty.all(Colors.transparent); + + return Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.end, + spacing: 20, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(15, 0, 50, 0), + child: Text( + FFLocalizations.of(context).getVariableText( + ptText: 'Suas Categorias', + enText: 'Your Categories', + ), + style: TextStyle( + color: FlutterFlowTheme.of(context).primaryText, + fontSize: LimitedFontSizeUtil.getHeaderFontSize(context), + ), + ), + ), + FutureBuilder>( + future: dataProvider(), + builder: (context, snapshot) { + if (!snapshot.hasData) return SizedBox(); + final items = + snapshot.data!.map((item) => itemBuilder(item)).toList(); + return SizedBox( + height: 130, // Set a specific height + child: CarouselView( + itemExtent: 140, + enableSplash: true, + itemSnapping: true, + controller: CarouselController(initialItem: 1), + backgroundColor: backgroundColor, + overlayColor: overlayColor, + padding: EdgeInsets.zero, + elevation: 0, + shrinkExtent: 10, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + onTap: (index) => filter(snapshot.data![index] as T, context), + children: items, + ), + ); + }), + ], + ); + } +} diff --git a/lib/shared/widgets/enhanced_list_view.dart b/lib/shared/widgets/enhanced_list_view.dart index 79f01caf..75298097 100644 --- a/lib/shared/widgets/enhanced_list_view.dart +++ b/lib/shared/widgets/enhanced_list_view.dart @@ -20,6 +20,7 @@ typedef FooterRetrievalUseCase = Future> Function(); /// [Extensions] ---------------------------------------------------- +/// extension PaginatedListMergeExtensions on Stream>> { Stream> mergeWithPaginatedList( diff --git a/lib/shared/widgets/widgets.dart b/lib/shared/widgets/widgets.dart index 67f29a38..49bad510 100644 --- a/lib/shared/widgets/widgets.dart +++ b/lib/shared/widgets/widgets.dart @@ -1,6 +1,5 @@ import 'dart:developer'; import 'dart:io'; -import 'dart:nativewrappers/_internal/vm/lib/ffi_allocation_patch.dart'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:easy_debounce/easy_debounce.dart'; @@ -8,6 +7,7 @@ import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:hub/flutter_flow/index.dart'; import 'package:hub/shared/mixins/template_mixin.dart'; +import 'package:hub/shared/utils/index.dart'; import 'package:path_provider/path_provider.dart'; import 'package:pdfx/pdfx.dart'; import 'package:share_plus/share_plus.dart'; @@ -26,8 +26,8 @@ part 'entity.dart'; /// [View]'s part 'list_view.dart'; -part 'carousel_view.dart'; part 'read_view.dart'; +part 'enhanced_carousel_view.dart'; part 'enhanced_list_view.dart'; part 'enhanced_search_view.dart'; From a8220f7743f28696b21957da97345f7861ce0aea Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Thu, 20 Feb 2025 08:27:17 -0300 Subject: [PATCH 26/32] WIP --- .../backend/api_requests/api_calls.dart | 2 +- lib/features/documents/documents.dart | 8 +- lib/shared/constants.dart | 0 lib/shared/constants/index.dart | 1 - lib/shared/enums.dart | 1 + lib/shared/enums/index.dart | 1 - lib/shared/extensions.dart | 3 + lib/shared/extensions/index.dart | 3 - lib/shared/helpers.dart | 0 lib/shared/helpers/index.dart | 1 - lib/shared/index.dart | 1 - lib/shared/mixins.dart | 4 + lib/shared/mixins/index.dart | 3 - lib/shared/services.dart | 0 lib/shared/services/index.dart | 1 - lib/shared/utils.dart | 14 +++ lib/shared/utils/index.dart | 14 --- lib/shared/widgets.dart | 16 +++ lib/shared/widgets/component.dart | 3 +- .../widgets/enhanced_carousel_view.dart | 4 +- lib/shared/widgets/enhanced_list_view.dart | 7 +- lib/shared/widgets/enhanced_search_view.dart | 2 - lib/shared/widgets/entity.dart | 2 - lib/shared/widgets/list_view.dart | 2 - lib/shared/widgets/model.dart | 2 +- lib/shared/widgets/page.dart | 4 +- lib/shared/widgets/read_view.dart | 12 +- lib/shared/widgets/screen.dart | 4 +- lib/shared/widgets/text.dart | 2 +- lib/shared/widgets/widgets.dart | 35 ------ lib/shared/widgets/widgets.rxb.g.dart | 108 ------------------ 31 files changed, 71 insertions(+), 189 deletions(-) create mode 100644 lib/shared/constants.dart delete mode 100644 lib/shared/constants/index.dart create mode 100644 lib/shared/enums.dart delete mode 100644 lib/shared/enums/index.dart create mode 100644 lib/shared/extensions.dart delete mode 100644 lib/shared/extensions/index.dart create mode 100644 lib/shared/helpers.dart delete mode 100644 lib/shared/helpers/index.dart delete mode 100644 lib/shared/index.dart create mode 100644 lib/shared/mixins.dart delete mode 100644 lib/shared/mixins/index.dart create mode 100644 lib/shared/services.dart delete mode 100644 lib/shared/services/index.dart create mode 100644 lib/shared/utils.dart delete mode 100644 lib/shared/utils/index.dart create mode 100644 lib/shared/widgets.dart delete mode 100644 lib/shared/widgets/widgets.dart delete mode 100644 lib/shared/widgets/widgets.rxb.g.dart diff --git a/lib/features/backend/api_requests/api_calls.dart b/lib/features/backend/api_requests/api_calls.dart index 0e0a7421..192bae1a 100644 --- a/lib/features/backend/api_requests/api_calls.dart +++ b/lib/features/backend/api_requests/api_calls.dart @@ -11,7 +11,7 @@ import 'package:hub/features/storage/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 'package:hub/shared/widgets.dart'; import '/flutter_flow/flutter_flow_util.dart'; import 'api_manager.dart'; diff --git a/lib/features/documents/documents.dart b/lib/features/documents/documents.dart index ba712d91..a3a9eab1 100644 --- a/lib/features/documents/documents.dart +++ b/lib/features/documents/documents.dart @@ -1,17 +1,15 @@ import 'dart:developer'; - import 'package:easy_debounce/easy_debounce.dart'; import 'package:flutter/material.dart'; import 'package:hub/components/templates_components/details_component/details_component_widget.dart'; 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/utils/index.dart'; -import 'package:hub/shared/widgets/widgets.dart'; +import 'package:hub/shared/extensions.dart'; +import 'package:hub/shared/utils.dart'; +import 'package:hub/shared/widgets.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_rx_bloc/flutter_rx_bloc.dart'; - import 'package:rx_bloc/rx_bloc.dart'; import 'package:rxdart/rxdart.dart' as rx; diff --git a/lib/shared/constants.dart b/lib/shared/constants.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/shared/constants/index.dart b/lib/shared/constants/index.dart deleted file mode 100644 index 8b137891..00000000 --- a/lib/shared/constants/index.dart +++ /dev/null @@ -1 +0,0 @@ - diff --git a/lib/shared/enums.dart b/lib/shared/enums.dart new file mode 100644 index 00000000..85af0099 --- /dev/null +++ b/lib/shared/enums.dart @@ -0,0 +1 @@ +export 'enums/enum_throw_exception.dart'; diff --git a/lib/shared/enums/index.dart b/lib/shared/enums/index.dart deleted file mode 100644 index 585bd715..00000000 --- a/lib/shared/enums/index.dart +++ /dev/null @@ -1 +0,0 @@ -export 'enum_throw_exception.dart'; diff --git a/lib/shared/extensions.dart b/lib/shared/extensions.dart new file mode 100644 index 00000000..c07e669c --- /dev/null +++ b/lib/shared/extensions.dart @@ -0,0 +1,3 @@ +export 'extensions/dialog_extensions.dart'; +export 'extensions/flutter_secure_storage_extensions.dart'; +export 'extensions/string_extensions.dart'; diff --git a/lib/shared/extensions/index.dart b/lib/shared/extensions/index.dart deleted file mode 100644 index 0f71878e..00000000 --- a/lib/shared/extensions/index.dart +++ /dev/null @@ -1,3 +0,0 @@ -export 'dialog_extensions.dart'; -export 'flutter_secure_storage_extensions.dart'; -export 'string_extensions.dart'; diff --git a/lib/shared/helpers.dart b/lib/shared/helpers.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/shared/helpers/index.dart b/lib/shared/helpers/index.dart deleted file mode 100644 index 8b137891..00000000 --- a/lib/shared/helpers/index.dart +++ /dev/null @@ -1 +0,0 @@ - diff --git a/lib/shared/index.dart b/lib/shared/index.dart deleted file mode 100644 index 8b137891..00000000 --- a/lib/shared/index.dart +++ /dev/null @@ -1 +0,0 @@ - diff --git a/lib/shared/mixins.dart b/lib/shared/mixins.dart new file mode 100644 index 00000000..bd86cf22 --- /dev/null +++ b/lib/shared/mixins.dart @@ -0,0 +1,4 @@ +export 'mixins/pegeable_mixin.dart'; +export 'mixins/status_mixin.dart'; +export 'mixins/switcher_mixin.dart'; +export 'mixins/template_mixin.dart'; diff --git a/lib/shared/mixins/index.dart b/lib/shared/mixins/index.dart deleted file mode 100644 index 974783fd..00000000 --- a/lib/shared/mixins/index.dart +++ /dev/null @@ -1,3 +0,0 @@ -export 'pegeable_mixin.dart'; -export 'status_mixin.dart'; -export 'switcher_mixin.dart'; diff --git a/lib/shared/services.dart b/lib/shared/services.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/shared/services/index.dart b/lib/shared/services/index.dart deleted file mode 100644 index 8b137891..00000000 --- a/lib/shared/services/index.dart +++ /dev/null @@ -1 +0,0 @@ - diff --git a/lib/shared/utils.dart b/lib/shared/utils.dart new file mode 100644 index 00000000..09901b4d --- /dev/null +++ b/lib/shared/utils.dart @@ -0,0 +1,14 @@ +export 'utils/biometric_util.dart'; +export 'utils/device_util.dart'; +export 'utils/dialog_util.dart'; +export 'utils/image_util.dart'; +export 'utils/limited_text_size.dart'; +export 'utils/loading_util.dart'; +export 'utils/log_util.dart'; +export 'utils/path_util.dart'; +export 'utils/share_util.dart'; +export 'utils/snackbar_util.dart'; +export 'utils/string_util.dart'; +export 'utils/text_util.dart'; +export 'utils/validator_util.dart'; +export 'utils/webview_util.dart'; diff --git a/lib/shared/utils/index.dart b/lib/shared/utils/index.dart deleted file mode 100644 index 6d20915d..00000000 --- a/lib/shared/utils/index.dart +++ /dev/null @@ -1,14 +0,0 @@ -export 'biometric_util.dart'; -export 'device_util.dart'; -export 'dialog_util.dart'; -export 'image_util.dart'; -export 'limited_text_size.dart'; -export 'loading_util.dart'; -export 'log_util.dart'; -export 'path_util.dart'; -export 'share_util.dart'; -export 'snackbar_util.dart'; -export 'string_util.dart'; -export 'text_util.dart'; -export 'validator_util.dart'; -export 'webview_util.dart'; diff --git a/lib/shared/widgets.dart b/lib/shared/widgets.dart new file mode 100644 index 00000000..6c68e385 --- /dev/null +++ b/lib/shared/widgets.dart @@ -0,0 +1,16 @@ +/// [Base] +export 'widgets/page.dart'; +export 'widgets/component.dart'; +export 'widgets/screen.dart'; +export 'widgets/model.dart'; +export 'widgets/entity.dart'; + +/// [View]'s +export 'widgets/list_view.dart'; +export 'widgets/read_view.dart'; +export 'widgets/enhanced_carousel_view.dart'; +export 'widgets/enhanced_list_view.dart'; +export 'widgets/enhanced_search_view.dart'; + +/// [Component]'s +export 'widgets/text.dart'; diff --git a/lib/shared/widgets/component.dart b/lib/shared/widgets/component.dart index 74fc9ae1..a622f134 100644 --- a/lib/shared/widgets/component.dart +++ b/lib/shared/widgets/component.dart @@ -1,4 +1,5 @@ -part of 'widgets.dart'; +import 'package:flutter/material.dart'; +import 'package:hub/shared/widgets.dart'; /// [ComponentWidget] diff --git a/lib/shared/widgets/enhanced_carousel_view.dart b/lib/shared/widgets/enhanced_carousel_view.dart index 7338dfb0..c6bb4349 100644 --- a/lib/shared/widgets/enhanced_carousel_view.dart +++ b/lib/shared/widgets/enhanced_carousel_view.dart @@ -1,4 +1,6 @@ -part of 'widgets.dart'; +import 'package:flutter/material.dart'; +import 'package:hub/flutter_flow/index.dart'; +import 'package:hub/shared/utils.dart'; class EnhancedCarouselView extends StatelessWidget { final Future> Function() dataProvider; diff --git a/lib/shared/widgets/enhanced_list_view.dart b/lib/shared/widgets/enhanced_list_view.dart index 75298097..a2de85e5 100644 --- a/lib/shared/widgets/enhanced_list_view.dart +++ b/lib/shared/widgets/enhanced_list_view.dart @@ -1,4 +1,9 @@ -part of 'widgets.dart'; +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:rx_bloc/rx_bloc.dart'; +import 'package:rx_bloc_list/rx_bloc_list.dart'; +import 'package:rxdart/rxdart.dart'; /// [TypeDefs] ---------------------------------------------------- diff --git a/lib/shared/widgets/enhanced_search_view.dart b/lib/shared/widgets/enhanced_search_view.dart index fb18038a..d8ffda2e 100644 --- a/lib/shared/widgets/enhanced_search_view.dart +++ b/lib/shared/widgets/enhanced_search_view.dart @@ -1,5 +1,3 @@ -part of 'widgets.dart'; - // class EnhancedSearchView extends StatelessWidget { // const EnhancedSearchView({ // super.key, diff --git a/lib/shared/widgets/entity.dart b/lib/shared/widgets/entity.dart index ae05f63f..58bcaa43 100644 --- a/lib/shared/widgets/entity.dart +++ b/lib/shared/widgets/entity.dart @@ -1,3 +1 @@ -part of 'widgets.dart'; - abstract class Entity {} diff --git a/lib/shared/widgets/list_view.dart b/lib/shared/widgets/list_view.dart index e1074f79..04a3e12c 100644 --- a/lib/shared/widgets/list_view.dart +++ b/lib/shared/widgets/list_view.dart @@ -1,5 +1,3 @@ -part of 'widgets.dart'; - // typedef SearchKey = GlobalKey; // typedef Query = X?; diff --git a/lib/shared/widgets/model.dart b/lib/shared/widgets/model.dart index c35a6491..0bea7b2f 100644 --- a/lib/shared/widgets/model.dart +++ b/lib/shared/widgets/model.dart @@ -1,4 +1,4 @@ -part of 'widgets.dart'; +import 'package:flutter/material.dart'; class ModelWidget extends Widget { const ModelWidget({super.key}); diff --git a/lib/shared/widgets/page.dart b/lib/shared/widgets/page.dart index 8fda7fad..492c3651 100644 --- a/lib/shared/widgets/page.dart +++ b/lib/shared/widgets/page.dart @@ -1,4 +1,6 @@ -part of 'widgets.dart'; +import 'package:flutter/widgets.dart'; +import 'package:hub/shared/mixins/template_mixin.dart'; +import 'package:hub/shared/widgets.dart'; /// [PageWidget] diff --git a/lib/shared/widgets/read_view.dart b/lib/shared/widgets/read_view.dart index 2e5208e0..b3e394ca 100644 --- a/lib/shared/widgets/read_view.dart +++ b/lib/shared/widgets/read_view.dart @@ -1,6 +1,14 @@ -part of 'widgets.dart'; +import 'dart:developer'; +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:hub/features/backend/index.dart'; +import 'package:hub/flutter_flow/index.dart'; +import 'package:hub/shared/widgets.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:pdfx/pdfx.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:http/http.dart' as http; -// typedef PDFViewerKey = GlobalKey; typedef ReadViewController = PdfController; typedef DocumentType = PdfDocument; diff --git a/lib/shared/widgets/screen.dart b/lib/shared/widgets/screen.dart index f4dd20c8..3acf7a57 100644 --- a/lib/shared/widgets/screen.dart +++ b/lib/shared/widgets/screen.dart @@ -1,4 +1,6 @@ -part of 'widgets.dart'; +import 'package:flutter/material.dart'; +import 'package:hub/shared/mixins/template_mixin.dart'; +import 'package:hub/shared/widgets.dart'; abstract class ScreenWidget extends Widget { const ScreenWidget({super.key}); diff --git a/lib/shared/widgets/text.dart b/lib/shared/widgets/text.dart index 68b8b065..c18c2861 100644 --- a/lib/shared/widgets/text.dart +++ b/lib/shared/widgets/text.dart @@ -1,4 +1,4 @@ -part of 'widgets.dart'; +import 'package:auto_size_text/auto_size_text.dart'; class AutoText extends AutoSizeText { const AutoText( diff --git a/lib/shared/widgets/widgets.dart b/lib/shared/widgets/widgets.dart deleted file mode 100644 index 49bad510..00000000 --- a/lib/shared/widgets/widgets.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'dart:developer'; -import 'dart:io'; - -import 'package:auto_size_text/auto_size_text.dart'; -import 'package:easy_debounce/easy_debounce.dart'; -import 'package:flutter/material.dart'; -import 'package:http/http.dart' as http; -import 'package:hub/flutter_flow/index.dart'; -import 'package:hub/shared/mixins/template_mixin.dart'; -import 'package:hub/shared/utils/index.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:pdfx/pdfx.dart'; -import 'package:share_plus/share_plus.dart'; -import 'package:rx_bloc_list/rx_bloc_list.dart'; -import 'package:rxdart/rxdart.dart'; -import 'package:rx_bloc/rx_bloc.dart'; - -part 'widgets.rxb.g.dart'; - -/// [Base] -part 'page.dart'; -part 'component.dart'; -part 'screen.dart'; -part 'model.dart'; -part 'entity.dart'; - -/// [View]'s -part 'list_view.dart'; -part 'read_view.dart'; -part 'enhanced_carousel_view.dart'; -part 'enhanced_list_view.dart'; -part 'enhanced_search_view.dart'; - -/// [Component]'s -part 'text.dart'; diff --git a/lib/shared/widgets/widgets.rxb.g.dart b/lib/shared/widgets/widgets.rxb.g.dart deleted file mode 100644 index 4dd0c665..00000000 --- a/lib/shared/widgets/widgets.rxb.g.dart +++ /dev/null @@ -1,108 +0,0 @@ -// // dart format width=80 -// // GENERATED CODE - DO NOT MODIFY BY HAND - -// // ************************************************************************** -// // Generator: RxBlocGeneratorForAnnotation -// // ************************************************************************** - -part of 'widgets.dart'; - -// /// Used as a contractor for the bloc, events and states classes -// /// @nodoc -// abstract class EnhancedListViewBlocType extends RxBlocTypeBase { -// EnhancedListViewEvents get events; -// EnhancedListViewStates get states; -// } - -// /// [$EnhancedListViewBloc] extended by the [EnhancedListViewBloc] -// /// @nodoc -// abstract class $EnhancedListViewBloc extends RxBlocBase -// implements -// EnhancedListViewEvents, -// EnhancedListViewStates, -// EnhancedListViewBlocType { -// final _compositeSubscription = CompositeSubscription(); - -// /// Тhe [Subject] where events sink to by calling [loadBodyItems] -// final _$loadBodyItemsEvent = PublishSubject<({bool reset, dynamic query})>(); - -// /// Тhe [Subject] where events sink to by calling [loadHeaderItems] -// final _$loadHeaderItemsEvent = PublishSubject(); - -// /// Тhe [Subject] where events sink to by calling [loadFooterItems] -// final _$loadFooterItemsEvent = PublishSubject(); - -// /// The state of [bodyItems] implemented in [_mapToBodyItemsState] -// late final Stream> _bodyItemsState = _mapToBodyItemsState(); - -// /// The state of [headerItems] implemented in [_mapToHeaderItemsState] -// late final Stream> _headerItemsState = _mapToHeaderItemsState(); - -// /// The state of [footerItems] implemented in [_mapToFooterItemsState] -// late final Stream> _footerItemsState = _mapToFooterItemsState(); - -// /// The state of [isLoading] implemented in [_mapToIsLoadingState] -// late final Stream _isLoadingState = _mapToIsLoadingState(); - -// /// The state of [errors] implemented in [_mapToErrorsState] -// late final Stream _errorsState = _mapToErrorsState(); - -// @override -// void loadBodyItems({ -// bool reset = false, -// dynamic query = null, -// }) => -// _$loadBodyItemsEvent.add(( -// reset: reset, -// query: query, -// )); - -// @override -// void loadHeaderItems() => _$loadHeaderItemsEvent.add(null); - -// @override -// void loadFooterItems() => _$loadFooterItemsEvent.add(null); - -// @override -// Stream> get bodyItems => _bodyItemsState; - -// @override -// Stream> get headerItems => _headerItemsState; - -// @override -// Stream> get footerItems => _footerItemsState; - -// @override -// Stream get isLoading => _isLoadingState; - -// @override -// Stream get errors => _errorsState; - -// Stream> _mapToBodyItemsState(); - -// Stream> _mapToHeaderItemsState(); - -// Stream> _mapToFooterItemsState(); - -// Stream _mapToIsLoadingState(); - -// Stream _mapToErrorsState(); - -// @override -// EnhancedListViewEvents get events => this; - -// @override -// EnhancedListViewStates get states => this; - -// @override -// void dispose() { -// _$loadBodyItemsEvent.close(); -// _$loadHeaderItemsEvent.close(); -// _$loadFooterItemsEvent.close(); -// _compositeSubscription.dispose(); -// super.dispose(); -// } -// } - -// // ignore: unused_element -// typedef _LoadBodyItemsEventArgs = ({bool reset, dynamic query}); From 00e5f416b99543662069fd6edfe16ce9635ea22e Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Thu, 20 Feb 2025 08:33:39 -0300 Subject: [PATCH 27/32] misc --- lib/features/documents/documents.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/features/documents/documents.dart b/lib/features/documents/documents.dart index a3a9eab1..1bf083f9 100644 --- a/lib/features/documents/documents.dart +++ b/lib/features/documents/documents.dart @@ -170,8 +170,6 @@ class _DocumentViewerScreenState extends ScreenState { final String title = widget.doc.$1.description; final theme = FlutterFlowTheme.of(context); final locale = FFLocalizations.of(context); - final Color color = widget.doc.$1.category.color; - ; backAction() => widget.bloc.events.unselectDocument(); infoAction() => DetailsComponentWidget( From d03b3fbdfed834a17073bd2a026bd5f8757b6d6c Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Thu, 20 Feb 2025 08:52:59 -0300 Subject: [PATCH 28/32] resolve conflits --- .../details_component_widget.dart | 410 +++++++++--------- .../backend/api_requests/api_calls.dart | 11 +- .../notification/deep_link_service.dart | 1 + lib/initialization.dart | 2 + .../vehicle_history_screen.dart | 2 +- .../vehicle_model.dart | 16 +- .../vehicles_on_the_property.dart | 2 +- pubspec.yaml | 1 - 8 files changed, 228 insertions(+), 217 deletions(-) diff --git a/lib/components/templates_components/details_component/details_component_widget.dart b/lib/components/templates_components/details_component/details_component_widget.dart index 67a21e19..6e78a391 100644 --- a/lib/components/templates_components/details_component/details_component_widget.dart +++ b/lib/components/templates_components/details_component/details_component_widget.dart @@ -67,75 +67,114 @@ class _DetailsComponentWidgetState extends State { // CachedNetworkImage.evictFromCache(widget.imagePath ?? ''); final double limitedBodyFontSize = LimitedFontSizeUtil.getBodyFontSize(context); - return Container( - constraints: BoxConstraints( - maxWidth: MediaQuery.of(context).size.width, - maxHeight: MediaQuery.of(context).size.height, - ), - decoration: BoxDecoration( - color: FlutterFlowTheme.of(context).primaryBackground, - borderRadius: const BorderRadius.all(Radius.circular(25.0)), - ), - child: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - SizedBox(height: MediaQuery.of(context).size.height * 0.02), - if (widget.imagePath != null && widget.imagePath != '') - Container( - width: MediaQuery.of(context).size.width * 0.3, - height: MediaQuery.of(context).size.width * 0.3, - clipBehavior: Clip.antiAlias, - decoration: const BoxDecoration( - shape: BoxShape.circle, + return Material( + type: MaterialType.transparency, + child: Container( + constraints: BoxConstraints( + maxWidth: MediaQuery.of(context).size.width, + maxHeight: MediaQuery.of(context).size.height, + ), + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context).primaryBackground, + borderRadius: const BorderRadius.all(Radius.circular(25.0)), + ), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SizedBox(height: MediaQuery.of(context).size.height * 0.02), + if (widget.imagePath != null && widget.imagePath != '') + Container( + width: MediaQuery.of(context).size.width * 0.3, + height: MediaQuery.of(context).size.width * 0.3, + clipBehavior: Clip.antiAlias, + decoration: const BoxDecoration( + shape: BoxShape.circle, + ), + child: CachedNetworkImage( + fadeInDuration: const Duration(milliseconds: 100), + fadeOutDuration: const Duration(milliseconds: 100), + imageUrl: widget.imagePath!, + fit: BoxFit.cover, + useOldImageOnUrlChange: true, + ), ), - child: CachedNetworkImage( - fadeInDuration: const Duration(milliseconds: 100), - fadeOutDuration: const Duration(milliseconds: 100), - imageUrl: widget.imagePath!, - fit: BoxFit.cover, - useOldImageOnUrlChange: true, + if (widget.icon != null && widget.icon != '') + Container( + width: MediaQuery.of(context).size.width * 0.3, + height: MediaQuery.of(context).size.width * 0.3, + clipBehavior: Clip.antiAlias, + decoration: const BoxDecoration( + shape: BoxShape.circle, + ), + child: widget.icon!, ), - ), - if (widget.icon != null && widget.icon != '') - Container( - width: MediaQuery.of(context).size.width * 0.3, - height: MediaQuery.of(context).size.width * 0.3, - clipBehavior: Clip.antiAlias, - decoration: const BoxDecoration( - shape: BoxShape.circle, - ), - child: widget.icon!, - ), - SizedBox(height: MediaQuery.of(context).size.height * 0.03), - Row( - children: statusLinkedHashMap.expand((linkedHashMap) { - return linkedHashMap.entries - .map((MapEntry item) { - return Expanded( - child: Padding( - padding: EdgeInsets.symmetric( - horizontal: MediaQuery.of(context).size.width * 0.05, - ), - child: TextFormField( - autofocus: false, - canRequestFocus: false, - readOnly: true, - initialValue: item.key, - obscureText: false, - decoration: InputDecoration( - isDense: true, - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10.0), - borderSide: BorderSide( - color: item.value, + SizedBox(height: MediaQuery.of(context).size.height * 0.03), + Row( + children: statusLinkedHashMap.expand((linkedHashMap) { + return linkedHashMap.entries + .map((MapEntry item) { + return Expanded( + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.05, + ), + child: TextFormField( + autofocus: false, + canRequestFocus: false, + readOnly: true, + initialValue: item.key, + obscureText: false, + decoration: InputDecoration( + isDense: true, + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10.0), + borderSide: BorderSide( + color: item.value, + ), + ), + filled: true, + fillColor: item.value, + // labelText: item.key, + labelStyle: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: FlutterFlowTheme.of(context) + .labelMediumFamily, + fontWeight: FontWeight.bold, + color: FlutterFlowTheme.of(context).info, + letterSpacing: 0.0, + useGoogleFonts: + GoogleFonts.asMap().containsKey( + FlutterFlowTheme.of(context) + .labelMediumFamily, + ), + fontSize: limitedBodyFontSize, + ), + hintStyle: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: FlutterFlowTheme.of(context) + .labelMediumFamily, + color: FlutterFlowTheme.of(context).info, + letterSpacing: 0.0, + useGoogleFonts: + GoogleFonts.asMap().containsKey( + FlutterFlowTheme.of(context) + .labelMediumFamily, + ), + fontSize: limitedBodyFontSize, + ), + focusedBorder: InputBorder.none, + errorBorder: InputBorder.none, + focusedErrorBorder: InputBorder.none, + suffixIcon: Icon( + Icons.info, + color: FlutterFlowTheme.of(context).info, ), ), - filled: true, - fillColor: item.value, - // labelText: item.key, - labelStyle: FlutterFlowTheme.of(context) + style: FlutterFlowTheme.of(context) .labelMedium .override( fontFamily: FlutterFlowTheme.of(context) @@ -149,150 +188,117 @@ class _DetailsComponentWidgetState extends State { ), fontSize: limitedBodyFontSize, ), - hintStyle: FlutterFlowTheme.of(context) - .labelMedium - .override( - fontFamily: FlutterFlowTheme.of(context) - .labelMediumFamily, - color: FlutterFlowTheme.of(context).info, - letterSpacing: 0.0, - useGoogleFonts: GoogleFonts.asMap().containsKey( - FlutterFlowTheme.of(context) - .labelMediumFamily, - ), - fontSize: limitedBodyFontSize, - ), - focusedBorder: InputBorder.none, - errorBorder: InputBorder.none, - focusedErrorBorder: InputBorder.none, - suffixIcon: Icon( - Icons.info, - color: FlutterFlowTheme.of(context).info, - ), + textAlign: TextAlign.center, + maxLines: null, + keyboardType: TextInputType.name, + validator: _model.textController1Validator + .asValidator(context), ), - style: FlutterFlowTheme.of(context) - .labelMedium - .override( - fontFamily: FlutterFlowTheme.of(context) - .labelMediumFamily, - fontWeight: FontWeight.bold, - color: FlutterFlowTheme.of(context).info, - letterSpacing: 0.0, - useGoogleFonts: GoogleFonts.asMap().containsKey( + ), + ); + }).toList(); + }).toList(), + ), + SizedBox(height: MediaQuery.of(context).size.height * 0.03), + ListView.builder( + shrinkWrap: true, + itemCount: labelsLinkedHashMap.length, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + String key = labelsLinkedHashMap.keys.elementAt(index); + String value = labelsLinkedHashMap[key]!; + // return Text('key: $key, value: $value'); + return TextFormField( + readOnly: true, + initialValue: value, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: + FlutterFlowTheme.of(context).bodyMediumFamily, + color: FlutterFlowTheme.of(context).primaryText, + letterSpacing: 0.0, + useGoogleFonts: GoogleFonts.asMap().containsKey( + FlutterFlowTheme.of(context).bodyMediumFamily, + ), + fontSize: limitedBodyFontSize, + ), + decoration: InputDecoration( + labelText: key, + filled: true, + fillColor: FlutterFlowTheme.of(context).primaryBackground, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10.0), + borderSide: BorderSide( + color: FlutterFlowTheme.of(context) + .primaryBackground, // Change border color here + ), + ), + labelStyle: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: FlutterFlowTheme.of(context).labelMediumFamily, - ), - fontSize: limitedBodyFontSize, + color: FlutterFlowTheme.of(context).primaryText, + letterSpacing: 0.0, + useGoogleFonts: GoogleFonts.asMap().containsKey( + FlutterFlowTheme.of(context).labelMediumFamily, ), - textAlign: TextAlign.center, - maxLines: null, - keyboardType: TextInputType.name, - validator: _model.textController1Validator - .asValidator(context), + ), + hintStyle: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: + FlutterFlowTheme.of(context).labelMediumFamily, + color: FlutterFlowTheme.of(context).primaryText, + letterSpacing: 0.0, + useGoogleFonts: GoogleFonts.asMap().containsKey( + FlutterFlowTheme.of(context).labelMediumFamily, + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10.0), + borderSide: BorderSide( + color: FlutterFlowTheme.of(context) + .primaryBackground, // Change border color here + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10.0), + borderSide: BorderSide( + color: FlutterFlowTheme.of(context) + .primaryBackground, // Change border color here + ), + ), + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10.0), + borderSide: BorderSide( + color: FlutterFlowTheme.of(context) + .primaryBackground, // Change border color here + ), + ), + focusedErrorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10.0), + borderSide: BorderSide( + color: FlutterFlowTheme.of(context) + .primaryBackground, // Change border color here + ), ), ), ); - }).toList(); - }).toList(), - ), - SizedBox(height: MediaQuery.of(context).size.height * 0.03), - ListView.builder( - shrinkWrap: true, - itemCount: labelsLinkedHashMap.length, - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (context, index) { - String key = labelsLinkedHashMap.keys.elementAt(index); - String value = labelsLinkedHashMap[key]!; - // return Text('key: $key, value: $value'); - return TextFormField( - readOnly: true, - initialValue: value, - style: FlutterFlowTheme.of(context).bodyMedium.override( - fontFamily: - FlutterFlowTheme.of(context).bodyMediumFamily, - color: FlutterFlowTheme.of(context).primaryText, - letterSpacing: 0.0, - useGoogleFonts: GoogleFonts.asMap().containsKey( - FlutterFlowTheme.of(context).bodyMediumFamily, - ), - fontSize: limitedBodyFontSize, - ), - decoration: InputDecoration( - labelText: key, - filled: true, - fillColor: FlutterFlowTheme.of(context).primaryBackground, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10.0), - borderSide: BorderSide( - color: FlutterFlowTheme.of(context) - .primaryBackground, // Change border color here - ), - ), - labelStyle: FlutterFlowTheme.of(context) - .labelMedium - .override( - fontFamily: - FlutterFlowTheme.of(context).labelMediumFamily, - color: FlutterFlowTheme.of(context).primaryText, - letterSpacing: 0.0, - useGoogleFonts: GoogleFonts.asMap().containsKey( - FlutterFlowTheme.of(context).labelMediumFamily, - ), - ), - hintStyle: FlutterFlowTheme.of(context) - .labelMedium - .override( - fontFamily: - FlutterFlowTheme.of(context).labelMediumFamily, - color: FlutterFlowTheme.of(context).primaryText, - letterSpacing: 0.0, - useGoogleFonts: GoogleFonts.asMap().containsKey( - FlutterFlowTheme.of(context).labelMediumFamily, - ), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10.0), - borderSide: BorderSide( - color: FlutterFlowTheme.of(context) - .primaryBackground, // Change border color here - ), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10.0), - borderSide: BorderSide( - color: FlutterFlowTheme.of(context) - .primaryBackground, // Change border color here - ), - ), - errorBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10.0), - borderSide: BorderSide( - color: FlutterFlowTheme.of(context) - .primaryBackground, // Change border color here - ), - ), - focusedErrorBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10.0), - borderSide: BorderSide( - color: FlutterFlowTheme.of(context) - .primaryBackground, // Change border color here - ), - ), - ), - ); - }, - ), - SizedBox(height: MediaQuery.of(context).size.height * 0.02), - if (widget.buttons!.isNotEmpty || widget.buttons != null) - OverflowBar( - overflowAlignment: OverflowBarAlignment.center, - alignment: MainAxisAlignment.center, - overflowSpacing: 2, - spacing: 2, - // mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: widget.buttons!, + }, ), - SizedBox(height: MediaQuery.of(context).size.height * 0.02), - ], + SizedBox(height: MediaQuery.of(context).size.height * 0.02), + if (widget.buttons!.isNotEmpty || widget.buttons != null) + OverflowBar( + overflowAlignment: OverflowBarAlignment.center, + alignment: MainAxisAlignment.center, + overflowSpacing: 2, + spacing: 2, + // mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: widget.buttons!, + ), + SizedBox(height: MediaQuery.of(context).size.height * 0.02), + ], + ), ), ), ); diff --git a/lib/features/backend/api_requests/api_calls.dart b/lib/features/backend/api_requests/api_calls.dart index da746b83..78d1444d 100644 --- a/lib/features/backend/api_requests/api_calls.dart +++ b/lib/features/backend/api_requests/api_calls.dart @@ -79,6 +79,9 @@ class FreAccessWSGlobal extends Api { static UpdateVehicle updateVehicle = UpdateVehicle(); static DeleteVehicle deleteVehicle = DeleteVehicle(); static CancelDeleteVehicle cancelDelete = CancelDeleteVehicle(); + static GetPDF getPDF = GetPDF(); + static GetCategories getCategories = GetCategories(); + static GetDocuments getDocuments = GetDocuments(); } class CancelDeleteVehicle { @@ -88,7 +91,7 @@ class CancelDeleteVehicle { required final String model, required final String color, }) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; final String userUUID = @@ -130,7 +133,7 @@ class DeleteVehicle { required final String model, required final String color, }) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; final String userUUID = @@ -173,7 +176,7 @@ class RegisterVehicle { final String? color, final String? model, }) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; @@ -217,7 +220,7 @@ class UpdateVehicle { final String? color, final String? model, }) async { - final String baseUrl = PhpGroup.getBaseUrl(); + final String baseUrl = FreAccessWSGlobal.getBaseUrl(); final String devUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; final String userUUID = diff --git a/lib/features/notification/deep_link_service.dart b/lib/features/notification/deep_link_service.dart index aac0351c..003f1903 100644 --- a/lib/features/notification/deep_link_service.dart +++ b/lib/features/notification/deep_link_service.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:developer'; import 'package:app_links/app_links.dart'; import 'package:flutter/material.dart'; import 'package:hub/features/storage/index.dart'; diff --git a/lib/initialization.dart b/lib/initialization.dart index 82902a36..808d3220 100644 --- a/lib/initialization.dart +++ b/lib/initialization.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:app_tracking_transparency/app_tracking_transparency.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_crashlytics/firebase_crashlytics.dart'; diff --git a/lib/pages/vehicles_on_the_property/vehicle_history_screen.dart b/lib/pages/vehicles_on_the_property/vehicle_history_screen.dart index ddbf6271..7d6c7eae 100644 --- a/lib/pages/vehicles_on_the_property/vehicle_history_screen.dart +++ b/lib/pages/vehicles_on_the_property/vehicle_history_screen.dart @@ -10,7 +10,7 @@ class VehicleHistoryScreen extends StatefulWidget { class _VehicleHistoryScreenState extends State with Pageable { - final apiCall = PhpGroup.getVehiclesByProperty; + final apiCall = FreAccessWSGlobal.getVehiclesByProperty; int totalOwnerVehicles = 0; final PagingController _pagingController = PagingController(firstPageKey: 1); diff --git a/lib/pages/vehicles_on_the_property/vehicle_model.dart b/lib/pages/vehicles_on_the_property/vehicle_model.dart index 61a53dc5..0a5f23c6 100644 --- a/lib/pages/vehicles_on_the_property/vehicle_model.dart +++ b/lib/pages/vehicles_on_the_property/vehicle_model.dart @@ -7,8 +7,8 @@ import 'package:hub/features/backend/index.dart'; import 'package:hub/features/storage/index.dart'; import 'package:hub/flutter_flow/index.dart'; import 'package:hub/pages/vehicles_on_the_property/vehicles_on_the_property.dart'; -import 'package:hub/shared/extensions/index.dart'; -import 'package:hub/shared/utils/index.dart'; +import 'package:hub/shared/extensions.dart'; +import 'package:hub/shared/utils.dart'; /// [VehicleModel] is a class that contains the business logic of the vehicle page. class VehicleModel extends FlutterFlowModel @@ -196,7 +196,7 @@ mixin class _BaseVehiclePage { /// [_VehicleRegisterScreenModel] is a mixin that contains the business logic of the vehicle register page. mixin _VehicleRegisterScreenModel on _BaseVehiclePage { Future registerVehicle() async { - final response = await PhpGroup.registerVehicle.call( + final response = await FreAccessWSGlobal.registerVehicle.call( licensePlate: textFieldControllerLicensePlate!.text, color: textFieldControllerColor!.text, model: textFieldControllerModel!.text, @@ -218,7 +218,7 @@ mixin _VehicleRegisterScreenModel on _BaseVehiclePage { /// [_VehicleUpdateScreenModel] is a mixin that contains the business logic of the vehicle update page. mixin _VehicleUpdateScreenModel on _BaseVehiclePage { Future updateVehicle() async { - final response = await PhpGroup.updateVehicle.call( + final response = await FreAccessWSGlobal.updateVehicle.call( licensePlate: textFieldControllerLicensePlate!.text, color: textFieldControllerColor!.text, model: textFieldControllerModel!.text, @@ -403,7 +403,7 @@ mixin _VehicleHistoryScreenModel on _BaseVehiclePage { Future processDeleteRequest(dynamic item) async { log('processDeleteRequest -> item[$item]'); - bool result = await PhpGroup.deleteVehicle + bool result = await FreAccessWSGlobal.deleteVehicle .call( vehicleId: item['vehicleId'], licensePlate: item['licensePlate'], @@ -493,7 +493,7 @@ mixin _VehicleHistoryScreenModel on _BaseVehiclePage { } Future processCancelDeleteRequest(dynamic item) async { - return await PhpGroup.deleteVehicle.call( + return await FreAccessWSGlobal.deleteVehicle.call( vehicleId: item['vehicleId'], licensePlate: item['licensePlate'], model: item['model'], @@ -502,7 +502,7 @@ mixin _VehicleHistoryScreenModel on _BaseVehiclePage { } Future processCancelUpdateRequest(dynamic item) async { - return await PhpGroup.deleteVehicle.call( + return await FreAccessWSGlobal.deleteVehicle.call( vehicleId: item['vehicleId'], licensePlate: item['licensePlate'], model: item['model'], @@ -511,7 +511,7 @@ mixin _VehicleHistoryScreenModel on _BaseVehiclePage { } Future processCancelCreateRequest(dynamic item) async { - return await PhpGroup.cancelDelete.call( + return await FreAccessWSGlobal.cancelDelete.call( vehicleId: item['vehicleId'], licensePlate: item['licensePlate'], model: item['model'], diff --git a/lib/pages/vehicles_on_the_property/vehicles_on_the_property.dart b/lib/pages/vehicles_on_the_property/vehicles_on_the_property.dart index 9e51e429..318d05c7 100644 --- a/lib/pages/vehicles_on_the_property/vehicles_on_the_property.dart +++ b/lib/pages/vehicles_on_the_property/vehicles_on_the_property.dart @@ -13,7 +13,7 @@ import 'package:hub/features/backend/index.dart'; import 'package:hub/features/module/index.dart'; import 'package:hub/flutter_flow/index.dart'; import 'package:hub/pages/vehicles_on_the_property/vehicle_model.dart'; -import 'package:hub/shared/extensions/index.dart'; +import 'package:hub/shared/extensions.dart'; import 'package:hub/shared/mixins/pageable_mixin.dart'; import 'package:hub/shared/utils/dialog_util.dart'; import 'package:hub/shared/utils/license_util.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index 0999c417..28d1d316 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,6 @@ name: hub description: . # Descrição do projeto (adicione mais detalhes se necessário) publish_to: "none" # Destino de publicação -publish_to: "none" version: 1.4.0+27 From 575cfc5a83e4e73b64afb8e41625b60294f80bae Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Mon, 24 Feb 2025 17:30:41 -0300 Subject: [PATCH 29/32] fix nosso de cada dia --- lib/features/documents/documents.dart | 7 ++----- lib/shared/mixins/template_mixin.dart | 2 +- lib/shared/widgets/enhanced_carousel_view.dart | 1 + 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/features/documents/documents.dart b/lib/features/documents/documents.dart index 1bf083f9..b9377c6c 100644 --- a/lib/features/documents/documents.dart +++ b/lib/features/documents/documents.dart @@ -7,7 +7,6 @@ import 'package:hub/flutter_flow/index.dart'; import 'package:hub/shared/extensions.dart'; import 'package:hub/shared/utils.dart'; import 'package:hub/shared/widgets.dart'; -import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_rx_bloc/flutter_rx_bloc.dart'; import 'package:rx_bloc/rx_bloc.dart'; @@ -241,7 +240,6 @@ class DocumentModel extends FlutterFlowModel { late EnhancedListViewKey vehicleScreenManager; late DocumentKey vehicleScreenViewer; - late PagingController _pagingController; late bool categoryIsSelected; @@ -252,14 +250,12 @@ class DocumentModel extends FlutterFlowModel { vehicleScreenManager = EnhancedListViewKey(); vehicleScreenViewer = DocumentKey(); - _pagingController = PagingController(firstPageKey: 1); categoryIsSelected = false; } @override void dispose() { - _pagingController.dispose(); vehicleScreenManager.currentState?.dispose(); vehicleScreenViewer.currentState?.dispose(); } @@ -745,6 +741,7 @@ class CategoryComponent extends StatelessComponent { @override Widget build(BuildContext context) { final backgroundTheme = FlutterFlowTheme.of(context).primaryBackground; + final textTheme = FlutterFlowTheme.of(context).primaryText; return ColoredBox( color: backgroundTheme, child: Padding( @@ -767,7 +764,7 @@ class CategoryComponent extends StatelessComponent { Text( category.title, style: TextStyle( - color: category.color, + color: textTheme, fontWeight: FontWeight.bold, ), overflow: TextOverflow.ellipsis, diff --git a/lib/shared/mixins/template_mixin.dart b/lib/shared/mixins/template_mixin.dart index 2e3611d0..38e9268b 100644 --- a/lib/shared/mixins/template_mixin.dart +++ b/lib/shared/mixins/template_mixin.dart @@ -44,7 +44,7 @@ mixin Template { ), icon: Icon( Symbols.info_i_rounded, - color: Colors.black, + color: FlutterFlowTheme.of(context).primaryText, ), ) ]; diff --git a/lib/shared/widgets/enhanced_carousel_view.dart b/lib/shared/widgets/enhanced_carousel_view.dart index c6bb4349..0724ef2d 100644 --- a/lib/shared/widgets/enhanced_carousel_view.dart +++ b/lib/shared/widgets/enhanced_carousel_view.dart @@ -56,6 +56,7 @@ class EnhancedCarouselView extends StatelessWidget { overlayColor: overlayColor, padding: EdgeInsets.zero, elevation: 0, + reverse: true, shrinkExtent: 10, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), From aa1bd55818ac75f018340cd0e3b6f7a74d2728ba Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Mon, 24 Feb 2025 17:59:29 -0300 Subject: [PATCH 30/32] WIP --- lib/shared/widgets/read_view.dart | 82 ++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 28 deletions(-) diff --git a/lib/shared/widgets/read_view.dart b/lib/shared/widgets/read_view.dart index b3e394ca..6df9c6cb 100644 --- a/lib/shared/widgets/read_view.dart +++ b/lib/shared/widgets/read_view.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:hub/features/backend/index.dart'; import 'package:hub/flutter_flow/index.dart'; +import 'package:hub/shared/utils/dialog_util.dart'; import 'package:hub/shared/widgets.dart'; import 'package:path_provider/path_provider.dart'; import 'package:pdfx/pdfx.dart'; @@ -44,58 +45,78 @@ class ReadView extends StatefulWidget { class ReadViewState extends State { Future _initializePdf() async { - final file = await downloadPdf(widget.url); - final Future document = DocumentType.openFile(file.path); - return ReadViewController(document: document); + try { + final file = await downloadPdf(widget.url); + final Future document = DocumentType.openFile(file.path); + return ReadViewController(document: document); + } catch (e) { + logError('Erro ao baixar o PDF', e); + return Future.error(e); + } } Future downloadPdf(String url) async { - final response = await http.get(Uri.parse(url)); - if (response.statusCode == 200) { - final bytes = response.bodyBytes; - final dir = await getTemporaryDirectory(); - final file = File('${dir.path}/downloaded.pdf'); - await file.writeAsBytes(bytes); - return file; - } else { - throw Exception('Falha ao baixar o PDF'); + try { + final response = await http.get(Uri.parse(url)); + if (response.statusCode == 200) { + final bytes = response.bodyBytes; + final dir = await getTemporaryDirectory(); + final file = File('${dir.path}/downloaded.pdf'); + await file.writeAsBytes(bytes); + return file; + } else { + throw Exception('Falha ao baixar o PDF'); + } + } catch (e) { + logError('Erro ao baixar o PDF', e); + rethrow; } } + void logError(String message, dynamic error) { + log('$message: $error'); + DialogUtil.error(context, message); + } + @override Widget build(BuildContext context) { return Stack( children: [ - _buildPDFViewer(), - buildShareButton(), + _buildPDFViewer(context), + buildShareButton(context), ], ); } - Positioned buildShareButton() { + Positioned buildShareButton(BuildContext context) { + final theme = FlutterFlowTheme.of(context); return Positioned( bottom: 10, right: 10, child: IconButton( icon: Icon( Icons.share, - color: Colors.black, + color: theme.primaryText, ), - color: Colors.black, + color: theme.primaryText, onPressed: onShare, ), ); } void onShare() async { - final Uri uri = Uri.parse(widget.url); - final response = await http.get(uri); - if (response.statusCode == 200) { - final XFile xfile = XFile.fromData(response.bodyBytes, - name: '${widget.title}.pdf', mimeType: 'application/pdf'); - await Share.shareXFiles([xfile], text: 'Confira este PDF!'); - } else { - log('Erro ao baixar o arquivo: ${response.statusCode}'); + try { + final Uri uri = Uri.parse(widget.url); + final response = await http.get(uri); + if (response.statusCode == 200) { + final XFile xfile = XFile.fromData(response.bodyBytes, + name: '${widget.title}.pdf', mimeType: 'application/pdf'); + await Share.shareXFiles([xfile], text: 'Confira este PDF!'); + } else { + throw Exception('Erro ao compartilhar o arquivo: ${response.statusCode}'); + } + } catch (e) { + logError('Erro ao compartilhar o arquivo', e); } } @@ -112,12 +133,19 @@ class ReadViewState extends State { ); } - Widget _buildPDFViewer() => Padding( + Widget _buildPDFViewer(BuildContext context) => Padding( padding: EdgeInsets.all(10), child: FutureBuilder( future: _initializePdf(), builder: (context, snapshot) { if (!snapshot.hasData) return buildLoadingIndicator(context); + if (snapshot.error != null) { + return buildLoadingIndicator(context); + } + if (snapshot.connectionState == ConnectionState.waiting) { + return buildLoadingIndicator(context); + } + return PdfView( controller: snapshot.data!, renderer: (PdfPage page) => page.render( @@ -134,6 +162,4 @@ class ReadViewState extends State { ); }), ); - - // Widget get progressIndicator => LoadingUtil.buildLoadingIndicator(context); } From a7f1ea418b40d726cff47dd60d2f05b011d5a530 Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Tue, 25 Feb 2025 17:13:09 -0300 Subject: [PATCH 31/32] =?UTF-8?q?corre=C3=A7=C3=B5es=20do=20pr?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/api_requests/api_manager.dart | 1 - lib/features/documents/documents.dart | 124 +++++++++-------- .../locals_remote_data_source.dart | 11 +- lib/shared/utils.dart | 1 + lib/shared/utils/color_util.dart | 48 +++++++ lib/shared/widgets.dart | 2 + .../widgets/enhanced_carousel_view.dart | 128 +++++++++++------- lib/shared/widgets/enhanced_list_view.dart | 9 +- lib/shared/widgets/read_view.dart | 34 +++-- 9 files changed, 237 insertions(+), 121 deletions(-) create mode 100644 lib/shared/utils/color_util.dart diff --git a/lib/features/backend/api_requests/api_manager.dart b/lib/features/backend/api_requests/api_manager.dart index d12c03dd..7f13a68f 100644 --- a/lib/features/backend/api_requests/api_manager.dart +++ b/lib/features/backend/api_requests/api_manager.dart @@ -1,6 +1,5 @@ import 'dart:convert'; import 'dart:core'; -import 'dart:developer'; import 'dart:io'; import 'dart:typed_data'; diff --git a/lib/features/documents/documents.dart b/lib/features/documents/documents.dart index b9377c6c..1b84a344 100644 --- a/lib/features/documents/documents.dart +++ b/lib/features/documents/documents.dart @@ -164,13 +164,14 @@ class _DocumentViewerScreenState extends ScreenState { super.initState(); } + backAction() => widget.bloc.events.unselectDocument(); + @override Widget build(BuildContext context) { final String title = widget.doc.$1.description; final theme = FlutterFlowTheme.of(context); final locale = FFLocalizations.of(context); - backAction() => widget.bloc.events.unselectDocument(); infoAction() => DetailsComponentWidget( buttons: [], statusHashMap: [ @@ -225,6 +226,7 @@ class _DocumentViewerScreenState extends ScreenState { return ReadView( title: widget.doc.$1.description, url: widget.doc.$2.toString(), + onError: backAction, ); } } @@ -330,14 +332,16 @@ class DocumentModel extends FlutterFlowModel { Widget itemFooterBuilder( Future> Function() fetchData) => Builder(builder: (context) { - CategoryComponent categoryItemBuilder(T? item) { - return CategoryComponent(category: item! as Category); + CategoryComponent categoryItemBuilder(T? item, bool isSelected) { + return CategoryComponent( + category: item! as Category, isSelected: isSelected); } return EnhancedCarouselView( dataProvider: fetchData, itemBuilder: categoryItemBuilder, filter: filterByCategory, + showIndicator: true, ); }); @@ -654,64 +658,71 @@ class DocumentComponent extends StatelessComponent { final description = document.description; final title = document.category.title; const double size = 20; + final date = ValidatorUtil.toLocalDateTime( + 'yyyy-MM-dd', + document.updatedAt, + ); - return InkWell( - onTap: () => onPressed(document, context), - enableFeedback: true, - overlayColor: WidgetStateProperty.all(primaryColor), - borderRadius: BorderRadius.circular(10), - child: SizedBox( - height: boxHeight, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - spacing: size, - children: [ - // const SizedBox(width: 10), - Icon(icon, color: color), + return Tooltip( + message: description, + child: InkWell( + onTap: () => onPressed(document, context), + enableFeedback: true, + overlayColor: WidgetStateProperty.all(primaryColor), + borderRadius: BorderRadius.circular(10), + child: SizedBox( + height: boxHeight, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: size, + children: [ + // const SizedBox(width: 10), + Icon(icon, color: color), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Tooltip( - message: description, - child: AutoText( - description, - style: textStyleMajor, - overflow: TextOverflow.ellipsis, + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Tooltip( + message: description, + child: AutoText( + description, + style: textStyleMajor, + overflow: TextOverflow.ellipsis, + ), ), - ), - AutoText( - ValidatorUtil.toLocalDateTime( - 'yyyy-MM-dd', - document.updatedAt, + Tooltip( + message: date, + child: AutoText( + date, + style: textStyleMinor, + overflow: TextOverflow.ellipsis, + ), ), - style: textStyleMinor, - overflow: TextOverflow.ellipsis, - ), - ], + ], + ), ), - ), - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - _buildTooltip(title, color, context, constraints), - ], + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + _buildTooltip(title, color, context, constraints), + ], + ), ), - ), - // const SizedBox(width: 10), - Center( - child: Icon( - Icons.arrow_right, - color: primaryText, + // const SizedBox(width: 10), + Center( + child: Icon( + Icons.arrow_right, + color: primaryText, + ), ), - ), - ] // - .addToStart(space) - .addToEnd(space), + ] // + .addToStart(space) + .addToEnd(space), + ), ), ), ); @@ -732,16 +743,19 @@ class DocumentComponent extends StatelessComponent { class CategoryComponent extends StatelessComponent { final Category category; + final bool isSelected; const CategoryComponent({ super.key, required this.category, + this.isSelected = false, }); @override Widget build(BuildContext context) { final backgroundTheme = FlutterFlowTheme.of(context).primaryBackground; final textTheme = FlutterFlowTheme.of(context).primaryText; + final color = isSelected ? category.color.highlight : category.color; return ColoredBox( color: backgroundTheme, child: Padding( @@ -751,7 +765,7 @@ class CategoryComponent extends StatelessComponent { Container( padding: const EdgeInsets.all(8.0), decoration: BoxDecoration( - color: category.color, + color: color, shape: BoxShape.circle, ), child: Icon( diff --git a/lib/features/local/data/data_sources/locals_remote_data_source.dart b/lib/features/local/data/data_sources/locals_remote_data_source.dart index 39d4d166..c991ed37 100644 --- a/lib/features/local/data/data_sources/locals_remote_data_source.dart +++ b/lib/features/local/data/data_sources/locals_remote_data_source.dart @@ -34,20 +34,13 @@ class LocalsRemoteDataSourceImpl implements LocalsRemoteDataSource { try { final GetLocalsCall callback = FreAccessWSGlobal.getLocalsCall; var response = await callback.call(); - final bool? isError = response.jsonBody['error']; + if (response.jsonBody == null) return; + final bool? isError = response.jsonBody['error']; if (isError == true) { LocalUtil.handleError(context, response.jsonBody['error_msg']); return; } - if (response.jsonBody == null) { - // final String errorMsg = FFLocalizations.of(context).getVariableText( - // enText: 'Verify your connection', - // ptText: 'Verifique sua conexão', - // ); - // await DialogUtil.error(context, errorMsg).whenComplete(() async => await selectLocal(context, response)); - return; - } final List locals = response.jsonBody['locais'] ?? []; final bool isEmpty = locals.isEmpty; diff --git a/lib/shared/utils.dart b/lib/shared/utils.dart index 09901b4d..c3eae2ca 100644 --- a/lib/shared/utils.dart +++ b/lib/shared/utils.dart @@ -12,3 +12,4 @@ export 'utils/string_util.dart'; export 'utils/text_util.dart'; export 'utils/validator_util.dart'; export 'utils/webview_util.dart'; +export 'utils/color_util.dart'; diff --git a/lib/shared/utils/color_util.dart b/lib/shared/utils/color_util.dart new file mode 100644 index 00000000..291762a8 --- /dev/null +++ b/lib/shared/utils/color_util.dart @@ -0,0 +1,48 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; + +class ColorUtil { + static Color getContrastColor(Color a, Color b) { + double luminance(Color color) { + return (0.299 * color.r + 0.587 * color.g + 0.114 * color.b) / 255; + } + + double contrastRatio(Color a, Color b) { + final lumA = luminance(a) + 0.05; + final lumB = luminance(b) + 0.05; + return lumA > lumB ? lumA / lumB : lumB / lumA; + } + + if (contrastRatio(a, b) < 4.5) { + // Find a color with higher contrast within the same hue + final hsv = HSVColor.fromColor(a); + double hue = hsv.hue; + double saturation = hsv.saturation; + double brightness = hsv.value; + + // Increase brightness to ensure higher contrast + brightness = brightness > 0.5 ? brightness - 0.5 : brightness + 0.5; + + return HSVColor.fromAHSV(1.0, hue, saturation, brightness).toColor(); + } + + return b; + } + + static Color getSelfContrastColor(Color color) { + final hsv = HSVColor.fromColor(color); + double hue = hsv.hue; + double saturation = hsv.saturation; + double brightness = hsv.value; + + // Increase brightness to ensure higher contrast + brightness = brightness > 0.5 ? brightness - 0.5 : brightness + 0.5; + + return HSVColor.fromAHSV(1.0, hue, saturation, brightness).toColor(); + } +} + +extension ColorUtilExtension on Color { + Color get highlight => ColorUtil.getSelfContrastColor(this); +} diff --git a/lib/shared/widgets.dart b/lib/shared/widgets.dart index 6c68e385..1622e8b2 100644 --- a/lib/shared/widgets.dart +++ b/lib/shared/widgets.dart @@ -1,4 +1,6 @@ /// [Base] +library; + export 'widgets/page.dart'; export 'widgets/component.dart'; export 'widgets/screen.dart'; diff --git a/lib/shared/widgets/enhanced_carousel_view.dart b/lib/shared/widgets/enhanced_carousel_view.dart index 0724ef2d..6cb3a1b1 100644 --- a/lib/shared/widgets/enhanced_carousel_view.dart +++ b/lib/shared/widgets/enhanced_carousel_view.dart @@ -2,70 +2,106 @@ import 'package:flutter/material.dart'; import 'package:hub/flutter_flow/index.dart'; import 'package:hub/shared/utils.dart'; -class EnhancedCarouselView extends StatelessWidget { +class EnhancedCarouselView extends StatefulWidget { final Future> Function() dataProvider; final void Function(T, BuildContext) filter; - final Widget Function(T? item) itemBuilder; + final Widget Function(T? item, bool isSelected) itemBuilder; + final bool showIndicator; const EnhancedCarouselView({ super.key, required this.dataProvider, required this.filter, required this.itemBuilder, + this.showIndicator = false, }); + @override + _EnhancedCarouselViewState createState() => + _EnhancedCarouselViewState(); +} + +class _EnhancedCarouselViewState extends State> { + T? selectedCategory; + @override Widget build(BuildContext context) { final theme = FlutterFlowTheme.of(context); final backgroundColor = theme.primary; final overlayColor = WidgetStateProperty.all(Colors.transparent); - return Column( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.end, - mainAxisAlignment: MainAxisAlignment.end, - spacing: 20, + return Stack( children: [ - Padding( - padding: const EdgeInsets.fromLTRB(15, 0, 50, 0), - child: Text( - FFLocalizations.of(context).getVariableText( - ptText: 'Suas Categorias', - enText: 'Your Categories', - ), - style: TextStyle( - color: FlutterFlowTheme.of(context).primaryText, - fontSize: LimitedFontSizeUtil.getHeaderFontSize(context), - ), - ), - ), - FutureBuilder>( - future: dataProvider(), - builder: (context, snapshot) { - if (!snapshot.hasData) return SizedBox(); - final items = - snapshot.data!.map((item) => itemBuilder(item)).toList(); - return SizedBox( - height: 130, // Set a specific height - child: CarouselView( - itemExtent: 140, - enableSplash: true, - itemSnapping: true, - controller: CarouselController(initialItem: 1), - backgroundColor: backgroundColor, - overlayColor: overlayColor, - padding: EdgeInsets.zero, - elevation: 0, - reverse: true, - shrinkExtent: 10, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - ), - onTap: (index) => filter(snapshot.data![index] as T, context), - children: items, + Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(15, 0, 50, 0), + child: Text( + FFLocalizations.of(context).getVariableText( + ptText: 'Suas Categorias', + enText: 'Your Categories', ), - ); - }), + style: TextStyle( + color: FlutterFlowTheme.of(context).primaryText, + fontSize: LimitedFontSizeUtil.getHeaderFontSize(context), + ), + ), + ), + FutureBuilder>( + future: widget.dataProvider(), + builder: (context, snapshot) { + if (!snapshot.hasData) return SizedBox(); + final items = snapshot.data! + .map((item) => + widget.itemBuilder(item, item == selectedCategory)) + .toList(); + return SizedBox( + height: 130, // Set a specific height + child: CarouselView( + itemExtent: 140, + enableSplash: true, + itemSnapping: true, + controller: CarouselController(initialItem: 1), + backgroundColor: backgroundColor, + overlayColor: overlayColor, + padding: EdgeInsets.zero, + elevation: 0, + reverse: true, + shrinkExtent: 10, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + onTap: (index) { + setState(() { + if (selectedCategory == snapshot.data![index]) + selectedCategory = null; + else + selectedCategory = snapshot.data![index] as T; + }); + widget.filter(snapshot.data![index] as T, context); + }, + children: items, + ), + ); + }, + ), + ], + ), + if (widget.showIndicator) + Positioned( + left: 0, + top: 50, + child: Icon(Icons.arrow_left, size: 30, color: Colors.grey), + ), + if (widget.showIndicator) + Positioned( + right: 0, + top: 50, + child: Icon(Icons.arrow_right, size: 30, color: Colors.grey), + ), ], ); } diff --git a/lib/shared/widgets/enhanced_list_view.dart b/lib/shared/widgets/enhanced_list_view.dart index a2de85e5..1f370b65 100644 --- a/lib/shared/widgets/enhanced_list_view.dart +++ b/lib/shared/widgets/enhanced_list_view.dart @@ -1,6 +1,7 @@ import 'dart:developer'; import 'package:flutter/material.dart'; +import 'package:hub/flutter_flow/index.dart'; import 'package:rx_bloc/rx_bloc.dart'; import 'package:rx_bloc_list/rx_bloc_list.dart'; import 'package:rxdart/rxdart.dart'; @@ -211,6 +212,10 @@ class EnhancedListViewState @override Widget build(BuildContext context) { + final String defaultMessage = FFLocalizations.of(context).getVariableText( + ptText: 'Nenhum item encontrado', + enText: 'No items found', + ); final header = StreamBuilder>( stream: bloc.states.headerItems.cast>(), builder: (context, headerSnapshot) { @@ -250,7 +255,9 @@ class EnhancedListViewState } else if (bodySnapshot.hasError) { return EnhancedErrorWidget(error: bodySnapshot.error); } else if (!bodySnapshot.hasData || bodySnapshot.data!.isEmpty) { - return const SizedBox.shrink(); + return Center( + child: Text(defaultMessage), + ); } else { return ListView.builder( itemCount: bodySnapshot.data?.length ?? 0, diff --git a/lib/shared/widgets/read_view.dart b/lib/shared/widgets/read_view.dart index 6df9c6cb..8be31b52 100644 --- a/lib/shared/widgets/read_view.dart +++ b/lib/shared/widgets/read_view.dart @@ -1,7 +1,7 @@ import 'dart:developer'; import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:hub/features/backend/index.dart'; +// Removed unnecessary import import 'package:hub/flutter_flow/index.dart'; import 'package:hub/shared/utils/dialog_util.dart'; import 'package:hub/shared/widgets.dart'; @@ -32,9 +32,11 @@ abstract interface class Viewer extends StatelessComponent { class ReadView extends StatefulWidget { final String url; final String title; + final VoidCallback onError; const ReadView({ super.key, + required this.onError, required this.url, required this.title, }); @@ -50,8 +52,12 @@ class ReadViewState extends State { final Future document = DocumentType.openFile(file.path); return ReadViewController(document: document); } catch (e) { - logError('Erro ao baixar o PDF', e); - return Future.error(e); + final message = FFLocalizations.of(context).getVariableText( + ptText: 'Erro ao baixar o PDF', + enText: 'Error downloading PDF', + ); + + return Future.error(message); } } @@ -68,14 +74,19 @@ class ReadViewState extends State { throw Exception('Falha ao baixar o PDF'); } } catch (e) { - logError('Erro ao baixar o PDF', e); + final message = FFLocalizations.of(context).getVariableText( + ptText: 'Erro ao baixar o PDF', + enText: 'Error downloading PDF', + ); + await throwError(message, e); rethrow; } } - void logError(String message, dynamic error) { + Future throwError(String message, dynamic error) async { log('$message: $error'); - DialogUtil.error(context, message); + await DialogUtil.error(context, message) + .whenComplete(() => widget.onError()); } @override @@ -104,7 +115,7 @@ class ReadViewState extends State { ); } - void onShare() async { + Future onShare() async { try { final Uri uri = Uri.parse(widget.url); final response = await http.get(uri); @@ -113,10 +124,15 @@ class ReadViewState extends State { name: '${widget.title}.pdf', mimeType: 'application/pdf'); await Share.shareXFiles([xfile], text: 'Confira este PDF!'); } else { - throw Exception('Erro ao compartilhar o arquivo: ${response.statusCode}'); + throw Exception( + 'Erro ao compartilhar o arquivo: ${response.statusCode}'); } } catch (e) { - logError('Erro ao compartilhar o arquivo', e); + final message = FFLocalizations.of(context).getVariableText( + ptText: 'Erro ao compartilhar o arquivo', + enText: 'Error sharing file', + ); + await throwError(message, e); } } From 203632bbdf2d01856dfc11a69f9954307b08e707 Mon Sep 17 00:00:00 2001 From: jantunesmessias Date: Wed, 26 Feb 2025 14:41:28 -0300 Subject: [PATCH 32/32] fix --- lib/features/documents/documents.dart | 31 +++++++++---------- .../widgets/enhanced_carousel_view.dart | 28 +++++++++++++---- lib/shared/widgets/read_view.dart | 4 ++- 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/lib/features/documents/documents.dart b/lib/features/documents/documents.dart index 1b84a344..1373641d 100644 --- a/lib/features/documents/documents.dart +++ b/lib/features/documents/documents.dart @@ -131,7 +131,7 @@ class DocumentManagerScreen extends StatelessScreen { children: [ Expanded( child: EnhancedListView( - key: model.vehicleScreenManager, + key: model.enhancedListViewKey, controller: controller, repository: repository, ), @@ -240,25 +240,25 @@ class DocumentModel extends FlutterFlowModel { DocumentModel(this.bloc); late EnhancedListViewKey - vehicleScreenManager; - late DocumentKey vehicleScreenViewer; + enhancedListViewKey; + late EnhancedCarouselViewKey carouselViewKey; - late bool categoryIsSelected; + late DocumentKey vehicleScreenViewer; /// ------------ @override void initState(BuildContext context) { - vehicleScreenManager = + enhancedListViewKey = EnhancedListViewKey(); + carouselViewKey = EnhancedCarouselViewKey(); vehicleScreenViewer = DocumentKey(); - - categoryIsSelected = false; } @override void dispose() { - vehicleScreenManager.currentState?.dispose(); + enhancedListViewKey.currentState?.dispose(); + carouselViewKey.currentState?.dispose(); vehicleScreenViewer.currentState?.dispose(); } @@ -338,6 +338,7 @@ class DocumentModel extends FlutterFlowModel { } return EnhancedCarouselView( + key: carouselViewKey, dataProvider: fetchData, itemBuilder: categoryItemBuilder, filter: filterByCategory, @@ -454,18 +455,14 @@ class DocumentModel extends FlutterFlowModel { /// [Filter] void filterBySearchBar(T query, BuildContext context) { - final key = vehicleScreenManager.currentState; - return key?.filterBodyItems(query); + final enhancedListViewState = enhancedListViewKey.currentState; + return enhancedListViewState?.filterBodyItems(query); } - void filterByCategory(T query, BuildContext context) { - final key = vehicleScreenManager.currentState; + void filterByCategory(T? query, BuildContext context) { + final enhancedListViewState = enhancedListViewKey.currentState; - categoryIsSelected - ? key?.filterBodyItems(null) - : key?.filterBodyItems(query); - - categoryIsSelected = !categoryIsSelected; + enhancedListViewState?.filterBodyItems(query); } /// [Exception] diff --git a/lib/shared/widgets/enhanced_carousel_view.dart b/lib/shared/widgets/enhanced_carousel_view.dart index 6cb3a1b1..c031172b 100644 --- a/lib/shared/widgets/enhanced_carousel_view.dart +++ b/lib/shared/widgets/enhanced_carousel_view.dart @@ -1,10 +1,14 @@ +import 'dart:developer'; + import 'package:flutter/material.dart'; import 'package:hub/flutter_flow/index.dart'; import 'package:hub/shared/utils.dart'; +typedef EnhancedCarouselViewKey = GlobalKey<_EnhancedCarouselViewState>; + class EnhancedCarouselView extends StatefulWidget { final Future> Function() dataProvider; - final void Function(T, BuildContext) filter; + final void Function(T?, BuildContext) filter; final Widget Function(T? item, bool isSelected) itemBuilder; final bool showIndicator; @@ -24,6 +28,10 @@ class EnhancedCarouselView extends StatefulWidget { class _EnhancedCarouselViewState extends State> { T? selectedCategory; + bool itemIsSelected(T item) { + return selectedCategory == item; + } + @override Widget build(BuildContext context) { final theme = FlutterFlowTheme.of(context); @@ -56,7 +64,7 @@ class _EnhancedCarouselViewState extends State> { if (!snapshot.hasData) return SizedBox(); final items = snapshot.data! .map((item) => - widget.itemBuilder(item, item == selectedCategory)) + widget.itemBuilder(item, itemIsSelected(item as T))) .toList(); return SizedBox( height: 130, // Set a specific height @@ -74,14 +82,22 @@ class _EnhancedCarouselViewState extends State> { shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), - onTap: (index) { + onTap: (index) async { + log('Selected: ${snapshot.data![index]}'); + log('Selected Category: $selectedCategory'); + final bool isSame = + itemIsSelected(snapshot.data![index]!); setState(() { - if (selectedCategory == snapshot.data![index]) + if (isSame) { selectedCategory = null; - else + } else { selectedCategory = snapshot.data![index] as T; + } }); - widget.filter(snapshot.data![index] as T, context); + if (isSame) + widget.filter(null, context); + else + widget.filter(snapshot.data![index] as T, context); }, children: items, ), diff --git a/lib/shared/widgets/read_view.dart b/lib/shared/widgets/read_view.dart index 8be31b52..4cd5a806 100644 --- a/lib/shared/widgets/read_view.dart +++ b/lib/shared/widgets/read_view.dart @@ -122,7 +122,9 @@ class ReadViewState extends State { if (response.statusCode == 200) { final XFile xfile = XFile.fromData(response.bodyBytes, name: '${widget.title}.pdf', mimeType: 'application/pdf'); - await Share.shareXFiles([xfile], text: 'Confira este PDF!'); + await Share.shareXFiles([xfile], + text: 'Confira este PDF!', + fileNameOverrides: ['${widget.title}.pdf']); } else { throw Exception( 'Erro ao compartilhar o arquivo: ${response.statusCode}');