correções do pr

This commit is contained in:
jantunesmessias 2025-02-25 17:13:09 -03:00
parent aa1bd55818
commit a7f1ea418b
9 changed files with 237 additions and 121 deletions

View File

@ -1,6 +1,5 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:core'; import 'dart:core';
import 'dart:developer';
import 'dart:io'; import 'dart:io';
import 'dart:typed_data'; import 'dart:typed_data';

View File

@ -164,13 +164,14 @@ class _DocumentViewerScreenState extends ScreenState<DocumentViewerScreen> {
super.initState(); super.initState();
} }
backAction() => widget.bloc.events.unselectDocument();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final String title = widget.doc.$1.description; final String title = widget.doc.$1.description;
final theme = FlutterFlowTheme.of(context); final theme = FlutterFlowTheme.of(context);
final locale = FFLocalizations.of(context); final locale = FFLocalizations.of(context);
backAction() => widget.bloc.events.unselectDocument();
infoAction() => DetailsComponentWidget( infoAction() => DetailsComponentWidget(
buttons: [], buttons: [],
statusHashMap: [ statusHashMap: [
@ -225,6 +226,7 @@ class _DocumentViewerScreenState extends ScreenState<DocumentViewerScreen> {
return ReadView( return ReadView(
title: widget.doc.$1.description, title: widget.doc.$1.description,
url: widget.doc.$2.toString(), url: widget.doc.$2.toString(),
onError: backAction,
); );
} }
} }
@ -330,14 +332,16 @@ class DocumentModel extends FlutterFlowModel<DocumentPage> {
Widget itemFooterBuilder<T extends Category>( Widget itemFooterBuilder<T extends Category>(
Future<List<T?>> Function() fetchData) => Future<List<T?>> Function() fetchData) =>
Builder(builder: (context) { Builder(builder: (context) {
CategoryComponent categoryItemBuilder<T>(T? item) { CategoryComponent categoryItemBuilder<T>(T? item, bool isSelected) {
return CategoryComponent(category: item! as Category); return CategoryComponent(
category: item! as Category, isSelected: isSelected);
} }
return EnhancedCarouselView<T>( return EnhancedCarouselView<T>(
dataProvider: fetchData, dataProvider: fetchData,
itemBuilder: categoryItemBuilder, itemBuilder: categoryItemBuilder,
filter: filterByCategory<T>, filter: filterByCategory<T>,
showIndicator: true,
); );
}); });
@ -654,64 +658,71 @@ class DocumentComponent extends StatelessComponent {
final description = document.description; final description = document.description;
final title = document.category.title; final title = document.category.title;
const double size = 20; const double size = 20;
final date = ValidatorUtil.toLocalDateTime(
'yyyy-MM-dd',
document.updatedAt,
);
return InkWell( return Tooltip(
onTap: () => onPressed(document, context), message: description,
enableFeedback: true, child: InkWell(
overlayColor: WidgetStateProperty.all<Color>(primaryColor), onTap: () => onPressed(document, context),
borderRadius: BorderRadius.circular(10), enableFeedback: true,
child: SizedBox( overlayColor: WidgetStateProperty.all<Color>(primaryColor),
height: boxHeight, borderRadius: BorderRadius.circular(10),
child: Row( child: SizedBox(
mainAxisAlignment: MainAxisAlignment.spaceBetween, height: boxHeight,
spacing: size, child: Row(
children: [ mainAxisAlignment: MainAxisAlignment.spaceBetween,
// const SizedBox(width: 10), spacing: size,
Icon(icon, color: color), children: [
// const SizedBox(width: 10),
Icon(icon, color: color),
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Tooltip( Tooltip(
message: description, message: description,
child: AutoText( child: AutoText(
description, description,
style: textStyleMajor, style: textStyleMajor,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
),
), ),
), Tooltip(
AutoText( message: date,
ValidatorUtil.toLocalDateTime( child: AutoText(
'yyyy-MM-dd', date,
document.updatedAt, style: textStyleMinor,
overflow: TextOverflow.ellipsis,
),
), ),
style: textStyleMinor, ],
overflow: TextOverflow.ellipsis, ),
),
],
), ),
), Expanded(
Expanded( child: Column(
child: Column( mainAxisAlignment: MainAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end, children: [
children: [ _buildTooltip(title, color, context, constraints),
_buildTooltip(title, color, context, constraints), ],
], ),
), ),
), // const SizedBox(width: 10),
// const SizedBox(width: 10), Center(
Center( child: Icon(
child: Icon( Icons.arrow_right,
Icons.arrow_right, color: primaryText,
color: primaryText, ),
), ),
), ] //
] // .addToStart(space)
.addToStart(space) .addToEnd(space),
.addToEnd(space), ),
), ),
), ),
); );
@ -732,16 +743,19 @@ class DocumentComponent extends StatelessComponent {
class CategoryComponent extends StatelessComponent { class CategoryComponent extends StatelessComponent {
final Category category; final Category category;
final bool isSelected;
const CategoryComponent({ const CategoryComponent({
super.key, super.key,
required this.category, required this.category,
this.isSelected = false,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final backgroundTheme = FlutterFlowTheme.of(context).primaryBackground; final backgroundTheme = FlutterFlowTheme.of(context).primaryBackground;
final textTheme = FlutterFlowTheme.of(context).primaryText; final textTheme = FlutterFlowTheme.of(context).primaryText;
final color = isSelected ? category.color.highlight : category.color;
return ColoredBox( return ColoredBox(
color: backgroundTheme, color: backgroundTheme,
child: Padding( child: Padding(
@ -751,7 +765,7 @@ class CategoryComponent extends StatelessComponent {
Container( Container(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
decoration: BoxDecoration( decoration: BoxDecoration(
color: category.color, color: color,
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
child: Icon( child: Icon(

View File

@ -34,20 +34,13 @@ class LocalsRemoteDataSourceImpl implements LocalsRemoteDataSource {
try { try {
final GetLocalsCall callback = FreAccessWSGlobal.getLocalsCall; final GetLocalsCall callback = FreAccessWSGlobal.getLocalsCall;
var response = await callback.call(); 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) { if (isError == true) {
LocalUtil.handleError(context, response.jsonBody['error_msg']); LocalUtil.handleError(context, response.jsonBody['error_msg']);
return; 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<dynamic> locals = response.jsonBody['locais'] ?? []; final List<dynamic> locals = response.jsonBody['locais'] ?? [];
final bool isEmpty = locals.isEmpty; final bool isEmpty = locals.isEmpty;

View File

@ -12,3 +12,4 @@ export 'utils/string_util.dart';
export 'utils/text_util.dart'; export 'utils/text_util.dart';
export 'utils/validator_util.dart'; export 'utils/validator_util.dart';
export 'utils/webview_util.dart'; export 'utils/webview_util.dart';
export 'utils/color_util.dart';

View File

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

View File

@ -1,4 +1,6 @@
/// [Base] /// [Base]
library;
export 'widgets/page.dart'; export 'widgets/page.dart';
export 'widgets/component.dart'; export 'widgets/component.dart';
export 'widgets/screen.dart'; export 'widgets/screen.dart';

View File

@ -2,70 +2,106 @@ import 'package:flutter/material.dart';
import 'package:hub/flutter_flow/index.dart'; import 'package:hub/flutter_flow/index.dart';
import 'package:hub/shared/utils.dart'; import 'package:hub/shared/utils.dart';
class EnhancedCarouselView<T> extends StatelessWidget { class EnhancedCarouselView<T> extends StatefulWidget {
final Future<List<T?>> Function() dataProvider; final Future<List<T?>> Function() dataProvider;
final void Function(T, BuildContext) filter; final void Function(T, BuildContext) filter;
final Widget Function<T>(T? item) itemBuilder; final Widget Function<T>(T? item, bool isSelected) itemBuilder;
final bool showIndicator;
const EnhancedCarouselView({ const EnhancedCarouselView({
super.key, super.key,
required this.dataProvider, required this.dataProvider,
required this.filter, required this.filter,
required this.itemBuilder, required this.itemBuilder,
this.showIndicator = false,
}); });
@override
_EnhancedCarouselViewState<T> createState() =>
_EnhancedCarouselViewState<T>();
}
class _EnhancedCarouselViewState<T> extends State<EnhancedCarouselView<T>> {
T? selectedCategory;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = FlutterFlowTheme.of(context); final theme = FlutterFlowTheme.of(context);
final backgroundColor = theme.primary; final backgroundColor = theme.primary;
final overlayColor = WidgetStateProperty.all(Colors.transparent); final overlayColor = WidgetStateProperty.all(Colors.transparent);
return Column( return Stack(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
spacing: 20,
children: [ children: [
Padding( Column(
padding: const EdgeInsets.fromLTRB(15, 0, 50, 0), mainAxisSize: MainAxisSize.max,
child: Text( crossAxisAlignment: CrossAxisAlignment.end,
FFLocalizations.of(context).getVariableText( mainAxisAlignment: MainAxisAlignment.end,
ptText: 'Suas Categorias', children: [
enText: 'Your Categories', Padding(
), padding: const EdgeInsets.fromLTRB(15, 0, 50, 0),
style: TextStyle( child: Text(
color: FlutterFlowTheme.of(context).primaryText, FFLocalizations.of(context).getVariableText(
fontSize: LimitedFontSizeUtil.getHeaderFontSize(context), ptText: 'Suas Categorias',
), enText: 'Your Categories',
),
),
FutureBuilder<List<T?>>(
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,
), ),
); style: TextStyle(
}), color: FlutterFlowTheme.of(context).primaryText,
fontSize: LimitedFontSizeUtil.getHeaderFontSize(context),
),
),
),
FutureBuilder<List<T?>>(
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),
),
], ],
); );
} }

View File

@ -1,6 +1,7 @@
import 'dart:developer'; import 'dart:developer';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hub/flutter_flow/index.dart';
import 'package:rx_bloc/rx_bloc.dart'; import 'package:rx_bloc/rx_bloc.dart';
import 'package:rx_bloc_list/rx_bloc_list.dart'; import 'package:rx_bloc_list/rx_bloc_list.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
@ -211,6 +212,10 @@ class EnhancedListViewState<ItemType, HeaderType, FooterType, QueryType>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final String defaultMessage = FFLocalizations.of(context).getVariableText(
ptText: 'Nenhum item encontrado',
enText: 'No items found',
);
final header = StreamBuilder<List<HeaderType>>( final header = StreamBuilder<List<HeaderType>>(
stream: bloc.states.headerItems.cast<List<HeaderType>>(), stream: bloc.states.headerItems.cast<List<HeaderType>>(),
builder: (context, headerSnapshot) { builder: (context, headerSnapshot) {
@ -250,7 +255,9 @@ class EnhancedListViewState<ItemType, HeaderType, FooterType, QueryType>
} else if (bodySnapshot.hasError) { } else if (bodySnapshot.hasError) {
return EnhancedErrorWidget(error: bodySnapshot.error); return EnhancedErrorWidget(error: bodySnapshot.error);
} else if (!bodySnapshot.hasData || bodySnapshot.data!.isEmpty) { } else if (!bodySnapshot.hasData || bodySnapshot.data!.isEmpty) {
return const SizedBox.shrink(); return Center(
child: Text(defaultMessage),
);
} else { } else {
return ListView.builder( return ListView.builder(
itemCount: bodySnapshot.data?.length ?? 0, itemCount: bodySnapshot.data?.length ?? 0,

View File

@ -1,7 +1,7 @@
import 'dart:developer'; import 'dart:developer';
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart'; 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/flutter_flow/index.dart';
import 'package:hub/shared/utils/dialog_util.dart'; import 'package:hub/shared/utils/dialog_util.dart';
import 'package:hub/shared/widgets.dart'; import 'package:hub/shared/widgets.dart';
@ -32,9 +32,11 @@ abstract interface class Viewer extends StatelessComponent {
class ReadView extends StatefulWidget { class ReadView extends StatefulWidget {
final String url; final String url;
final String title; final String title;
final VoidCallback onError;
const ReadView({ const ReadView({
super.key, super.key,
required this.onError,
required this.url, required this.url,
required this.title, required this.title,
}); });
@ -50,8 +52,12 @@ class ReadViewState extends State<ReadView> {
final Future<DocumentType> document = DocumentType.openFile(file.path); final Future<DocumentType> document = DocumentType.openFile(file.path);
return ReadViewController(document: document); return ReadViewController(document: document);
} catch (e) { } catch (e) {
logError('Erro ao baixar o PDF', e); final message = FFLocalizations.of(context).getVariableText(
return Future.error(e); ptText: 'Erro ao baixar o PDF',
enText: 'Error downloading PDF',
);
return Future.error(message);
} }
} }
@ -68,14 +74,19 @@ class ReadViewState extends State<ReadView> {
throw Exception('Falha ao baixar o PDF'); throw Exception('Falha ao baixar o PDF');
} }
} catch (e) { } 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; rethrow;
} }
} }
void logError(String message, dynamic error) { Future<void> throwError(String message, dynamic error) async {
log('$message: $error'); log('$message: $error');
DialogUtil.error(context, message); await DialogUtil.error(context, message)
.whenComplete(() => widget.onError());
} }
@override @override
@ -104,7 +115,7 @@ class ReadViewState extends State<ReadView> {
); );
} }
void onShare() async { Future<void> onShare() async {
try { try {
final Uri uri = Uri.parse(widget.url); final Uri uri = Uri.parse(widget.url);
final response = await http.get(uri); final response = await http.get(uri);
@ -113,10 +124,15 @@ class ReadViewState extends State<ReadView> {
name: '${widget.title}.pdf', mimeType: 'application/pdf'); name: '${widget.title}.pdf', mimeType: 'application/pdf');
await Share.shareXFiles([xfile], text: 'Confira este PDF!'); await Share.shareXFiles([xfile], text: 'Confira este PDF!');
} else { } else {
throw Exception('Erro ao compartilhar o arquivo: ${response.statusCode}'); throw Exception(
'Erro ao compartilhar o arquivo: ${response.statusCode}');
} }
} catch (e) { } 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);
} }
} }