WIP
This commit is contained in:
parent
ed66fc86a2
commit
263013930e
|
@ -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<Uri> 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<ApiCallResponse> call() async {
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
part of 'index.dart';
|
||||
|
||||
abstract interface class Archive extends Entity {}
|
|
@ -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,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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<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),
|
||||
|
@ -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<Color>(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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Document>(
|
||||
key: model.searchKey,
|
||||
child: EnhancedRemoteListView<Document, Category>(
|
||||
key: model.managerKey,
|
||||
pagingController: model._pagingController,
|
||||
headerBuilder: model.listHeaderBuilder,
|
||||
bodyBuilder: model.listBodyBuilder,
|
||||
headerItems: model.generateCategories,
|
||||
bodyBuilder: model.documentItemBuilder,
|
||||
dataProvider: model.generateDocuments,
|
||||
onFetchError: model.onFetchError,
|
||||
),
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
part of 'index.dart';
|
||||
|
||||
/// -----------------------------------------------
|
||||
/// [DocumentPageBloc]
|
||||
/// -----------------------------------------------
|
||||
|
||||
class DocumentPageBloc extends Bloc<DocumentPageEvent, DocumentPageState> {
|
||||
final DocumentPageModel model;
|
||||
|
||||
DocumentPageBloc._(this.model, DocumentPageState initialState)
|
||||
: super(initialState) {
|
||||
on<SelectDocumentEvent>(_selectDocument);
|
||||
on<UnselectDocumentEvent>(_unselectDocument);
|
||||
on<SelectCategoryEvent>(_selectCategory);
|
||||
on<UnselectCategoryEvent>(_unselectCategory);
|
||||
}
|
||||
|
||||
static DocumentPageBloc create(DocumentPageModel model) {
|
||||
final initialState = DocumentPageState(
|
||||
categories: [],
|
||||
documents: [],
|
||||
);
|
||||
return DocumentPageBloc._(model, initialState);
|
||||
}
|
||||
|
||||
Future<void> _selectCategory(
|
||||
SelectCategoryEvent event, Emitter<DocumentPageState> 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<void> _unselectCategory(
|
||||
UnselectCategoryEvent event, Emitter<DocumentPageState> emit) async {
|
||||
emit(state);
|
||||
}
|
||||
|
||||
Future<void> _selectDocument(
|
||||
SelectDocumentEvent event, Emitter<DocumentPageState> emit) async {
|
||||
print('-> select');
|
||||
emit(state.copyWith(
|
||||
uri: await GetPDF().call(event.document.id),
|
||||
currentDocument: event.document,
|
||||
isDocumentSelected: true,
|
||||
));
|
||||
}
|
||||
|
||||
Future<void> _unselectDocument(
|
||||
UnselectDocumentEvent event, Emitter<DocumentPageState> 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<Document?> documents;
|
||||
final List<Category?> 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<Document?>? documents,
|
||||
Document? currentDocument,
|
||||
bool? isDocumentSelected,
|
||||
List<Category?>? 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,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,59 +1,80 @@
|
|||
part of 'index.dart';
|
||||
|
||||
class DocumentPageModel extends FlutterFlowModel<DocumentPage> {
|
||||
@override
|
||||
void dispose() {}
|
||||
DocumentPageModel();
|
||||
|
||||
late final GlobalKey<State<FutureBuilder>> pageKey;
|
||||
late final SearchKey managerKey;
|
||||
late final DocumentKey viewerKey;
|
||||
late final PagingController<int, Document> _pagingController;
|
||||
|
||||
/// ------------
|
||||
|
||||
@override
|
||||
void initState(BuildContext context) {}
|
||||
void initState(BuildContext context) {
|
||||
pageKey = GlobalKey<State<FutureBuilder>>();
|
||||
managerKey = SearchKey();
|
||||
viewerKey = DocumentKey();
|
||||
|
||||
final SearchKey searchKey = SearchKey();
|
||||
final DocumentKey docKey = DocumentKey();
|
||||
_pagingController = PagingController<int, Document>(firstPageKey: 1);
|
||||
}
|
||||
|
||||
final PagingController<int, Document> _pagingController =
|
||||
PagingController<int, Document>(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<DocumentPageBloc>().add(SelectDocumentEvent(document));
|
||||
}
|
||||
|
||||
List<Document?> documents = [];
|
||||
List<Category?> categories = [];
|
||||
/// [documentItemBuilder]
|
||||
DocumentItem documentItemBuilder<T extends Document>(
|
||||
BuildContext context, T item, int index) {
|
||||
return DocumentItem(
|
||||
document: item,
|
||||
onPressed: onView,
|
||||
);
|
||||
}
|
||||
|
||||
/// [listBodyBuilder]
|
||||
Widget listBodyBuilder<T>(BuildContext context, Document item, int index) {
|
||||
return DocumentItem(document: item);
|
||||
CategoryItem categoryItemBuilder<T>(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<T>(Future<List<T?>> 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<Category>(
|
||||
categories: categories,
|
||||
filter: filterByCategory,
|
||||
),
|
||||
],
|
||||
);
|
||||
EnhancedCarouselView<T>(
|
||||
generateItems: gen,
|
||||
itemBuilder: categoryItemBuilder,
|
||||
filter: filter<T>,
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
/// [generateDocuments]
|
||||
Future<(bool, List<Document?>)> generateDocuments(
|
||||
Future<(bool, List<Document?>?)> generateDocuments(
|
||||
pageKey, Query query) async {
|
||||
final List<Document?> error = [null];
|
||||
print('Query: ${query is Document}');
|
||||
|
@ -103,9 +124,8 @@ class DocumentPageModel extends FlutterFlowModel<DocumentPage> {
|
|||
}
|
||||
|
||||
/// [generateCategories]
|
||||
Future<List<Category?>> generateCategories(List<Document?> documents) async {
|
||||
Future<List<Category?>> generateCategories() async {
|
||||
final List<Category?> 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<DocumentPage> {
|
|||
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>(T query, BuildContext context) {
|
||||
context
|
||||
.read<DocumentPageBloc>()
|
||||
.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);
|
||||
}
|
||||
|
|
|
@ -11,44 +11,34 @@ class DocumentPage extends StatefulPage {
|
|||
|
||||
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 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<void>(
|
||||
future: initAsync(),
|
||||
builder: (context, snapshot) {
|
||||
return DocumentManagerScreen(model: model);
|
||||
},
|
||||
);
|
||||
// return DocumentViewScreen(document: documents.first);
|
||||
}
|
||||
return BlocProvider<DocumentPageBloc>(
|
||||
create: (context) => DocumentPageBloc.create(model),
|
||||
child: BlocBuilder<DocumentPageBloc, DocumentPageState>(
|
||||
builder: (context, state) {
|
||||
print('Bloc -> ${state.isCategorySelected}');
|
||||
|
||||
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');
|
||||
if (state.isDocumentSelected)
|
||||
return DocumentViewScreen(
|
||||
doc: state.currentDocument!,
|
||||
uri: state.uri!,
|
||||
);
|
||||
else
|
||||
return DocumentManagerScreen(
|
||||
model: model,
|
||||
state: state,
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<DocumentViewScreen> createState() => _DocumentViewScreenState();
|
||||
ScreenState<DocumentViewScreen> createState() => _DocumentViewScreenState();
|
||||
}
|
||||
|
||||
class _DocumentViewScreenState extends State<DocumentViewScreen> {
|
||||
class _DocumentViewScreenState extends ScreenState<DocumentViewScreen> {
|
||||
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<DocumentPageBloc>().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<DocumentViewScreen> {
|
|||
color: Colors.black,
|
||||
),
|
||||
color: Colors.black,
|
||||
onPressed: onPressed,
|
||||
onPressed: onShare,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
|
@ -6,25 +6,30 @@ import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
|||
extension PagedListViewExtension<PageKeyType, ItemType>
|
||||
on PagedSliverList<PageKeyType, ItemType> {}
|
||||
|
||||
typedef PaginatedListViewHeaderBuilder<T> = Widget Function<T>(
|
||||
Future<List<T?>> Function() gen);
|
||||
typedef PaginatedListViewBodyBuilder<T> = Widget Function(BuildContext, T, int);
|
||||
|
||||
mixin Pageable<T extends StatefulWidget> on State<T> {
|
||||
Expanded buildPaginatedListView<PageKeyType, ItemType>(
|
||||
Expanded buildPaginatedListView<PageKeyType, BodyType, HeaderType>(
|
||||
String noDataFound,
|
||||
PagingController<PageKeyType, ItemType> pg,
|
||||
Widget Function(BuildContext) headerBuilder,
|
||||
Widget Function(BuildContext, ItemType, int) bodyBuilder) {
|
||||
PagingController<PageKeyType, BodyType> pg,
|
||||
Future<List<HeaderType?>> Function() headerItems,
|
||||
PaginatedListViewHeaderBuilder<BodyType> headerBuilder,
|
||||
PaginatedListViewBodyBuilder<BodyType> bodyBuilder) {
|
||||
final theme = FlutterFlowTheme.of(context);
|
||||
return Expanded(
|
||||
child: RefreshIndicator(
|
||||
backgroundColor: theme.primaryBackground,
|
||||
color: theme.primary,
|
||||
onRefresh: () async => pg.refresh(),
|
||||
child: PagedListView<PageKeyType, ItemType>(
|
||||
child: PagedListView<PageKeyType, BodyType>(
|
||||
pagingController: pg,
|
||||
builderDelegate: PagedChildBuilderDelegate<ItemType>(
|
||||
builderDelegate: PagedChildBuilderDelegate<BodyType>(
|
||||
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<T extends StatefulWidget> on State<T> {
|
|||
firstPageProgressIndicatorBuilder: (context) =>
|
||||
buildLoadingIndicator(context),
|
||||
noItemsFoundIndicatorBuilder: (context) =>
|
||||
buildNoDataFound(context, noDataFound, headerBuilder),
|
||||
buildNoDataFound<HeaderType>(
|
||||
context, noDataFound, headerItems, headerBuilder),
|
||||
firstPageErrorIndicatorBuilder: (context) => const Placeholder(),
|
||||
newPageErrorIndicatorBuilder: (context) => const Placeholder(),
|
||||
),
|
||||
|
@ -92,16 +98,17 @@ mixin Pageable<T extends StatefulWidget> on State<T> {
|
|||
showSnackbar(context, message, true);
|
||||
}
|
||||
|
||||
Widget buildNoDataFound(
|
||||
Widget buildNoDataFound<T>(
|
||||
BuildContext context,
|
||||
String title,
|
||||
Widget Function(BuildContext) headerBuilder,
|
||||
Future<List<T?>> Function() items,
|
||||
Widget Function<T>(Future<List<T?>> 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(
|
||||
|
|
|
@ -20,3 +20,5 @@ abstract class StatefulComponent<T> extends StatefulWidget
|
|||
implements ComponentWidget<T> {
|
||||
const StatefulComponent({super.key});
|
||||
}
|
||||
|
||||
abstract class ComponentState<T extends StatefulComponent> extends State<T> {}
|
||||
|
|
|
@ -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<String>('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<T> extends ModelWidget implements PageWidget<T> {
|
|||
}
|
||||
|
||||
abstract class StatelessPage<T> extends StatelessWidget
|
||||
with Template
|
||||
implements PageWidget<T> {
|
||||
const StatelessPage({super.key});
|
||||
}
|
||||
|
@ -61,4 +68,4 @@ abstract class StatefulPage<T> extends StatefulWidget implements PageWidget<T> {
|
|||
}
|
||||
|
||||
abstract class PageState<T extends StatefulPage> extends State<T>
|
||||
with MixinPage {}
|
||||
with Template {}
|
||||
|
|
|
@ -9,6 +9,7 @@ abstract class ModelScreen<T> extends ModelWidget implements ScreenWidget<T> {
|
|||
}
|
||||
|
||||
abstract class StatelessScreen<T> extends StatelessWidget
|
||||
with Template
|
||||
implements ScreenWidget<T> {
|
||||
const StatelessScreen({super.key});
|
||||
}
|
||||
|
@ -17,3 +18,6 @@ abstract class StatefulScreen<T> extends StatefulWidget
|
|||
implements ScreenWidget<T> {
|
||||
const StatefulScreen({super.key});
|
||||
}
|
||||
|
||||
abstract class ScreenState<T extends StatefulScreen> extends State<T>
|
||||
with Template {}
|
||||
|
|
|
@ -1,59 +1,32 @@
|
|||
part of '../widgets.dart';
|
||||
|
||||
class CategoryCarousel<T> extends StatelessWidget {
|
||||
final List<T?> categories;
|
||||
final void Function(T) filter;
|
||||
class EnhancedCarouselView<T> extends StatelessWidget {
|
||||
final Future<List<T?>> Function() generateItems;
|
||||
final void Function(T, BuildContext) filter;
|
||||
final Widget Function<T>(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<List<T?>>(
|
||||
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(),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,39 +1,32 @@
|
|||
part of '../widgets.dart';
|
||||
|
||||
typedef SearchKey = GlobalKey<RemoteSearchViewState>;
|
||||
typedef SearchKey = GlobalKey<EnhancedRemoteListViewState>;
|
||||
|
||||
typedef Query<X extends DocumentEntity> = X?;
|
||||
typedef Query<X extends Archive> = X?;
|
||||
|
||||
/// -----------------------------------------------
|
||||
/// [SearchView]
|
||||
/// [EnhancedListView]
|
||||
/// -----------------------------------------------
|
||||
|
||||
class SearchView<T> extends StatefulComponent {
|
||||
const SearchView({super.key});
|
||||
|
||||
@override
|
||||
State<SearchView> createState() => _SearchViewState();
|
||||
abstract interface class EnhancedListView<T> extends StatefulWidget {
|
||||
const EnhancedListView({super.key});
|
||||
}
|
||||
|
||||
class _SearchViewState<T> extends State<SearchView> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Placeholder();
|
||||
}
|
||||
}
|
||||
abstract interface class EnhancedListViewState<T>
|
||||
extends State<EnhancedListView> {}
|
||||
|
||||
/// -----------------------------------------------
|
||||
/// [LocalSearchView]
|
||||
/// [EnhancedLocalListView]
|
||||
/// -----------------------------------------------
|
||||
|
||||
class LocalSearchView<T> extends SearchView<T> {
|
||||
class EnhancedLocalListView<T> extends EnhancedListView<T> {
|
||||
final List<T> list;
|
||||
final Widget Function(T) itemBuilder;
|
||||
final bool Function(T, String) filter;
|
||||
final Widget header;
|
||||
final List<T> Function(String)? onSearch;
|
||||
|
||||
LocalSearchView({
|
||||
EnhancedLocalListView({
|
||||
Key? key,
|
||||
required this.list,
|
||||
required this.itemBuilder,
|
||||
|
@ -49,10 +42,11 @@ class LocalSearchView<T> extends SearchView<T> {
|
|||
// return documents.where((documents) => filter(documents, query)).toList();
|
||||
|
||||
@override
|
||||
LocalSearchViewState<T> createState() => LocalSearchViewState<T>();
|
||||
EnhancedLocalListViewState<T> createState() =>
|
||||
EnhancedLocalListViewState<T>();
|
||||
}
|
||||
|
||||
class LocalSearchViewState<T> extends State<LocalSearchView<T>> {
|
||||
class EnhancedLocalListViewState<T> extends State<EnhancedLocalListView<T>> {
|
||||
TextEditingController editingController = TextEditingController();
|
||||
late List<T> filteredItems;
|
||||
|
||||
|
@ -141,22 +135,25 @@ class LocalSearchViewState<T> extends State<LocalSearchView<T>> {
|
|||
}
|
||||
|
||||
/// -----------------------------------------------
|
||||
/// [RemoteSearchView]
|
||||
/// [EnhancedRemoteListView]
|
||||
/// -----------------------------------------------
|
||||
|
||||
class RemoteSearchView<T> extends SearchView<T> {
|
||||
// ignore: must_be_immutable
|
||||
class EnhancedRemoteListView<T, Y> extends EnhancedListView<T> {
|
||||
final Widget Function(BuildContext, T, int) bodyBuilder;
|
||||
Widget Function(BuildContext) headerBuilder;
|
||||
final Future<List<Y?>> Function() headerItems;
|
||||
Widget Function<T>(Future<List<T?>> Function() gen) headerBuilder;
|
||||
final PagingController<int, T> pagingController;
|
||||
final Future<(bool, List<T?>)> Function(int pageKey, Query query)
|
||||
final Future<(bool, List<T?>?)> 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<T> extends SearchView<T> {
|
|||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
RemoteSearchViewState<T> createState() => RemoteSearchViewState<T>();
|
||||
EnhancedRemoteListViewState<T, Y> createState() =>
|
||||
EnhancedRemoteListViewState<T, Y>();
|
||||
}
|
||||
|
||||
class RemoteSearchViewState<T> extends State<RemoteSearchView<T>>
|
||||
with Pageable {
|
||||
class EnhancedRemoteListViewState<T, Y>
|
||||
extends State<EnhancedRemoteListView<T, Y>> with Pageable {
|
||||
TextEditingController editingController = TextEditingController();
|
||||
bool isLoading = false;
|
||||
Query query = Document.fromDesc('');
|
||||
|
@ -246,9 +244,10 @@ class RemoteSearchViewState<T> extends State<RemoteSearchView<T>>
|
|||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
buildPaginatedListView<int, T>(
|
||||
buildPaginatedListView<int, T, Y>(
|
||||
noDataFound,
|
||||
widget.pagingController,
|
||||
widget.headerItems,
|
||||
widget.headerBuilder,
|
||||
widget.bodyBuilder,
|
||||
),
|
|
@ -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]
|
||||
|
|
Loading…
Reference in New Issue