This commit is contained in:
jantunesmessias 2025-02-12 17:49:55 -03:00
parent 56f16d2934
commit 1dc611004a
22 changed files with 698 additions and 373 deletions

28
.vscode/launch.json vendored Normal file
View File

@ -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"
}
]
}

View File

@ -101,6 +101,7 @@ class _CardItemTemplateComponentWidgetState
}
Widget _generateImage() {
print('img: ${widget.imagePath ?? ''}');
// CachedNetworkImage.evictFromCache(widget.imagePath ?? '');
return ClipRRect(
borderRadius: BorderRadius.circular(20),

View File

@ -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<ApiCallResponse> call() async {
final String baseUrl = FreAccessWSGlobal.getBaseUrl();
final String devUUID =
@ -113,7 +118,11 @@ class GetCategories extends Endpoint {
}
class GetDocuments extends Endpoint {
Future<ApiCallResponse> call(final String page) async {
@override
Future<ApiCallResponse> 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<ApiCallResponse> 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<ApiCallResponse> 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<ApiCallResponse> 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<ApiCallResponse> 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<ApiCallResponse> call() async {
final String baseUrl = FreAccessWSGlobal.getBaseUrl();
final String devUUID =
@ -327,6 +346,7 @@ class GetLicense extends Endpoint {
}
class UnregisterDevice extends Endpoint {
@override
Future<ApiCallResponse> call() async {
final String baseUrl = FreAccessWSGlobal.getBaseUrl();
final String devUUID =
@ -357,6 +377,7 @@ class UnregisterDevice extends Endpoint {
}
class DeletePet extends Endpoint {
@override
Future<ApiCallResponse> 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<ApiCallResponse> call({
final int? petID,
final String? image,
@ -452,6 +474,7 @@ class UpdatePet extends Endpoint {
}
class GetPets extends Endpoint {
@override
Future<ApiCallResponse> call({
final int? page,
final int? pageSize,
@ -492,6 +515,7 @@ class GetPets extends Endpoint {
}
class GetPetPhoto extends Endpoint {
@override
Future<ApiCallResponse> call({final int? petId}) async {
final String baseUrl = FreAccessWSGlobal.getBaseUrl();
@ -528,6 +552,7 @@ class GetPetPhoto extends Endpoint {
}
class RegisterPet extends Endpoint {
@override
Future<ApiCallResponse> call({
final String? image,
final String? name,
@ -585,6 +610,7 @@ class RegisterPet extends Endpoint {
}
class BuscaEnconcomendas extends Endpoint {
@override
Future<ApiCallResponse> call({
final String? page,
final String? pageSize,
@ -629,6 +655,7 @@ class BuscaEnconcomendas extends Endpoint {
}
class CancelaVisita extends Endpoint {
@override
Future<ApiCallResponse> call({
final int? idDestino,
final int? idVisita,
@ -674,6 +701,7 @@ class CancelaVisita extends Endpoint {
}
class DeleteAccount extends Endpoint {
@override
Future<ApiCallResponse> 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<ApiCallResponse> call({
final String? newSenhaPanico,
}) async {
@ -743,6 +772,7 @@ class ChangePanic extends Endpoint {
}
class ChangePass extends Endpoint {
@override
Future<ApiCallResponse> call({
final String? newSenha,
}) async {
@ -782,6 +812,7 @@ class ChangePass extends Endpoint {
}
class RespondeVinculo extends Endpoint {
@override
Future<ApiCallResponse> call({
final String? tarefa,
}) async {
@ -819,6 +850,7 @@ class RespondeVinculo extends Endpoint {
}
class ChangeNotifica extends Endpoint {
@override
Future<ApiCallResponse> call({
final String? notifica,
}) async {
@ -858,6 +890,7 @@ class ChangeNotifica extends Endpoint {
}
class UpdateIDE extends Endpoint {
@override
Future<ApiCallResponse> call() async {
final String baseUrl = FreAccessWSGlobal.getBaseUrl();
@ -897,6 +930,7 @@ class UpdateIDE extends Endpoint {
}
class UpdToken extends Endpoint {
@override
Future<ApiCallResponse> call() async {
final String baseUrl = FreAccessWSGlobal.getBaseUrl();
@ -930,6 +964,7 @@ class UpdToken extends Endpoint {
}
class LoginCall extends Endpoint {
@override
Future<ApiCallResponse> call() async {
final String baseUrl = FreAccessWSGlobal.getBaseUrl();
final String devUUID =
@ -980,6 +1015,7 @@ class LoginCall extends Endpoint {
}
class RegisterCall extends Endpoint {
@override
Future<ApiCallResponse> call({
required final String name,
required final String email,
@ -1019,6 +1055,7 @@ class RegisterCall extends Endpoint {
}
class ChangePasswordCall extends Endpoint {
@override
Future<ApiCallResponse> call({
required final String email,
required final String token,
@ -1059,6 +1096,7 @@ class ChangePasswordCall extends Endpoint {
}
class ForgotPasswordCall extends Endpoint {
@override
Future<ApiCallResponse> call({
final String? email,
}) async {
@ -1086,6 +1124,7 @@ class ForgotPasswordCall extends Endpoint {
}
class GetLocalsCall extends Endpoint {
@override
Future<ApiCallResponse> call() async {
final String baseUrl = FreAccessWSGlobal.getBaseUrl();
@ -1125,6 +1164,7 @@ class GetLocalsCall extends Endpoint {
}
class PostScheduleVisitorCall extends Endpoint {
@override
Future<ApiCallResponse> call({
required final String documento,
required final String nome,
@ -1179,6 +1219,7 @@ class PostScheduleVisitorCall extends Endpoint {
}
class PostScheduleVisitCall extends Endpoint {
@override
Future<ApiCallResponse> call({
final String? devDesc,
final String? idVisitante,
@ -1241,6 +1282,7 @@ class PostScheduleVisitCall extends Endpoint {
}
class GetScheduleVisitCall extends Endpoint {
@override
Future<ApiCallResponse> call({
final int? pageSize,
final int? pageNumber,
@ -1522,6 +1564,7 @@ class GetScheduleVisitCall extends Endpoint {
}
class GetDadosCall extends Endpoint {
@override
Future<ApiCallResponse> call() async {
final String baseUrl = FreAccessWSGlobal.getBaseUrl();
@ -1768,6 +1811,7 @@ class GetDadosCall extends Endpoint {
}
class GetVisitorByDocCall extends Endpoint {
@override
Future<ApiCallResponse> call({
final String? documento,
}) async {
@ -1824,6 +1868,7 @@ class GetVisitorByDocCall extends Endpoint {
}
class GetFotoVisitanteCall extends Endpoint {
@override
Future<ApiCallResponse> call({
final String? documento,
final String? tipo,
@ -1864,6 +1909,7 @@ class GetFotoVisitanteCall extends Endpoint {
}
class PostProvVisitSchedulingCall extends Endpoint {
@override
Future<ApiCallResponse> call({
final String? data,
final String? motivo,
@ -1918,6 +1964,7 @@ class PostProvVisitSchedulingCall extends Endpoint {
}
class GetVisitsCall extends Endpoint {
@override
Future<ApiCallResponse> call({
final int? pageSize,
final int? pageNumber,
@ -2185,6 +2232,7 @@ class GetVisitsCall extends Endpoint {
}
class DeleteVisitCall extends Endpoint {
@override
Future<ApiCallResponse> call({
final String? idVisita,
}) async {
@ -2233,6 +2281,7 @@ class DeleteVisitCall extends Endpoint {
}
class GetPessoasLocalCall extends Endpoint {
@override
Future<ApiCallResponse> call() async {
final String baseUrl = FreAccessWSGlobal.getBaseUrl();
@ -2298,6 +2347,7 @@ class GetPessoasLocalCall extends Endpoint {
}
class RespondeSolicitacaoCall extends Endpoint {
@override
Future<ApiCallResponse> call({
final String? referencia,
final String? tarefa,
@ -2352,6 +2402,7 @@ class RespondeSolicitacaoCall extends Endpoint {
}
class GetAccessCall extends Endpoint {
@override
Future<ApiCallResponse> call({
final String? pageSize,
final String? pageNumber,
@ -2606,6 +2657,7 @@ class GetAccessCall extends Endpoint {
}
class GetLiberationsCall extends Endpoint {
@override
Stream<ApiCallResponse> call() {
final String baseUrl = FreAccessWSGlobal.getBaseUrl();
final StreamController<ApiCallResponse> controller = StreamController();
@ -2799,6 +2851,7 @@ class GetLiberationsCall extends Endpoint {
}
class GetMessagesCall extends Endpoint {
@override
Future<ApiCallResponse> call({
final String? pageSize,
final String? pageNumber,

View File

@ -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<String, dynamic> extra = <String, dynamic>{
'document': document,
kTransitionInfoKey: const TransitionInfo(
hasTransition: true,
transitionType: PageTransitionType.rightToLeft,
alignment: Alignment.bottomCenter,
),
};
Future<Object?> 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<Color>(primaryColor),
overlayColor: WidgetStateProperty.all<Color>(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,
),

View File

@ -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<Document> documents;
final List<Category> categories;
const DocumentManagerScreen({
super.key,
required this.model,
});
@override
Widget build(BuildContext context) {
final GlobalKey<LocalSearchViewState> _listViewKey =
GlobalKey<LocalSearchViewState>();
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<Document> filterByString(String query) {
return documents.where((documents) => filter(documents, query)).toList();
}
final SizedBox space = SizedBox(height: 30);
return Column(
children: [
Expanded(
child: RemoteSearchListView<dynamic>(
key: _listViewKey,
header: header,
onSearch: filterByString,
list: documents,
itemBuilder: itemBuilder,
filter: filter,
title: '',
// fetchItems: (String ) { },,
child: RemoteSearchView<Document>(
key: model.searchKey,
pagingController: model._pagingController,
headerBuilder: model.listHeaderBuilder,
bodyBuilder: model.listBodyBuilder,
dataProvider: model.generateDocuments,
onFetchError: model.onFetchError,
),
),
] //

View File

@ -1 +1,156 @@
part of 'index.dart';
class DocumentPageModel extends FlutterFlowModel<DocumentPage> {
@override
void dispose() {}
@override
void initState(BuildContext context) {}
final SearchKey searchKey = SearchKey();
final DocumentKey docKey = DocumentKey();
final PagingController<int, Document> _pagingController =
PagingController<int, Document>(firstPageKey: 1);
int count = 0;
final dynamic page = 1;
Query query = Document.fromDesc('');
late Document currentDocument;
bool isCategorySelected = false;
late Category currentCategory;
List<Document?> documents = [];
List<Category?> categories = [];
/// [listBodyBuilder]
Widget listBodyBuilder<T>(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<Category>(
categories: categories,
filter: filterByCategory,
),
],
);
/// [generateDocuments]
Future<(bool, List<Document?>)> generateDocuments(
pageKey, Query query) async {
final List<Document?> 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<dynamic> list = newItems.jsonBody['value']['list'];
late final List<Document> 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<List<Category?>> generateCategories(List<Document?> documents) async {
final List<Category?> 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<dynamic>;
late final List<Category> cats = [];
for (var item in list) {
final String color = item['color'];
final String title = item['description'];
final int id = item['id'];
final 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);
}
}

View File

@ -1,69 +1,54 @@
part of 'index.dart';
List<Document> generateDocuments(int count) {
String str() => randomString(8, 8, true, true, true);
Color color() => randomColor();
typedef DocumentKey = GlobalKey<FREDocumentPageState>;
return List<Document>.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<Category> generateCategories(List<Document> documents) {
final Map<String, Category> 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<FREDocumentPage> createState() => _FREDocumentPageState();
State<DocumentPage> createState() => FREDocumentPageState();
}
class _FREDocumentPageState extends PageState<FREDocumentPage> {
class FREDocumentPageState<T extends DocumentPage>
extends PageState<DocumentPage> {
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<Document> documents;
late List<Category> categories;
@override
void initState() {
super.initState();
documents = generateDocuments(20);
categories = generateCategories(documents);
}
Widget buildBody(BuildContext context) {
return DocumentManagerScreen(
documents: documents,
categories: categories,
return FutureBuilder<void>(
future: initAsync(),
builder: (context, snapshot) {
return DocumentManagerScreen(model: model);
},
);
// return DocumentViewScreen(document: documents.first);
}
Future<void> 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');
}
}

View File

@ -13,33 +13,45 @@ class DocumentViewScreen extends StatefulScreen {
}
class _DocumentViewScreenState extends State<DocumentViewScreen> {
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,
),
),
],

View File

@ -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';

View File

@ -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);

View File

@ -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())

View File

@ -74,38 +74,6 @@ class _AppState extends State<App> {
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<LocalizationsDelegate<dynamic>>? localizationsDelegates =
const [
FFLocalizationsDelegate(),
@ -201,6 +169,39 @@ class _AppState extends State<App> {
@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<App> {
localizationsDelegates: localizationsDelegates,
locale: _locale,
supportedLocales: supportedLocales,
theme: _theme,
darkTheme: _darkTheme,
theme: lightTheme,
darkTheme: darkTheme,
themeMode: _themeMode,
routerConfig: _router,
);

View File

@ -163,8 +163,10 @@ class PetsPageModel extends FlutterFlowModel<PetsPageWidget> {
// 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<PetsPageWidget> {
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,

View File

@ -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));
}
}

View File

@ -1,2 +1,3 @@
export 'status_mixin.dart';
export 'switcher_mixin.dart';
export 'pegeable_mixin.dart';
export 'status_mixin.dart';
export 'switcher_mixin.dart';

View File

@ -7,30 +7,33 @@ extension PagedListViewExtension<PageKeyType, ItemType>
on PagedSliverList<PageKeyType, ItemType> {}
mixin Pageable<T extends StatefulWidget> on State<T> {
Expanded buildPaginatedListView<X, Y>(
Expanded buildPaginatedListView<PageKeyType, ItemType>(
String noDataFound,
PagingController<X, Y> pg,
Widget Function(BuildContext, Y, int) itemBuilder) {
PagingController<PageKeyType, ItemType> 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<X, Y>(
child: PagedListView<PageKeyType, ItemType>(
pagingController: pg,
builderDelegate: PagedChildBuilderDelegate<Y>(
builderDelegate: PagedChildBuilderDelegate<ItemType>(
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<T extends StatefulWidget> on State<T> {
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(

View File

@ -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) {

View File

@ -1,46 +1,28 @@
part of '../widgets.dart';
class CategoryCarousel extends StatelessWidget {
final List<Category> categories;
final void Function(Category) onSelect;
final void Function(Category) onUnselect;
class CategoryCarousel<T> extends StatelessWidget {
final List<T?> 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(

View File

@ -1,7 +1,12 @@
part of '../widgets.dart';
typedef SearchKey = GlobalKey<RemoteSearchViewState>;
typedef Query<X extends DocumentEntity> = X?;
/// -----------------------------------------------
/// [SearchView]
/// -----------------------------------------------
class SearchView<T> extends StatefulComponent {
const SearchView({super.key});
@ -17,6 +22,10 @@ class _SearchViewState<T> extends State<SearchView> {
}
}
/// -----------------------------------------------
/// [LocalSearchView]
/// -----------------------------------------------
class LocalSearchView<T> extends SearchView<T> {
final List<T> list;
final Widget Function(T) itemBuilder;
@ -77,109 +86,104 @@ class LocalSearchViewState<T> extends State<LocalSearchView<T>> {
),
),
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<T> extends SearchView<T> {
final List<T> list;
final String title;
final Widget Function(T) itemBuilder;
// final Future<List<T>> Function(String) fetchItems;
final bool Function(T, String) filter;
final Widget header;
final List<T> Function(String)? onSearch;
/// -----------------------------------------------
/// [RemoteSearchView]
/// -----------------------------------------------
RemoteSearchListView({
class RemoteSearchView<T> extends SearchView<T> {
final Widget Function(BuildContext, T, int) bodyBuilder;
Widget Function(BuildContext) headerBuilder;
final PagingController<int, T> pagingController;
final Future<(bool, List<T?>)> 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<T> Function(String)? onSearch,
Widget? header,
}) : header = header ?? const SizedBox.shrink(),
onSearch = onSearch ??
((String query) =>
list.where((documents) => filter(documents, query)).toList()),
super(key: key);
required this.bodyBuilder,
required this.headerBuilder,
required this.pagingController,
required this.dataProvider,
required this.onFetchError,
}) : super(key: key);
@override
_RemoteSearchViewState<T> createState() => _RemoteSearchViewState<T>();
RemoteSearchViewState<T> createState() => RemoteSearchViewState<T>();
}
class _RemoteSearchViewState<T> extends State<RemoteSearchListView<T>>
class RemoteSearchViewState<T> extends State<RemoteSearchView<T>>
with Pageable {
TextEditingController editingController = TextEditingController();
late List<T> filteredItems;
bool isLoading = false;
final apiCall = FreAccessWSGlobal.getDocuments;
int count = 0;
final PagingController<int, dynamic> _pagingController =
PagingController<int, dynamic>(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<dynamic> docs =
(newItems.jsonBody['value']['list'] as List<dynamic>?) ?? [];
_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<T> extends State<RemoteSearchListView<T>>
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: <Widget>[
buildPaginatedListView<int, T>(
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<int, dynamic>(
noDataFound,
_pagingController,
(BuildContext context, dynamic item, int index) =>
widget.itemBuilder(item),
),
],
);
}

View File

@ -1,9 +1,12 @@
part of '../widgets.dart';
typedef PDFViewerState = GlobalKey<SfPdfViewerState>;
typedef PDFViewerKey = GlobalKey<SfPdfViewerState>;
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,
);
}
}

View File

@ -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';

View File

@ -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