WIP
This commit is contained in:
parent
07f55bbceb
commit
866e438c87
|
@ -5,8 +5,7 @@ import 'dart:convert';
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:http/http.dart';
|
import 'package:hub/features/documents/documents.dart' as doc;
|
||||||
import 'package:hub/features/documents/index.dart' as doc;
|
|
||||||
import 'package:hub/features/notification/index.dart';
|
import 'package:hub/features/notification/index.dart';
|
||||||
import 'package:hub/features/storage/index.dart';
|
import 'package:hub/features/storage/index.dart';
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1 @@
|
||||||
part of 'index.dart';
|
part of 'index.dart';
|
||||||
|
|
||||||
abstract interface class Archive extends Entity {}
|
|
||||||
|
|
|
@ -1,68 +1 @@
|
||||||
part of 'index.dart';
|
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,177 +1 @@
|
||||||
part of 'index.dart';
|
part of 'index.dart';
|
||||||
|
|
||||||
interface class Document extends Archive {
|
|
||||||
final int id;
|
|
||||||
final String description;
|
|
||||||
final String type;
|
|
||||||
final Category category;
|
|
||||||
final String person;
|
|
||||||
final String property;
|
|
||||||
String createdAt;
|
|
||||||
String updatedAt;
|
|
||||||
|
|
||||||
Document({
|
|
||||||
required this.id,
|
|
||||||
required this.description,
|
|
||||||
required this.type,
|
|
||||||
required this.category,
|
|
||||||
required this.person,
|
|
||||||
required this.property,
|
|
||||||
required this.createdAt,
|
|
||||||
required this.updatedAt,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory Document.fromDesc(String desc) => Document(
|
|
||||||
id: 0,
|
|
||||||
description: desc,
|
|
||||||
type: '',
|
|
||||||
category: Category.fromDesc(''),
|
|
||||||
person: '',
|
|
||||||
property: '',
|
|
||||||
createdAt: '',
|
|
||||||
updatedAt: '',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ignore: must_be_immutable
|
|
||||||
class DocumentItem extends StatelessComponent {
|
|
||||||
final Document document;
|
|
||||||
void Function(Document, BuildContext) onPressed;
|
|
||||||
|
|
||||||
DocumentItem({
|
|
||||||
super.key,
|
|
||||||
required this.document,
|
|
||||||
required this.onPressed,
|
|
||||||
});
|
|
||||||
|
|
||||||
Tooltip _buildTooltip(String text, Color color, BuildContext context,
|
|
||||||
BoxConstraints constraints) {
|
|
||||||
final Color textColor = FlutterFlowTheme.of(context).info;
|
|
||||||
|
|
||||||
final area = (MediaQuery.of(context).size.height +
|
|
||||||
MediaQuery.of(context).size.width) /
|
|
||||||
2;
|
|
||||||
|
|
||||||
final double boxHeight = area * 0.033;
|
|
||||||
final double boxWidth = area * 0.19;
|
|
||||||
|
|
||||||
return Tooltip(
|
|
||||||
message: text,
|
|
||||||
child: Container(
|
|
||||||
width: boxWidth,
|
|
||||||
height: boxHeight,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: color,
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
child: Center(
|
|
||||||
child: AutoText(
|
|
||||||
text,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: TextStyle(
|
|
||||||
color: textColor,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final Color primaryText = FlutterFlowTheme.of(context).primaryText;
|
|
||||||
final Color primaryColor = FlutterFlowTheme.of(context).primary;
|
|
||||||
|
|
||||||
final TextStyle textStyleMajor = TextStyle(
|
|
||||||
color: primaryText,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
);
|
|
||||||
final TextStyle textStyleMinor = TextStyle(
|
|
||||||
color: primaryText,
|
|
||||||
fontWeight: FontWeight.normal,
|
|
||||||
fontStyle: FontStyle.italic,
|
|
||||||
);
|
|
||||||
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.all(8),
|
|
||||||
child: LayoutBuilder(
|
|
||||||
builder: (context, constraints) {
|
|
||||||
final double boxHeight = constraints.maxHeight > 350
|
|
||||||
? MediaQuery.of(context).size.height * 0.07
|
|
||||||
: MediaQuery.of(context).size.height * 2;
|
|
||||||
|
|
||||||
return InkWell(
|
|
||||||
onTap: () => onPressed(document, context),
|
|
||||||
enableFeedback: true,
|
|
||||||
overlayColor: WidgetStateProperty.all<Color>(primaryColor),
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
child: SizedBox(
|
|
||||||
height: boxHeight,
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
// const SizedBox(width: 10),
|
|
||||||
Icon(Icons.description, color: document.category.color),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Tooltip(
|
|
||||||
message: document.description,
|
|
||||||
child: AutoText(
|
|
||||||
document.description,
|
|
||||||
style: textStyleMajor,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
AutoText(
|
|
||||||
ValidatorUtil.toLocalDateTime(
|
|
||||||
'yyyy-MM-dd', document.updatedAt),
|
|
||||||
style: textStyleMinor,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
_buildTooltip(
|
|
||||||
document.category.title,
|
|
||||||
document.category.color,
|
|
||||||
context,
|
|
||||||
constraints,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// const SizedBox(width: 10),
|
|
||||||
Center(
|
|
||||||
child: Icon(
|
|
||||||
Icons.arrow_right,
|
|
||||||
color: primaryText,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
DocumentItem copyWith({
|
|
||||||
Document? document,
|
|
||||||
}) {
|
|
||||||
return DocumentItem(
|
|
||||||
document: document ?? this.document,
|
|
||||||
onPressed: onPressed,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,148 +1 @@
|
||||||
part of 'index.dart';
|
part of 'index.dart';
|
||||||
|
|
||||||
/// -----------------------------------------------
|
|
||||||
/// [DocumentPageBloc]
|
|
||||||
/// -----------------------------------------------
|
|
||||||
|
|
||||||
class DocumentPageBloc extends Bloc<DocumentPageEvent, DocumentPageState> {
|
|
||||||
final DocumentPageModel model;
|
|
||||||
static DocumentPageBloc? _singleton;
|
|
||||||
|
|
||||||
factory DocumentPageBloc(DocumentPageModel model) {
|
|
||||||
_singleton ??= DocumentPageBloc._internal(model);
|
|
||||||
return _singleton!;
|
|
||||||
}
|
|
||||||
|
|
||||||
DocumentPageBloc._internal(this.model) : super(DocumentPageState()) {
|
|
||||||
on<SelectDocumentEvent>(_selectDocument);
|
|
||||||
on<UnselectDocumentEvent>(_unselectDocument);
|
|
||||||
on<FilterCategoryEvent>(_filterCategoryEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _filterCategoryEvent(
|
|
||||||
FilterCategoryEvent event, Emitter<DocumentPageState> emit) async {
|
|
||||||
_selectCategory(event, emit);
|
|
||||||
state.isCategorySelected
|
|
||||||
? _unselectCategory(event, emit)
|
|
||||||
: _selectCategory(event, emit);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _selectCategory(
|
|
||||||
FilterCategoryEvent event, Emitter<DocumentPageState> emit) async {
|
|
||||||
log('filterItems A: ${event.query}');
|
|
||||||
emit(state.copyWith(
|
|
||||||
isCategorySelected: true,
|
|
||||||
));
|
|
||||||
|
|
||||||
final listViewState = model.vihicleScreenManager.currentState!;
|
|
||||||
listViewState.widget.bodyItems = (await model.generateBodyItems(
|
|
||||||
1, 10, event.query)) as BodyItemsBuilder<Document>;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _unselectCategory(
|
|
||||||
FilterCategoryEvent event, Emitter<DocumentPageState> emit) async {
|
|
||||||
emit(state.copyWith(
|
|
||||||
isCategorySelected: false,
|
|
||||||
));
|
|
||||||
|
|
||||||
final listViewState = model.vihicleScreenManager.currentState!;
|
|
||||||
listViewState.widget.bodyItems = (await model.generateBodyItems(
|
|
||||||
1, 10, null)) as BodyItemsBuilder<Document>;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
emit(
|
|
||||||
state.copyWith(
|
|
||||||
currentDocument: null,
|
|
||||||
isDocumentSelected: false,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// -----------------------------------------------
|
|
||||||
/// [DocumentPageEvent]
|
|
||||||
/// -----------------------------------------------
|
|
||||||
|
|
||||||
abstract class DocumentPageEvent {}
|
|
||||||
|
|
||||||
class SelectDocumentEvent extends DocumentPageEvent {
|
|
||||||
final Document document;
|
|
||||||
SelectDocumentEvent(
|
|
||||||
this.document,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class UnselectDocumentEvent extends DocumentPageEvent {}
|
|
||||||
|
|
||||||
class FilterCategoryEvent extends DocumentPageEvent {
|
|
||||||
final Query query;
|
|
||||||
FilterCategoryEvent(this.query);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// -----------------------------------------------
|
|
||||||
/// [DocumentPageState]
|
|
||||||
/// -----------------------------------------------
|
|
||||||
|
|
||||||
class DocumentPageState {
|
|
||||||
final bool isCategorySelected;
|
|
||||||
final bool isDocumentSelected;
|
|
||||||
final Document? currentDocument;
|
|
||||||
final Category? currentCategory;
|
|
||||||
final Uri? uri;
|
|
||||||
final int? count;
|
|
||||||
final dynamic page;
|
|
||||||
final Query? query;
|
|
||||||
|
|
||||||
const DocumentPageState({
|
|
||||||
this.query,
|
|
||||||
this.count,
|
|
||||||
this.page,
|
|
||||||
this.uri,
|
|
||||||
this.currentDocument,
|
|
||||||
this.isCategorySelected = false,
|
|
||||||
this.currentCategory,
|
|
||||||
this.isDocumentSelected = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
DocumentPageState copyWith({
|
|
||||||
Uri? uri,
|
|
||||||
Query? query,
|
|
||||||
int? count,
|
|
||||||
dynamic page,
|
|
||||||
List<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,
|
|
||||||
//
|
|
||||||
currentDocument: currentDocument ?? this.currentDocument,
|
|
||||||
isDocumentSelected: isDocumentSelected ?? this.isDocumentSelected,
|
|
||||||
//
|
|
||||||
currentCategory: currentCategory ?? this.currentCategory,
|
|
||||||
isCategorySelected: isCategorySelected ?? this.isCategorySelected,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,175 +1 @@
|
||||||
part of 'index.dart';
|
part of 'index.dart';
|
||||||
|
|
||||||
class DocumentPageModel extends FlutterFlowModel<DocumentPage> {
|
|
||||||
DocumentPageModel._privateConstructor();
|
|
||||||
|
|
||||||
static final DocumentPageModel _instance =
|
|
||||||
DocumentPageModel._privateConstructor();
|
|
||||||
|
|
||||||
factory DocumentPageModel() {
|
|
||||||
return _instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
late EnhancedListViewKey<Document, Category, Null> vihicleScreenManager;
|
|
||||||
late DocumentKey vehicleScreenViewer;
|
|
||||||
late PagingController<int, Document> _pagingController;
|
|
||||||
|
|
||||||
/// ------------
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState(BuildContext context) {
|
|
||||||
vihicleScreenManager = EnhancedListViewKey<Document, Category, Null>();
|
|
||||||
vehicleScreenViewer = DocumentKey();
|
|
||||||
|
|
||||||
_pagingController = PagingController<int, Document>(firstPageKey: 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_pagingController.dispose();
|
|
||||||
vihicleScreenManager.currentState?.dispose();
|
|
||||||
vehicleScreenViewer.currentState?.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ------------
|
|
||||||
|
|
||||||
/// [onView]
|
|
||||||
void onView(Document document, BuildContext context) async {
|
|
||||||
context.read<DocumentPageBloc>().add(SelectDocumentEvent(document));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// [itemBodyBuilder]
|
|
||||||
DocumentItem itemBodyBuilder<T extends Document>(
|
|
||||||
BuildContext context, T item, int index) {
|
|
||||||
print('ItemBuilder -> $index');
|
|
||||||
return DocumentItem(
|
|
||||||
document: item,
|
|
||||||
onPressed: onView,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
CategoryItem categoryItemBuilder<T>(T? item) {
|
|
||||||
return CategoryItem(category: item! as Category);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// [itemHeaderBuilder]
|
|
||||||
Widget itemHeaderBuilder<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(
|
|
||||||
FFLocalizations.of(context).getVariableText(
|
|
||||||
enText: 'Recent Documents', ptText: 'Últimos Documentos'),
|
|
||||||
style: TextStyle(
|
|
||||||
color: FlutterFlowTheme.of(context).primaryText,
|
|
||||||
fontSize: LimitedFontSizeUtil.getHeaderFontSize(context),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
EnhancedCarouselView<T>(
|
|
||||||
generateItems: gen,
|
|
||||||
itemBuilder: categoryItemBuilder,
|
|
||||||
filter: filter<T>,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
/// [generateBodyItems]
|
|
||||||
Future<List<T?>> generateBodyItems<T>(
|
|
||||||
int pageKey, int pageSize, dynamic query) async {
|
|
||||||
log('generateDocuments: $query');
|
|
||||||
|
|
||||||
final List<T?> error = [null];
|
|
||||||
print('Query: ${query is Document}');
|
|
||||||
final GetDocuments getDocuments = FreAccessWSGlobal.getDocuments;
|
|
||||||
final ApiCallResponse newItems = await getDocuments.call(pageKey, query);
|
|
||||||
|
|
||||||
if (newItems.jsonBody == null) return error;
|
|
||||||
if (newItems.jsonBody['error'] == true) return error;
|
|
||||||
|
|
||||||
final List<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 docs as List<T?>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// [generateHeaderItems]
|
|
||||||
Future<List<T?>> generateHeaderItems<T>() async {
|
|
||||||
log('generateCategories: ');
|
|
||||||
final List<T?> error = [null];
|
|
||||||
|
|
||||||
final GetCategories getCategories = FreAccessWSGlobal.getCategories;
|
|
||||||
final ApiCallResponse newItems = await getCategories.call();
|
|
||||||
|
|
||||||
if (newItems.jsonBody['error'] == true) return error;
|
|
||||||
if (newItems.jsonBody == null) return error;
|
|
||||||
final list = newItems.jsonBody['value'] as List<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 as List<T?>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// [filter]
|
|
||||||
void filter<T>(T query, BuildContext context) {
|
|
||||||
context
|
|
||||||
.read<DocumentPageBloc>()
|
|
||||||
.add(FilterCategoryEvent(query as Archive?));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// [onFetchError]
|
|
||||||
void onFetchError(Object e, StackTrace s) {
|
|
||||||
DialogUtil.errorDefault(vehicleScreenViewer.currentContext!);
|
|
||||||
LogUtil.requestAPIFailed(
|
|
||||||
"proccessRequest.php", "", "Consulta de Veículo", e, s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,44 +1 @@
|
||||||
part of 'index.dart';
|
part of 'index.dart';
|
||||||
|
|
||||||
typedef DocumentKey = GlobalKey<FREDocumentPageState>;
|
|
||||||
|
|
||||||
class DocumentPage extends StatefulPage {
|
|
||||||
const DocumentPage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<DocumentPage> createState() => FREDocumentPageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class FREDocumentPageState<T extends DocumentPage>
|
|
||||||
extends PageState<DocumentPage> {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) => buildBody(context);
|
|
||||||
DocumentPageModel model = DocumentPageModel();
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
model.initState(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget buildBody(BuildContext context) {
|
|
||||||
return BlocProvider<DocumentPageBloc>(
|
|
||||||
create: (context) => DocumentPageBloc(model),
|
|
||||||
child: BlocBuilder<DocumentPageBloc, DocumentPageState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
print('Bloc -> ${state.isCategorySelected}');
|
|
||||||
|
|
||||||
if (state.isDocumentSelected)
|
|
||||||
return DocumentViewScreen(
|
|
||||||
doc: state.currentDocument!,
|
|
||||||
uri: state.uri!,
|
|
||||||
);
|
|
||||||
else
|
|
||||||
return DocumentManagerScreen(
|
|
||||||
model: model,
|
|
||||||
state: state,
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,49 +1 @@
|
||||||
part of 'index.dart';
|
part of 'index.dart';
|
||||||
|
|
||||||
class DocumentManagerScreen extends StatelessScreen {
|
|
||||||
final DocumentPageModel model;
|
|
||||||
final DocumentPageState state;
|
|
||||||
|
|
||||||
const DocumentManagerScreen({
|
|
||||||
super.key,
|
|
||||||
required this.model,
|
|
||||||
required this.state,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final String title = FFLocalizations.of(context).getVariableText(
|
|
||||||
enText: 'Documents',
|
|
||||||
ptText: 'Documentos',
|
|
||||||
);
|
|
||||||
final theme = FlutterFlowTheme.of(context);
|
|
||||||
action() => Navigator.pop(context);
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: theme.primaryBackground,
|
|
||||||
appBar: buildAppBar(title, context, action),
|
|
||||||
body: buildBody(context),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget buildBody(BuildContext context) {
|
|
||||||
final SizedBox space = SizedBox(height: 30);
|
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: EnhancedListView<Document, Category, Null>(
|
|
||||||
key: model.vihicleScreenManager,
|
|
||||||
headerBuilder: model.itemHeaderBuilder<Category>,
|
|
||||||
headerItems: model.generateHeaderItems<Category>,
|
|
||||||
bodyBuilder: model.itemBodyBuilder<Document>,
|
|
||||||
bodyItems: model.generateBodyItems<Document>,
|
|
||||||
footerBuilder: null,
|
|
||||||
footerItems: null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
] //
|
|
||||||
.addToStart(space)
|
|
||||||
.addToEnd(space),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,46 +1 @@
|
||||||
part of 'index.dart';
|
part of 'index.dart';
|
||||||
|
|
||||||
class DocumentViewScreen extends StatefulScreen {
|
|
||||||
const DocumentViewScreen({
|
|
||||||
super.key,
|
|
||||||
required this.doc,
|
|
||||||
required this.uri,
|
|
||||||
});
|
|
||||||
|
|
||||||
final Document doc;
|
|
||||||
final Uri uri;
|
|
||||||
|
|
||||||
@override
|
|
||||||
ScreenState<DocumentViewScreen> createState() => _DocumentViewScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _DocumentViewScreenState extends ScreenState<DocumentViewScreen> {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
action() {
|
|
||||||
context.read<DocumentPageBloc>().add(UnselectDocumentEvent());
|
|
||||||
}
|
|
||||||
|
|
||||||
final String title = widget.doc.description;
|
|
||||||
final theme = FlutterFlowTheme.of(context);
|
|
||||||
return PopScope(
|
|
||||||
canPop: false,
|
|
||||||
onPopInvokedWithResult: (didPop, result) => action(),
|
|
||||||
child: Scaffold(
|
|
||||||
backgroundColor: theme.primaryBackground,
|
|
||||||
appBar: buildAppBar(title, context, action),
|
|
||||||
body: buildBody(context),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget buildBody(BuildContext context) {
|
|
||||||
// final PDFViewerKey _viewerKey = PDFViewerKey();
|
|
||||||
|
|
||||||
return ReadView(
|
|
||||||
// search: _viewerKey,
|
|
||||||
title: widget.doc.description,
|
|
||||||
url: widget.uri.toString(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,745 @@
|
||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:hub/features/backend/index.dart';
|
||||||
|
import 'package:hub/flutter_flow/index.dart';
|
||||||
|
import 'package:hub/shared/extensions/index.dart';
|
||||||
|
import 'package:hub/shared/utils/index.dart';
|
||||||
|
import 'package:hub/shared/widgets/widgets.dart';
|
||||||
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
|
|
||||||
|
/// -----------------------------------------------
|
||||||
|
/// [TypeDefs]
|
||||||
|
/// -----------------------------------------------
|
||||||
|
|
||||||
|
typedef DocumentKey = GlobalKey<FREDocumentPageState>;
|
||||||
|
|
||||||
|
/// -----------------------------------------------
|
||||||
|
|
||||||
|
/// [Page]
|
||||||
|
/// -----------------------------------------------
|
||||||
|
|
||||||
|
class DocumentPage extends StatefulPage {
|
||||||
|
const DocumentPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<DocumentPage> createState() => FREDocumentPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class FREDocumentPageState<T extends DocumentPage>
|
||||||
|
extends PageState<DocumentPage> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => buildBody(context);
|
||||||
|
DocumentPageModel model = DocumentPageModel();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
model.initState(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget buildBody(BuildContext context) {
|
||||||
|
log('Build -> DocumentPage');
|
||||||
|
return BlocProvider<DocumentPageBloc>(
|
||||||
|
create: (context) => DocumentPageBloc(model),
|
||||||
|
child: BlocBuilder<DocumentPageBloc, DocumentPageState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
log('Build -> DocumentPageBloc');
|
||||||
|
print('Bloc -> ${state.isCategorySelected}');
|
||||||
|
|
||||||
|
if (state.isDocumentSelected)
|
||||||
|
return DocumentViewScreen(
|
||||||
|
doc: state.currentDocument!,
|
||||||
|
uri: state.uri!,
|
||||||
|
);
|
||||||
|
else
|
||||||
|
return DocumentManagerScreen(
|
||||||
|
model: model,
|
||||||
|
state: state,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// -----------------------------------------------
|
||||||
|
|
||||||
|
/// [Model]
|
||||||
|
/// -----------------------------------------------
|
||||||
|
|
||||||
|
class DocumentPageModel extends FlutterFlowModel<DocumentPage> {
|
||||||
|
DocumentPageModel._privateConstructor();
|
||||||
|
|
||||||
|
static final DocumentPageModel _instance =
|
||||||
|
DocumentPageModel._privateConstructor();
|
||||||
|
|
||||||
|
factory DocumentPageModel() {
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
late EnhancedListViewKey<Document, Category, Null> vehicleScreenManager;
|
||||||
|
late DocumentKey vehicleScreenViewer;
|
||||||
|
late PagingController<int, Document> _pagingController;
|
||||||
|
|
||||||
|
/// ------------
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState(BuildContext context) {
|
||||||
|
vehicleScreenManager = EnhancedListViewKey<Document, Category, Null>();
|
||||||
|
vehicleScreenViewer = DocumentKey();
|
||||||
|
|
||||||
|
_pagingController = PagingController<int, Document>(firstPageKey: 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_pagingController.dispose();
|
||||||
|
vehicleScreenManager.currentState?.dispose();
|
||||||
|
vehicleScreenViewer.currentState?.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ------------
|
||||||
|
|
||||||
|
/// [onView]
|
||||||
|
void onView(Document document, BuildContext context) async {
|
||||||
|
vehicleScreenManager.currentContext!
|
||||||
|
.read<DocumentPageBloc>()
|
||||||
|
.add(SelectDocumentEvent(document));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [itemBodyBuilder]
|
||||||
|
DocumentItem itemBodyBuilder<T extends Document>(
|
||||||
|
BuildContext context, T item, int index) {
|
||||||
|
print('ItemBuilder -> $index');
|
||||||
|
return DocumentItem(
|
||||||
|
document: item,
|
||||||
|
onPressed: onView,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
CategoryItem categoryItemBuilder<T>(T? item) {
|
||||||
|
return CategoryItem(category: item! as Category);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [itemHeaderBuilder]
|
||||||
|
Widget itemHeaderBuilder<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(
|
||||||
|
FFLocalizations.of(context).getVariableText(
|
||||||
|
enText: 'Recent Documents', ptText: 'Últimos Documentos'),
|
||||||
|
style: TextStyle(
|
||||||
|
color: FlutterFlowTheme.of(context).primaryText,
|
||||||
|
fontSize: LimitedFontSizeUtil.getHeaderFontSize(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
EnhancedCarouselView<T>(
|
||||||
|
generateItems: gen,
|
||||||
|
itemBuilder: categoryItemBuilder,
|
||||||
|
filter: filter<T>,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
/// [generateBodyItems]
|
||||||
|
Future<List<T?>> generateBodyItems<T>(
|
||||||
|
int pageKey, int pageSize, dynamic query) async {
|
||||||
|
log('generateDocuments: $query');
|
||||||
|
|
||||||
|
final List<T?> error = [null];
|
||||||
|
print('Query: ${query is Document}');
|
||||||
|
final GetDocuments getDocuments = FreAccessWSGlobal.getDocuments;
|
||||||
|
final ApiCallResponse newItems = await getDocuments.call(pageKey, query);
|
||||||
|
|
||||||
|
if (newItems.jsonBody == null) return error;
|
||||||
|
if (newItems.jsonBody['error'] == true) return error;
|
||||||
|
|
||||||
|
final List<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 docs as List<T?>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [generateHeaderItems]
|
||||||
|
Future<List<T?>> generateHeaderItems<T>() async {
|
||||||
|
log('generateCategories: ');
|
||||||
|
final List<T?> error = [null];
|
||||||
|
|
||||||
|
final GetCategories getCategories = FreAccessWSGlobal.getCategories;
|
||||||
|
final ApiCallResponse newItems = await getCategories.call();
|
||||||
|
|
||||||
|
if (newItems.jsonBody['error'] == true) return error;
|
||||||
|
if (newItems.jsonBody == null) return error;
|
||||||
|
final list = newItems.jsonBody['value'] as List<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 as List<T?>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [filter]
|
||||||
|
void filter<T>(T query, BuildContext context) {
|
||||||
|
context
|
||||||
|
.read<DocumentPageBloc>()
|
||||||
|
.add(FilterCategoryEvent(query as Archive?));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [onFetchError]
|
||||||
|
void onFetchError(Object e, StackTrace s) {
|
||||||
|
DialogUtil.errorDefault(vehicleScreenViewer.currentContext!);
|
||||||
|
LogUtil.requestAPIFailed(
|
||||||
|
"proccessRequest.php", "", "Consulta de Veículo", e, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// -----------------------------------------------
|
||||||
|
/// [BLoC]
|
||||||
|
/// -----------------------------------------------
|
||||||
|
|
||||||
|
/// [DocumentPageBloc]
|
||||||
|
|
||||||
|
class DocumentPageBloc extends Bloc<DocumentPageEvent, DocumentPageState> {
|
||||||
|
final DocumentPageModel model;
|
||||||
|
static DocumentPageBloc? _singleton;
|
||||||
|
|
||||||
|
factory DocumentPageBloc(DocumentPageModel model) {
|
||||||
|
_singleton ??= DocumentPageBloc._internal(model);
|
||||||
|
return _singleton!;
|
||||||
|
}
|
||||||
|
|
||||||
|
DocumentPageBloc._internal(this.model) : super(DocumentPageState()) {
|
||||||
|
on<SelectDocumentEvent>(_selectDocument);
|
||||||
|
on<UnselectDocumentEvent>(_unselectDocument);
|
||||||
|
on<FilterCategoryEvent>(_filterCategoryEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _filterCategoryEvent(
|
||||||
|
FilterCategoryEvent event, Emitter<DocumentPageState> emit) async {
|
||||||
|
_selectCategory(event, emit);
|
||||||
|
state.isCategorySelected
|
||||||
|
? _unselectCategory(event, emit)
|
||||||
|
: _selectCategory(event, emit);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _selectCategory(
|
||||||
|
FilterCategoryEvent event, Emitter<DocumentPageState> emit) async {
|
||||||
|
log('filterItems A: ${event.query}');
|
||||||
|
emit(state.copyWith(
|
||||||
|
isCategorySelected: true,
|
||||||
|
));
|
||||||
|
|
||||||
|
final listViewState = model.vehicleScreenManager.currentState!;
|
||||||
|
listViewState.widget.bodyItems = (await model.generateBodyItems(
|
||||||
|
1, 10, event.query)) as BodyItemsBuilder<Document>;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _unselectCategory(
|
||||||
|
FilterCategoryEvent event, Emitter<DocumentPageState> emit) async {
|
||||||
|
emit(state.copyWith(
|
||||||
|
isCategorySelected: false,
|
||||||
|
));
|
||||||
|
|
||||||
|
final listViewState = model.vehicleScreenManager.currentState!;
|
||||||
|
listViewState.widget.bodyItems = (await model.generateBodyItems(
|
||||||
|
1, 10, null)) as BodyItemsBuilder<Document>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
currentDocument: null,
|
||||||
|
isDocumentSelected: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [DocumentPageEvent]
|
||||||
|
|
||||||
|
abstract class DocumentPageEvent {}
|
||||||
|
|
||||||
|
class SelectDocumentEvent extends DocumentPageEvent {
|
||||||
|
final Document document;
|
||||||
|
SelectDocumentEvent(
|
||||||
|
this.document,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class UnselectDocumentEvent extends DocumentPageEvent {}
|
||||||
|
|
||||||
|
class FilterCategoryEvent extends DocumentPageEvent {
|
||||||
|
final Query query;
|
||||||
|
FilterCategoryEvent(this.query);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [DocumentPageState]
|
||||||
|
|
||||||
|
class DocumentPageState {
|
||||||
|
final bool isCategorySelected;
|
||||||
|
final bool isDocumentSelected;
|
||||||
|
final Document? currentDocument;
|
||||||
|
final Category? currentCategory;
|
||||||
|
final Uri? uri;
|
||||||
|
final int? count;
|
||||||
|
final dynamic page;
|
||||||
|
final Query? query;
|
||||||
|
|
||||||
|
const DocumentPageState({
|
||||||
|
this.query,
|
||||||
|
this.count,
|
||||||
|
this.page,
|
||||||
|
this.uri,
|
||||||
|
this.currentDocument,
|
||||||
|
this.isCategorySelected = false,
|
||||||
|
this.currentCategory,
|
||||||
|
this.isDocumentSelected = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
DocumentPageState copyWith({
|
||||||
|
Uri? uri,
|
||||||
|
Query? query,
|
||||||
|
int? count,
|
||||||
|
dynamic page,
|
||||||
|
List<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,
|
||||||
|
//
|
||||||
|
currentDocument: currentDocument ?? this.currentDocument,
|
||||||
|
isDocumentSelected: isDocumentSelected ?? this.isDocumentSelected,
|
||||||
|
//
|
||||||
|
currentCategory: currentCategory ?? this.currentCategory,
|
||||||
|
isCategorySelected: isCategorySelected ?? this.isCategorySelected,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// -----------------------------------------------
|
||||||
|
/// [Screens]
|
||||||
|
/// -----------------------------------------------
|
||||||
|
|
||||||
|
/// [DocumentManagerScreen]
|
||||||
|
|
||||||
|
class DocumentManagerScreen extends StatelessScreen {
|
||||||
|
final DocumentPageModel model;
|
||||||
|
final DocumentPageState state;
|
||||||
|
|
||||||
|
const DocumentManagerScreen({
|
||||||
|
super.key,
|
||||||
|
required this.model,
|
||||||
|
required this.state,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final String title = FFLocalizations.of(context).getVariableText(
|
||||||
|
enText: 'Documents',
|
||||||
|
ptText: 'Documentos',
|
||||||
|
);
|
||||||
|
final theme = FlutterFlowTheme.of(context);
|
||||||
|
action() => Navigator.pop(context);
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: theme.primaryBackground,
|
||||||
|
appBar: buildAppBar(title, context, action),
|
||||||
|
body: buildBody(context),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget buildBody(BuildContext context) {
|
||||||
|
log('Build -> DocumentManagerScreen');
|
||||||
|
final SizedBox space = SizedBox(height: 30);
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: EnhancedListView<Document, Category, Null>(
|
||||||
|
key: model.vehicleScreenManager,
|
||||||
|
headerBuilder: model.itemHeaderBuilder<Category>,
|
||||||
|
headerItems: model.generateHeaderItems<Category>,
|
||||||
|
bodyBuilder: model.itemBodyBuilder<Document>,
|
||||||
|
bodyItems: model.generateBodyItems<Document>,
|
||||||
|
footerBuilder: null,
|
||||||
|
footerItems: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
] //
|
||||||
|
.addToStart(space)
|
||||||
|
.addToEnd(space),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [DocumentViewScreen]
|
||||||
|
|
||||||
|
class DocumentViewScreen extends StatefulScreen {
|
||||||
|
const DocumentViewScreen({
|
||||||
|
super.key,
|
||||||
|
required this.doc,
|
||||||
|
required this.uri,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Document doc;
|
||||||
|
final Uri uri;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ScreenState<DocumentViewScreen> createState() => _DocumentViewScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DocumentViewScreenState extends ScreenState<DocumentViewScreen> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
action() {
|
||||||
|
context.read<DocumentPageBloc>().add(UnselectDocumentEvent());
|
||||||
|
}
|
||||||
|
|
||||||
|
final String title = widget.doc.description;
|
||||||
|
final theme = FlutterFlowTheme.of(context);
|
||||||
|
return PopScope(
|
||||||
|
canPop: false,
|
||||||
|
onPopInvokedWithResult: (didPop, result) => action(),
|
||||||
|
child: Scaffold(
|
||||||
|
backgroundColor: theme.primaryBackground,
|
||||||
|
appBar: buildAppBar(title, context, action),
|
||||||
|
body: buildBody(context),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget buildBody(BuildContext context) {
|
||||||
|
// final PDFViewerKey _viewerKey = PDFViewerKey();
|
||||||
|
|
||||||
|
return ReadView(
|
||||||
|
// search: _viewerKey,
|
||||||
|
title: widget.doc.description,
|
||||||
|
url: widget.uri.toString(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// -----------------------------------------------
|
||||||
|
/// [Interfaces]
|
||||||
|
/// -----------------------------------------------
|
||||||
|
|
||||||
|
abstract interface class Archive extends Entity {}
|
||||||
|
|
||||||
|
interface class Document extends Archive {
|
||||||
|
final int id;
|
||||||
|
final String description;
|
||||||
|
final String type;
|
||||||
|
final Category category;
|
||||||
|
final String person;
|
||||||
|
final String property;
|
||||||
|
String createdAt;
|
||||||
|
String updatedAt;
|
||||||
|
|
||||||
|
Document({
|
||||||
|
required this.id,
|
||||||
|
required this.description,
|
||||||
|
required this.type,
|
||||||
|
required this.category,
|
||||||
|
required this.person,
|
||||||
|
required this.property,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.updatedAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Document.fromDesc(String desc) => Document(
|
||||||
|
id: 0,
|
||||||
|
description: desc,
|
||||||
|
type: '',
|
||||||
|
category: Category.fromDesc(''),
|
||||||
|
person: '',
|
||||||
|
property: '',
|
||||||
|
createdAt: '',
|
||||||
|
updatedAt: '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface class Category extends Archive {
|
||||||
|
final int id;
|
||||||
|
final Color color;
|
||||||
|
final String title;
|
||||||
|
|
||||||
|
Category({
|
||||||
|
required this.id,
|
||||||
|
required this.color,
|
||||||
|
required this.title,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Category.fromDesc(String desc) {
|
||||||
|
return Category(
|
||||||
|
id: 0,
|
||||||
|
color: Colors.transparent,
|
||||||
|
title: desc,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Color isSelected() => Colors.black;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// -----------------------------------------------
|
||||||
|
/// [Widgets]
|
||||||
|
/// -----------------------------------------------
|
||||||
|
|
||||||
|
// ignore: must_be_immutable
|
||||||
|
class DocumentItem extends StatelessComponent {
|
||||||
|
final Document document;
|
||||||
|
void Function(Document, BuildContext) onPressed;
|
||||||
|
|
||||||
|
DocumentItem({
|
||||||
|
super.key,
|
||||||
|
required this.document,
|
||||||
|
required this.onPressed,
|
||||||
|
});
|
||||||
|
|
||||||
|
Tooltip _buildTooltip(String text, Color color, BuildContext context,
|
||||||
|
BoxConstraints constraints) {
|
||||||
|
final Color textColor = FlutterFlowTheme.of(context).info;
|
||||||
|
|
||||||
|
final area = (MediaQuery.of(context).size.height +
|
||||||
|
MediaQuery.of(context).size.width) /
|
||||||
|
2;
|
||||||
|
|
||||||
|
final double boxHeight = area * 0.033;
|
||||||
|
final double boxWidth = area * 0.19;
|
||||||
|
|
||||||
|
return Tooltip(
|
||||||
|
message: text,
|
||||||
|
child: Container(
|
||||||
|
width: boxWidth,
|
||||||
|
height: boxHeight,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: color,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: AutoText(
|
||||||
|
text,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
color: textColor,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final Color primaryText = FlutterFlowTheme.of(context).primaryText;
|
||||||
|
final Color primaryColor = FlutterFlowTheme.of(context).primary;
|
||||||
|
|
||||||
|
final TextStyle textStyleMajor = TextStyle(
|
||||||
|
color: primaryText,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
);
|
||||||
|
final TextStyle textStyleMinor = TextStyle(
|
||||||
|
color: primaryText,
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
fontStyle: FontStyle.italic,
|
||||||
|
);
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
final double boxHeight = constraints.maxHeight > 350
|
||||||
|
? MediaQuery.of(context).size.height * 0.07
|
||||||
|
: MediaQuery.of(context).size.height * 2;
|
||||||
|
|
||||||
|
return InkWell(
|
||||||
|
onTap: () => onPressed(document, context),
|
||||||
|
enableFeedback: true,
|
||||||
|
overlayColor: WidgetStateProperty.all<Color>(primaryColor),
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
child: SizedBox(
|
||||||
|
height: boxHeight,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
// const SizedBox(width: 10),
|
||||||
|
Icon(Icons.description, color: document.category.color),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Tooltip(
|
||||||
|
message: document.description,
|
||||||
|
child: AutoText(
|
||||||
|
document.description,
|
||||||
|
style: textStyleMajor,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
AutoText(
|
||||||
|
ValidatorUtil.toLocalDateTime(
|
||||||
|
'yyyy-MM-dd', document.updatedAt),
|
||||||
|
style: textStyleMinor,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
_buildTooltip(
|
||||||
|
document.category.title,
|
||||||
|
document.category.color,
|
||||||
|
context,
|
||||||
|
constraints,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// const SizedBox(width: 10),
|
||||||
|
Center(
|
||||||
|
child: Icon(
|
||||||
|
Icons.arrow_right,
|
||||||
|
color: primaryText,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
DocumentItem copyWith({
|
||||||
|
Document? document,
|
||||||
|
}) {
|
||||||
|
return DocumentItem(
|
||||||
|
document: document ?? this.document,
|
||||||
|
onPressed: onPressed,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CategoryItem extends StatelessComponent {
|
||||||
|
final Category category;
|
||||||
|
|
||||||
|
const CategoryItem({
|
||||||
|
super.key,
|
||||||
|
required this.category,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final backgroundTheme = FlutterFlowTheme.of(context).primaryBackground;
|
||||||
|
return ColoredBox(
|
||||||
|
color: backgroundTheme,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: category.color,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
Icons.folder,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 40,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
category.title,
|
||||||
|
style: TextStyle(
|
||||||
|
color: category.color,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,24 +0,0 @@
|
||||||
import 'dart:developer';
|
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'package:hub/features/backend/index.dart';
|
|
||||||
import 'package:hub/flutter_flow/index.dart';
|
|
||||||
import 'package:hub/shared/extensions/index.dart';
|
|
||||||
import 'package:hub/shared/mixins/pegeable_mixin.dart';
|
|
||||||
import 'package:hub/shared/utils/index.dart';
|
|
||||||
import 'package:hub/shared/widgets/widgets.dart';
|
|
||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
|
||||||
import 'package:share_plus/share_plus.dart';
|
|
||||||
|
|
||||||
part 'document_page_widget.dart';
|
|
||||||
part 'document_screen_manager.dart';
|
|
||||||
part 'document_screen_viewer.dart';
|
|
||||||
part 'document_page_model.dart';
|
|
||||||
part 'document_item_component.dart';
|
|
||||||
part 'document_page_bloc.dart';
|
|
||||||
part 'category_item_component.dart';
|
|
||||||
part 'archive_item_component.dart';
|
|
|
@ -4,7 +4,7 @@ import 'dart:io';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:hub/features/backend/index.dart';
|
import 'package:hub/features/backend/index.dart';
|
||||||
import 'package:hub/features/documents/index.dart';
|
import 'package:hub/features/documents/documents.dart';
|
||||||
import 'package:hub/features/history/index.dart';
|
import 'package:hub/features/history/index.dart';
|
||||||
import 'package:hub/features/home/index.dart';
|
import 'package:hub/features/home/index.dart';
|
||||||
import 'package:hub/features/local/index.dart';
|
import 'package:hub/features/local/index.dart';
|
||||||
|
|
|
@ -179,117 +179,50 @@ class EnhancedListView<BodyType, HeaderType, FooterType>
|
||||||
|
|
||||||
class EnhancedListViewState<ItemType, HeaderType, FooterType>
|
class EnhancedListViewState<ItemType, HeaderType, FooterType>
|
||||||
extends State<EnhancedListView<ItemType, HeaderType, FooterType>> {
|
extends State<EnhancedListView<ItemType, HeaderType, FooterType>> {
|
||||||
final ScrollController _scrollController = ScrollController();
|
late EnhancedListViewBloc<ItemType, HeaderType, FooterType> bloc;
|
||||||
bool _isLoadingMore = false;
|
|
||||||
List<ItemType?> items = [];
|
|
||||||
List<HeaderType?> headerItems = [];
|
|
||||||
List<FooterType?> footerItems = [];
|
|
||||||
int currentPage = 1;
|
|
||||||
Query<ItemType> query;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_scrollController.addListener(_onScroll);
|
bloc = EnhancedListViewBloc<ItemType, HeaderType, FooterType>(
|
||||||
_loadBodyItems();
|
bodyItemsBuilder: widget.bodyItems,
|
||||||
_loadHeaderItems();
|
headerItemsBuilder: widget.headerItems ?? () async => [],
|
||||||
_loadFooterItems();
|
footerItemsBuilder: widget.footerItems ?? () async => [],
|
||||||
}
|
);
|
||||||
|
bloc.events.loadBodyItems();
|
||||||
void _onScroll() {
|
bloc.events.loadHeaderItems();
|
||||||
if (_scrollController.position.pixels ==
|
bloc.events.loadFooterItems();
|
||||||
_scrollController.position.maxScrollExtent &&
|
|
||||||
!_isLoadingMore) {
|
|
||||||
_loadMoreBodyItems();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _loadBodyItems() async {
|
|
||||||
log('loadInitialItems');
|
|
||||||
final newItems = await widget.bodyItems(1, 10, query);
|
|
||||||
setState(() {
|
|
||||||
items = newItems;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _loadMoreBodyItems() async {
|
|
||||||
log('loadMoreItems');
|
|
||||||
setState(() {
|
|
||||||
_isLoadingMore = true;
|
|
||||||
});
|
|
||||||
final newItems = await widget.bodyItems(currentPage + 1, 10, query);
|
|
||||||
setState(() {
|
|
||||||
_isLoadingMore = false;
|
|
||||||
if (newItems.isNotEmpty) {
|
|
||||||
items.addAll(newItems);
|
|
||||||
currentPage++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _loadHeaderItems() async {
|
|
||||||
if (widget.headerItems != null) {
|
|
||||||
log('loadHeaderItems');
|
|
||||||
final newHeaderItems = await widget.headerItems!();
|
|
||||||
setState(() {
|
|
||||||
headerItems = newHeaderItems;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _loadFooterItems() async {
|
|
||||||
if (widget.footerItems != null) {
|
|
||||||
log('loadFooterItems');
|
|
||||||
final newFooterItems = await widget.footerItems!();
|
|
||||||
setState(() {
|
|
||||||
footerItems = newFooterItems;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> filterBodyItems(Query<ItemType> newQuery) async {
|
|
||||||
log('filterItems B: ${newQuery.toString()}');
|
|
||||||
setState(() {
|
|
||||||
query = newQuery;
|
|
||||||
items = [];
|
|
||||||
currentPage = 1;
|
|
||||||
});
|
|
||||||
await _loadBodyItems();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
log('key: ${widget.key}');
|
return StreamBuilder<List<ItemType>>(
|
||||||
return ListView.builder(
|
stream: bloc.states.bodyItems.cast<List<ItemType>>(),
|
||||||
controller: _scrollController,
|
builder: (context, bodySnapshot) {
|
||||||
itemCount: items.length +
|
return StreamBuilder<List<HeaderType>>(
|
||||||
(headerItems.isNotEmpty ? 1 : 0) +
|
stream: bloc.states.headerItems.cast<List<HeaderType>>(),
|
||||||
(footerItems.isNotEmpty ? 1 : 0) +
|
builder: (context, headerSnapshot) {
|
||||||
(_isLoadingMore ? 1 : 0),
|
return StreamBuilder<List<FooterType>>(
|
||||||
itemBuilder: (context, index) {
|
stream: bloc.states.footerItems.cast<List<FooterType>>(),
|
||||||
if (headerItems.isNotEmpty && index == 0) {
|
builder: (context, footerSnapshot) {
|
||||||
return widget.headerBuilder!(() => Future.value(headerItems));
|
return ListView.builder(
|
||||||
}
|
itemCount: bodySnapshot.data?.length ?? 0,
|
||||||
if (footerItems.isNotEmpty &&
|
itemBuilder: (context, index) {
|
||||||
index == items.length + (headerItems.isNotEmpty ? 1 : 0)) {
|
return widget.bodyBuilder(
|
||||||
return widget.footerBuilder!(() => Future.value(footerItems));
|
context, bodySnapshot.data![index], index);
|
||||||
}
|
},
|
||||||
if (_isLoadingMore &&
|
);
|
||||||
index ==
|
},
|
||||||
items.length +
|
);
|
||||||
(headerItems.isNotEmpty ? 1 : 0) +
|
},
|
||||||
(footerItems.isNotEmpty ? 1 : 0)) {
|
);
|
||||||
return const EnhancedProgressIndicator();
|
|
||||||
}
|
|
||||||
final item = items[index - (headerItems.isNotEmpty ? 1 : 0)];
|
|
||||||
return widget.bodyBuilder(context, item as ItemType, index);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_scrollController.dispose();
|
bloc.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -358,7 +291,7 @@ class AuthorizationError implements Exception {
|
||||||
AuthorizationError(this.message);
|
AuthorizationError(this.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [State Managment]
|
/// [State Management]
|
||||||
Stream<bool> get loadingState => Stream<bool>.empty();
|
Stream<bool> get loadingState => Stream<bool>.empty();
|
||||||
Stream<Exception> get errorState => Stream<Exception>.empty();
|
Stream<Exception> get errorState => Stream<Exception>.empty();
|
||||||
|
|
||||||
|
@ -367,111 +300,108 @@ abstract class EnhancedListViewRepository<T> {
|
||||||
int page, int pageSize, PaginatedListViewBodyBuilder<T> builder);
|
int page, int pageSize, PaginatedListViewBodyBuilder<T> builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class EnhancedListViewBlocStates<T> {
|
abstract class EnhancedListViewEvents<T, H, F> {
|
||||||
|
void loadBodyItems({bool reset = false});
|
||||||
|
void loadHeaderItems();
|
||||||
|
void loadFooterItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class EnhancedListViewStates<T, H, F> {
|
||||||
|
Stream<List<T>> get bodyItems;
|
||||||
|
Stream<List<H>> get headerItems;
|
||||||
|
Stream<List<F>> get footerItems;
|
||||||
Stream<bool> get isLoading;
|
Stream<bool> get isLoading;
|
||||||
Stream<String> get errors;
|
Stream<String> get errors;
|
||||||
Stream<EnhancedPaginatedList<T>> get paginatedList;
|
|
||||||
|
|
||||||
@RxBlocIgnoreState()
|
|
||||||
Future<void> get refreshDone;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class EnhancedListViewEvents<T> {
|
@RxBloc()
|
||||||
void loadPage({bool reset = false});
|
class EnhancedListViewBloc<T, H, F> extends $EnhancedListViewBloc<T, H, F> {
|
||||||
}
|
|
||||||
|
|
||||||
abstract class EnhancedListViewBlocType<T> extends RxBlocTypeBase {
|
|
||||||
EnhancedListViewEvents<T> get events;
|
|
||||||
EnhancedListViewBlocStates<T> get states;
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class $EnhancedListViewBloc<T> extends RxBlocBase
|
|
||||||
implements
|
|
||||||
EnhancedListViewEvents<T>,
|
|
||||||
EnhancedListViewBlocStates<T>,
|
|
||||||
EnhancedListViewBlocType<T> {
|
|
||||||
final _compositeSubscription = CompositeSubscription();
|
|
||||||
|
|
||||||
final _$loadPageEvent = PublishSubject<bool>();
|
|
||||||
|
|
||||||
late final Stream<bool> _isLoadingState = _mapToIsLoadingState();
|
|
||||||
late final Stream<String> _errorsState = _mapToErrorsState();
|
|
||||||
late final Stream<EnhancedPaginatedList<T>> _paginatedListState =
|
|
||||||
_mapToPaginatedListState();
|
|
||||||
|
|
||||||
@override
|
|
||||||
void loadPage({bool reset = false}) => _$loadPageEvent.add(reset);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Stream<bool> get isLoading => _isLoadingState;
|
|
||||||
@override
|
|
||||||
Stream<String> get errors => _errorsState;
|
|
||||||
@override
|
|
||||||
Stream<EnhancedPaginatedList<T>> get paginatedList => _paginatedListState;
|
|
||||||
|
|
||||||
Stream<bool> _mapToIsLoadingState();
|
|
||||||
Stream<String> _mapToErrorsState();
|
|
||||||
Stream<EnhancedPaginatedList<T>> _mapToPaginatedListState();
|
|
||||||
|
|
||||||
@override
|
|
||||||
EnhancedListViewEvents<T> get events => this;
|
|
||||||
@override
|
|
||||||
EnhancedListViewBlocStates<T> get states => this;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_$loadPageEvent.close();
|
|
||||||
_compositeSubscription.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class EnhancedListViewBloc<T> extends $EnhancedListViewBloc<T> {
|
|
||||||
EnhancedListViewBloc({
|
EnhancedListViewBloc({
|
||||||
required EnhancedListViewRepository<T> repository,
|
required this.bodyItemsBuilder,
|
||||||
required PaginatedListViewBodyBuilder<T> builder,
|
required this.headerItemsBuilder,
|
||||||
required T item,
|
required this.footerItemsBuilder,
|
||||||
int initialPageSize = 50,
|
|
||||||
}) {
|
}) {
|
||||||
_$loadPageEvent
|
_$loadBodyItemsEvent
|
||||||
.startWith(true)
|
.startWith(true)
|
||||||
.fetchData(
|
.switchMap((reset) => _fetchBodyItems(reset))
|
||||||
repository,
|
.bind(_bodyItems)
|
||||||
(context, item, index) => builder(context, item, index),
|
.addTo(_compositeSubscription);
|
||||||
_paginatedList,
|
|
||||||
)
|
_$loadHeaderItemsEvent
|
||||||
.setResultStateHandler(this)
|
.switchMap((_) => _fetchHeaderItems())
|
||||||
.mergeWithPaginatedList(_paginatedList)
|
.bind(_headerItems)
|
||||||
.bind(_paginatedList)
|
.addTo(_compositeSubscription);
|
||||||
|
|
||||||
|
_$loadFooterItemsEvent
|
||||||
|
.switchMap((_) => _fetchFooterItems())
|
||||||
|
.bind(_footerItems)
|
||||||
.addTo(_compositeSubscription);
|
.addTo(_compositeSubscription);
|
||||||
}
|
}
|
||||||
|
|
||||||
final _paginatedList = BehaviorSubject<EnhancedPaginatedList<T>>.seeded(
|
final BodyItemsBuilder<T> bodyItemsBuilder;
|
||||||
EnhancedPaginatedList<T>(
|
final HeaderItemsBuilder<H> headerItemsBuilder;
|
||||||
items: [],
|
final FooterItemsBuilder<F> footerItemsBuilder;
|
||||||
pageSize: 1,
|
|
||||||
currentPage: 1,
|
|
||||||
error: Exception(),
|
|
||||||
isInitialized: true,
|
|
||||||
isLoading: false,
|
|
||||||
totalCount: 0,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
final _bodyItems = BehaviorSubject<List<T>>.seeded([]);
|
||||||
Future<void> get refreshDone async => _paginatedList.value.awaitLoad();
|
final _headerItems = BehaviorSubject<List<H>>.seeded([]);
|
||||||
|
final _footerItems = BehaviorSubject<List<F>>.seeded([]);
|
||||||
|
final _isLoading = BehaviorSubject<bool>.seeded(false);
|
||||||
|
final _errors = BehaviorSubject<String>();
|
||||||
|
|
||||||
@override
|
Stream<List<T>> _fetchBodyItems(bool reset) async* {
|
||||||
Stream<EnhancedPaginatedList<T>> _mapToPaginatedListState() => _paginatedList;
|
try {
|
||||||
@override
|
_isLoading.add(true);
|
||||||
Stream<String> _mapToErrorsState() =>
|
final items = await bodyItemsBuilder(1, 10, null);
|
||||||
errorState.map((error) => error.toString());
|
yield items.whereType<T>().toList();
|
||||||
@override
|
} catch (e) {
|
||||||
Stream<bool> _mapToIsLoadingState() => loadingState;
|
_errors.add(e.toString());
|
||||||
|
} finally {
|
||||||
|
_isLoading.add(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<List<H>> _fetchHeaderItems() async* {
|
||||||
|
try {
|
||||||
|
_isLoading.add(true);
|
||||||
|
final items = await headerItemsBuilder();
|
||||||
|
yield items.whereType<H>().toList();
|
||||||
|
} catch (e) {
|
||||||
|
_errors.add(e.toString());
|
||||||
|
} finally {
|
||||||
|
_isLoading.add(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<List<F>> _fetchFooterItems() async* {
|
||||||
|
try {
|
||||||
|
_isLoading.add(true);
|
||||||
|
final items = await footerItemsBuilder();
|
||||||
|
yield items.whereType<F>().toList();
|
||||||
|
} catch (e) {
|
||||||
|
_errors.add(e.toString());
|
||||||
|
} finally {
|
||||||
|
_isLoading.add(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_paginatedList.close();
|
_bodyItems.close();
|
||||||
|
_headerItems.close();
|
||||||
|
_footerItems.close();
|
||||||
|
_isLoading.close();
|
||||||
|
_errors.close();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<List<T>> _mapToBodyItemsState() => _bodyItems.stream;
|
||||||
|
@override
|
||||||
|
Stream<String> _mapToErrorsState() => _errors.stream;
|
||||||
|
@override
|
||||||
|
Stream<List<F>> _mapToFooterItemsState() => _footerItems.stream;
|
||||||
|
@override
|
||||||
|
Stream<List<H>> _mapToHeaderItemsState() => _headerItems.stream;
|
||||||
|
@override
|
||||||
|
Stream<bool> _mapToIsLoadingState() => _isLoading.stream;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,10 @@ import 'dart:developer';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:auto_size_text/auto_size_text.dart';
|
import 'package:auto_size_text/auto_size_text.dart';
|
||||||
import 'package:easy_debounce/easy_debounce.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:hub/features/documents/index.dart';
|
|
||||||
import 'package:hub/flutter_flow/index.dart';
|
import 'package:hub/flutter_flow/index.dart';
|
||||||
import 'package:hub/shared/mixins/pegeable_mixin.dart';
|
|
||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:pdfx/pdfx.dart';
|
import 'package:pdfx/pdfx.dart';
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
@ -17,6 +13,8 @@ import 'package:rx_bloc_list/rx_bloc_list.dart';
|
||||||
import 'package:rxdart/rxdart.dart';
|
import 'package:rxdart/rxdart.dart';
|
||||||
import 'package:rx_bloc/rx_bloc.dart';
|
import 'package:rx_bloc/rx_bloc.dart';
|
||||||
|
|
||||||
|
part 'widgets.rxb.g.dart';
|
||||||
|
|
||||||
/// [Base]
|
/// [Base]
|
||||||
part 'page.dart';
|
part 'page.dart';
|
||||||
part 'component.dart';
|
part 'component.dart';
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// Generator: RxBlocGeneratorForAnnotation
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
part of 'widgets.dart';
|
||||||
|
|
||||||
|
/// Used as a contractor for the bloc, events and states classes
|
||||||
|
/// @nodoc
|
||||||
|
abstract class EnhancedListViewBlocType extends RxBlocTypeBase {
|
||||||
|
EnhancedListViewEvents get events;
|
||||||
|
EnhancedListViewStates get states;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [$EnhancedListViewBloc<T, H, F>] extended by the [EnhancedListViewBloc<T, H, F>]
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $EnhancedListViewBloc<T, H, F> extends RxBlocBase
|
||||||
|
implements
|
||||||
|
EnhancedListViewEvents,
|
||||||
|
EnhancedListViewStates,
|
||||||
|
EnhancedListViewBlocType {
|
||||||
|
final _compositeSubscription = CompositeSubscription();
|
||||||
|
|
||||||
|
/// Тhe [Subject] where events sink to by calling [loadBodyItems]
|
||||||
|
final _$loadBodyItemsEvent = PublishSubject<bool>();
|
||||||
|
|
||||||
|
/// Тhe [Subject] where events sink to by calling [loadHeaderItems]
|
||||||
|
final _$loadHeaderItemsEvent = PublishSubject<void>();
|
||||||
|
|
||||||
|
/// Тhe [Subject] where events sink to by calling [loadFooterItems]
|
||||||
|
final _$loadFooterItemsEvent = PublishSubject<void>();
|
||||||
|
|
||||||
|
/// The state of [bodyItems] implemented in [_mapToBodyItemsState]
|
||||||
|
late final Stream<List<T>> _bodyItemsState = _mapToBodyItemsState();
|
||||||
|
|
||||||
|
/// The state of [headerItems] implemented in [_mapToHeaderItemsState]
|
||||||
|
late final Stream<List<H>> _headerItemsState = _mapToHeaderItemsState();
|
||||||
|
|
||||||
|
/// The state of [footerItems] implemented in [_mapToFooterItemsState]
|
||||||
|
late final Stream<List<F>> _footerItemsState = _mapToFooterItemsState();
|
||||||
|
|
||||||
|
/// The state of [isLoading] implemented in [_mapToIsLoadingState]
|
||||||
|
late final Stream<bool> _isLoadingState = _mapToIsLoadingState();
|
||||||
|
|
||||||
|
/// The state of [errors] implemented in [_mapToErrorsState]
|
||||||
|
late final Stream<String> _errorsState = _mapToErrorsState();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void loadBodyItems({bool reset = false}) => _$loadBodyItemsEvent.add(reset);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void loadHeaderItems() => _$loadHeaderItemsEvent.add(null);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void loadFooterItems() => _$loadFooterItemsEvent.add(null);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<List<T>> get bodyItems => _bodyItemsState;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<List<H>> get headerItems => _headerItemsState;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<List<F>> get footerItems => _footerItemsState;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<bool> get isLoading => _isLoadingState;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<String> get errors => _errorsState;
|
||||||
|
|
||||||
|
Stream<List<T>> _mapToBodyItemsState();
|
||||||
|
|
||||||
|
Stream<List<H>> _mapToHeaderItemsState();
|
||||||
|
|
||||||
|
Stream<List<F>> _mapToFooterItemsState();
|
||||||
|
|
||||||
|
Stream<bool> _mapToIsLoadingState();
|
||||||
|
|
||||||
|
Stream<String> _mapToErrorsState();
|
||||||
|
|
||||||
|
@override
|
||||||
|
EnhancedListViewEvents get events => this;
|
||||||
|
|
||||||
|
@override
|
||||||
|
EnhancedListViewStates get states => this;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_$loadBodyItemsEvent.close();
|
||||||
|
_$loadHeaderItemsEvent.close();
|
||||||
|
_$loadFooterItemsEvent.close();
|
||||||
|
_compositeSubscription.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue