correções do pr
This commit is contained in:
parent
aa1bd55818
commit
a7f1ea418b
|
@ -1,6 +1,5 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:core';
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
|
|
|
@ -164,13 +164,14 @@ class _DocumentViewerScreenState extends ScreenState<DocumentViewerScreen> {
|
|||
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<DocumentViewerScreen> {
|
|||
return ReadView(
|
||||
title: widget.doc.$1.description,
|
||||
url: widget.doc.$2.toString(),
|
||||
onError: backAction,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -330,14 +332,16 @@ class DocumentModel extends FlutterFlowModel<DocumentPage> {
|
|||
Widget itemFooterBuilder<T extends Category>(
|
||||
Future<List<T?>> Function() fetchData) =>
|
||||
Builder(builder: (context) {
|
||||
CategoryComponent categoryItemBuilder<T>(T? item) {
|
||||
return CategoryComponent(category: item! as Category);
|
||||
CategoryComponent categoryItemBuilder<T>(T? item, bool isSelected) {
|
||||
return CategoryComponent(
|
||||
category: item! as Category, isSelected: isSelected);
|
||||
}
|
||||
|
||||
return EnhancedCarouselView<T>(
|
||||
dataProvider: fetchData,
|
||||
itemBuilder: categoryItemBuilder,
|
||||
filter: filterByCategory<T>,
|
||||
showIndicator: true,
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -654,8 +658,14 @@ 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(
|
||||
return Tooltip(
|
||||
message: description,
|
||||
child: InkWell(
|
||||
onTap: () => onPressed(document, context),
|
||||
enableFeedback: true,
|
||||
overlayColor: WidgetStateProperty.all<Color>(primaryColor),
|
||||
|
@ -682,14 +692,14 @@ class DocumentComponent extends StatelessComponent {
|
|||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
AutoText(
|
||||
ValidatorUtil.toLocalDateTime(
|
||||
'yyyy-MM-dd',
|
||||
document.updatedAt,
|
||||
),
|
||||
Tooltip(
|
||||
message: date,
|
||||
child: AutoText(
|
||||
date,
|
||||
style: textStyleMinor,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -714,6 +724,7 @@ class DocumentComponent extends StatelessComponent {
|
|||
.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(
|
||||
|
|
|
@ -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<dynamic> locals = response.jsonBody['locais'] ?? [];
|
||||
final bool isEmpty = locals.isEmpty;
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
/// [Base]
|
||||
library;
|
||||
|
||||
export 'widgets/page.dart';
|
||||
export 'widgets/component.dart';
|
||||
export 'widgets/screen.dart';
|
||||
|
|
|
@ -2,29 +2,40 @@ import 'package:flutter/material.dart';
|
|||
import 'package:hub/flutter_flow/index.dart';
|
||||
import 'package:hub/shared/utils.dart';
|
||||
|
||||
class EnhancedCarouselView<T> extends StatelessWidget {
|
||||
class EnhancedCarouselView<T> extends StatefulWidget {
|
||||
final Future<List<T?>> Function() dataProvider;
|
||||
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({
|
||||
super.key,
|
||||
required this.dataProvider,
|
||||
required this.filter,
|
||||
required this.itemBuilder,
|
||||
this.showIndicator = false,
|
||||
});
|
||||
|
||||
@override
|
||||
_EnhancedCarouselViewState<T> createState() =>
|
||||
_EnhancedCarouselViewState<T>();
|
||||
}
|
||||
|
||||
class _EnhancedCarouselViewState<T> extends State<EnhancedCarouselView<T>> {
|
||||
T? selectedCategory;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = FlutterFlowTheme.of(context);
|
||||
final backgroundColor = theme.primary;
|
||||
final overlayColor = WidgetStateProperty.all(Colors.transparent);
|
||||
|
||||
return Column(
|
||||
return Stack(
|
||||
children: [
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
spacing: 20,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(15, 0, 50, 0),
|
||||
|
@ -40,11 +51,13 @@ class EnhancedCarouselView<T> extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
FutureBuilder<List<T?>>(
|
||||
future: dataProvider(),
|
||||
future: widget.dataProvider(),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) return SizedBox();
|
||||
final items =
|
||||
snapshot.data!.map((item) => itemBuilder(item)).toList();
|
||||
final items = snapshot.data!
|
||||
.map((item) =>
|
||||
widget.itemBuilder(item, item == selectedCategory))
|
||||
.toList();
|
||||
return SizedBox(
|
||||
height: 130, // Set a specific height
|
||||
child: CarouselView(
|
||||
|
@ -61,11 +74,34 @@ class EnhancedCarouselView<T> extends StatelessWidget {
|
|||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
onTap: (index) => filter(snapshot.data![index] as T, context),
|
||||
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),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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<ItemType, HeaderType, FooterType, QueryType>
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final String defaultMessage = FFLocalizations.of(context).getVariableText(
|
||||
ptText: 'Nenhum item encontrado',
|
||||
enText: 'No items found',
|
||||
);
|
||||
final header = StreamBuilder<List<HeaderType>>(
|
||||
stream: bloc.states.headerItems.cast<List<HeaderType>>(),
|
||||
builder: (context, headerSnapshot) {
|
||||
|
@ -250,7 +255,9 @@ class EnhancedListViewState<ItemType, HeaderType, FooterType, QueryType>
|
|||
} 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,
|
||||
|
|
|
@ -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<ReadView> {
|
|||
final Future<DocumentType> 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<ReadView> {
|
|||
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<void> 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<ReadView> {
|
|||
);
|
||||
}
|
||||
|
||||
void onShare() async {
|
||||
Future<void> onShare() async {
|
||||
try {
|
||||
final Uri uri = Uri.parse(widget.url);
|
||||
final response = await http.get(uri);
|
||||
|
@ -113,10 +124,15 @@ class ReadViewState extends State<ReadView> {
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue