WIP
This commit is contained in:
parent
e2b0f7538d
commit
963ccb64db
|
@ -1,19 +1,27 @@
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:easy_debounce/easy_debounce.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:hub/components/templates_components/details_component/details_component_widget.dart';
|
||||||
import 'package:hub/features/backend/index.dart';
|
import 'package:hub/features/backend/index.dart';
|
||||||
import 'package:hub/flutter_flow/index.dart';
|
import 'package:hub/flutter_flow/index.dart';
|
||||||
import 'package:hub/shared/extensions/index.dart';
|
import 'package:hub/shared/extensions/index.dart';
|
||||||
import 'package:hub/shared/utils/index.dart';
|
import 'package:hub/shared/utils/index.dart';
|
||||||
import 'package:hub/shared/widgets/widgets.dart';
|
import 'package:hub/shared/widgets/widgets.dart';
|
||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flutter_rx_bloc/flutter_rx_bloc.dart';
|
||||||
|
|
||||||
|
import 'package:rx_bloc/rx_bloc.dart';
|
||||||
|
import 'package:rxdart/rxdart.dart' as rx;
|
||||||
|
|
||||||
|
part 'documents.rxb.g.dart';
|
||||||
|
|
||||||
/// -----------------------------------------------
|
/// -----------------------------------------------
|
||||||
/// [TypeDefs]
|
/// [TypeDefs]
|
||||||
/// -----------------------------------------------
|
/// -----------------------------------------------
|
||||||
|
|
||||||
typedef DocumentKey = GlobalKey<FREDocumentPageState>;
|
typedef DocumentKey = GlobalKey<DocumentPageState>;
|
||||||
|
|
||||||
/// -----------------------------------------------
|
/// -----------------------------------------------
|
||||||
|
|
||||||
|
@ -24,43 +32,54 @@ class DocumentPage extends StatefulPage {
|
||||||
const DocumentPage({super.key});
|
const DocumentPage({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<DocumentPage> createState() => FREDocumentPageState();
|
State<DocumentPage> createState() => DocumentPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class FREDocumentPageState<T extends DocumentPage>
|
class DocumentPageState extends PageState<DocumentPage> {
|
||||||
extends PageState<DocumentPage> {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) => buildBody(context);
|
|
||||||
DocumentPageModel model = DocumentPageModel();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
model.initState(context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildBody(BuildContext context) {
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
log('Build -> DocumentPage');
|
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 RxBlocMultiBuilder2<DocumentPageBlocType, bool, (Document, Uri)?>(
|
||||||
return DocumentViewScreen(
|
state1: (bloc) => bloc.states.isDocumentSelected,
|
||||||
doc: state.currentDocument!,
|
state2: (bloc) => bloc.states.currentDocument,
|
||||||
uri: state.uri!,
|
bloc: context.read<DocumentPageBloc>(),
|
||||||
);
|
builder: (context, isSelect, current, bloc) {
|
||||||
else
|
log('-> Build -> DocumentPage -> RxBlocMultiBuilder2');
|
||||||
return DocumentManagerScreen(
|
if (isSelect.hasData && isSelect.data!) {
|
||||||
model: model,
|
return _buildDocumentViewScreen(current, bloc);
|
||||||
state: state,
|
} else {
|
||||||
);
|
return _buildDocumentManagerScreen();
|
||||||
}),
|
}
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildDocumentManagerScreen() {
|
||||||
|
final model = context.read<DocumentPageBloc>().model;
|
||||||
|
|
||||||
|
return DocumentManagerScreen(
|
||||||
|
model: model,
|
||||||
|
state: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildDocumentViewScreen(
|
||||||
|
AsyncSnapshot<(Document, Uri)?> snapshot, DocumentPageBlocType bloc) {
|
||||||
|
if (snapshot.hasData) {
|
||||||
|
return DocumentViewScreen(
|
||||||
|
doc: snapshot.data!,
|
||||||
|
bloc: bloc,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// -----------------------------------------------
|
/// -----------------------------------------------
|
||||||
|
@ -69,27 +88,26 @@ class FREDocumentPageState<T extends DocumentPage>
|
||||||
/// -----------------------------------------------
|
/// -----------------------------------------------
|
||||||
|
|
||||||
class DocumentPageModel extends FlutterFlowModel<DocumentPage> {
|
class DocumentPageModel extends FlutterFlowModel<DocumentPage> {
|
||||||
DocumentPageModel._privateConstructor();
|
final DocumentPageBlocType bloc;
|
||||||
|
DocumentPageModel(this.bloc);
|
||||||
|
|
||||||
static final DocumentPageModel _instance =
|
late EnhancedListViewKey<Document, Search, Category, Query>
|
||||||
DocumentPageModel._privateConstructor();
|
vehicleScreenManager;
|
||||||
|
|
||||||
factory DocumentPageModel() {
|
|
||||||
return _instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
late EnhancedListViewKey<Document, Category, Null> vehicleScreenManager;
|
|
||||||
late DocumentKey vehicleScreenViewer;
|
late DocumentKey vehicleScreenViewer;
|
||||||
late PagingController<int, Document> _pagingController;
|
late PagingController<int, Document> _pagingController;
|
||||||
|
|
||||||
|
late bool categoryIsSelected;
|
||||||
|
|
||||||
/// ------------
|
/// ------------
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState(BuildContext context) {
|
void initState(BuildContext context) {
|
||||||
vehicleScreenManager = EnhancedListViewKey<Document, Category, Null>();
|
vehicleScreenManager =
|
||||||
|
EnhancedListViewKey<Document, Search, Category, Query>();
|
||||||
vehicleScreenViewer = DocumentKey();
|
vehicleScreenViewer = DocumentKey();
|
||||||
|
|
||||||
_pagingController = PagingController<int, Document>(firstPageKey: 1);
|
_pagingController = PagingController<int, Document>(firstPageKey: 1);
|
||||||
|
|
||||||
|
categoryIsSelected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -101,58 +119,25 @@ class DocumentPageModel extends FlutterFlowModel<DocumentPage> {
|
||||||
|
|
||||||
/// ------------
|
/// ------------
|
||||||
|
|
||||||
/// [onView]
|
/// [Body]
|
||||||
|
|
||||||
void onView(Document document, BuildContext context) async {
|
void onView(Document document, BuildContext context) async {
|
||||||
vehicleScreenManager.currentContext!
|
log('Disparando evento selectDocument');
|
||||||
.read<DocumentPageBloc>()
|
bloc.events.selectDocument(document);
|
||||||
.add(SelectDocumentEvent(document));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [itemBodyBuilder]
|
Widget itemBodyBuilder<T extends Document>(
|
||||||
DocumentItem itemBodyBuilder<T extends Document>(
|
|
||||||
BuildContext context, T item, int index) {
|
BuildContext context, T item, int index) {
|
||||||
print('ItemBuilder -> $index');
|
print('ItemBuilder -> $index');
|
||||||
|
|
||||||
return DocumentItem(
|
return DocumentItem(
|
||||||
document: item,
|
document: item,
|
||||||
onPressed: onView,
|
onPressed: onView,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
CategoryItem categoryItemBuilder<T>(T? item) {
|
Future<List<T?>> generateBodyItems<T, Q>(
|
||||||
return CategoryItem(category: item! as Category);
|
int pageKey, int pageSize, Q query) async {
|
||||||
}
|
|
||||||
|
|
||||||
/// [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');
|
log('generateDocuments: $query');
|
||||||
|
|
||||||
final List<T?> error = [null];
|
final List<T?> error = [null];
|
||||||
|
@ -201,8 +186,42 @@ class DocumentPageModel extends FlutterFlowModel<DocumentPage> {
|
||||||
return docs as List<T?>;
|
return docs as List<T?>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [generateHeaderItems]
|
/// [Footer]
|
||||||
Future<List<T?>> generateHeaderItems<T>() async {
|
|
||||||
|
CategoryItem categoryItemBuilder<T>(T? item) {
|
||||||
|
return CategoryItem(category: item! as Category);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget itemFooterBuilder<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(
|
||||||
|
ptText: 'Suas Categorias',
|
||||||
|
enText: 'Your Categories',
|
||||||
|
),
|
||||||
|
style: TextStyle(
|
||||||
|
color: FlutterFlowTheme.of(context).primaryText,
|
||||||
|
fontSize: LimitedFontSizeUtil.getHeaderFontSize(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
EnhancedCarouselView<T>(
|
||||||
|
generateItems: gen,
|
||||||
|
itemBuilder: categoryItemBuilder,
|
||||||
|
filter: filterByCategory<T>,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<List<T?>> generateFooterItems<T>() async {
|
||||||
log('generateCategories: ');
|
log('generateCategories: ');
|
||||||
final List<T?> error = [null];
|
final List<T?> error = [null];
|
||||||
|
|
||||||
|
@ -229,12 +248,105 @@ class DocumentPageModel extends FlutterFlowModel<DocumentPage> {
|
||||||
return cats as List<T?>;
|
return cats as List<T?>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [filter]
|
/// [Header]
|
||||||
void filter<T>(T query, BuildContext context) {
|
|
||||||
vehicleScreenManager.currentState!.filterBodyItems(query);
|
Widget itemHeaderBuilder<T extends Search>(
|
||||||
|
Future<List<T?>> Function() generateHeaderItems) {
|
||||||
|
return Builder(builder: (context) {
|
||||||
|
final theme = FlutterFlowTheme.of(context);
|
||||||
|
final locale = FFLocalizations.of(context);
|
||||||
|
TextEditingController editingController = TextEditingController();
|
||||||
|
return TextFormField(
|
||||||
|
controller: editingController,
|
||||||
|
onChanged: (value) => EasyDebounce.debounce(
|
||||||
|
'_model.keyTextFieldTextController',
|
||||||
|
const Duration(milliseconds: 500),
|
||||||
|
() => filterBySearchBar(Document.fromDesc(value), context),
|
||||||
|
),
|
||||||
|
cursorColor: theme.primaryText,
|
||||||
|
showCursor: false,
|
||||||
|
cursorWidth: 2.0,
|
||||||
|
cursorRadius: Radius.circular(100),
|
||||||
|
style: TextStyle(
|
||||||
|
color: theme.primaryText,
|
||||||
|
fontSize: 16.0,
|
||||||
|
decorationColor: Colors.amber,
|
||||||
|
),
|
||||||
|
keyboardType: TextInputType.text,
|
||||||
|
textInputAction: TextInputAction.search,
|
||||||
|
autocorrect: true,
|
||||||
|
textCapitalization: TextCapitalization.sentences,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
prefixIcon: Icon(Icons.search, color: theme.primary),
|
||||||
|
labelText: locale.getVariableText(
|
||||||
|
ptText: 'Pesquisar',
|
||||||
|
enText: 'Search',
|
||||||
|
),
|
||||||
|
labelStyle: TextStyle(
|
||||||
|
color: theme.primaryText,
|
||||||
|
fontSize: 16.0,
|
||||||
|
),
|
||||||
|
hintText: locale.getVariableText(
|
||||||
|
ptText: 'Digite sua pesquisa',
|
||||||
|
enText: 'Enter your search',
|
||||||
|
),
|
||||||
|
hintStyle: TextStyle(
|
||||||
|
color: theme.accent2,
|
||||||
|
fontSize: 14.0,
|
||||||
|
),
|
||||||
|
filled: true,
|
||||||
|
fillColor: Colors.transparent,
|
||||||
|
helperStyle: TextStyle(
|
||||||
|
color: theme.primaryText,
|
||||||
|
decorationColor: theme.primaryText,
|
||||||
|
),
|
||||||
|
focusColor: theme.primaryText,
|
||||||
|
contentPadding:
|
||||||
|
EdgeInsets.symmetric(vertical: 10.0, horizontal: 15.0),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(15.0)),
|
||||||
|
borderSide: BorderSide(color: theme.primaryText),
|
||||||
|
),
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(15.0)),
|
||||||
|
borderSide: BorderSide(color: theme.primaryText),
|
||||||
|
),
|
||||||
|
errorBorder: UnderlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(15.0)),
|
||||||
|
borderSide: BorderSide(color: theme.primaryText),
|
||||||
|
),
|
||||||
|
focusedErrorBorder: UnderlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(15.0)),
|
||||||
|
borderSide: BorderSide(color: theme.primaryText, width: 2.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [onFetchError]
|
Future<List<T?>> generateHeaderItems<T extends Search>() async {
|
||||||
|
final Search item = Search();
|
||||||
|
return [item] as List<T?>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [Filter]
|
||||||
|
|
||||||
|
void filterBySearchBar<T>(T query, BuildContext context) {
|
||||||
|
final key = vehicleScreenManager.currentState;
|
||||||
|
return key?.filterBodyItems(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
void filterByCategory<T>(T query, BuildContext context) {
|
||||||
|
final key = vehicleScreenManager.currentState;
|
||||||
|
|
||||||
|
categoryIsSelected
|
||||||
|
? key?.filterBodyItems(null)
|
||||||
|
: key?.filterBodyItems(query);
|
||||||
|
|
||||||
|
categoryIsSelected = !categoryIsSelected;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [Exception]
|
||||||
void onFetchError(Object e, StackTrace s) {
|
void onFetchError(Object e, StackTrace s) {
|
||||||
DialogUtil.errorDefault(vehicleScreenViewer.currentContext!);
|
DialogUtil.errorDefault(vehicleScreenViewer.currentContext!);
|
||||||
LogUtil.requestAPIFailed(
|
LogUtil.requestAPIFailed(
|
||||||
|
@ -246,145 +358,45 @@ class DocumentPageModel extends FlutterFlowModel<DocumentPage> {
|
||||||
/// [BLoC]
|
/// [BLoC]
|
||||||
/// -----------------------------------------------
|
/// -----------------------------------------------
|
||||||
|
|
||||||
/// [DocumentPageBloc]
|
extension RxdartStartWithExtension<T> on Stream<T?> {
|
||||||
|
Stream<T?> rxdartStartWith(T? value) =>
|
||||||
class DocumentPageBloc extends Bloc<DocumentPageEvent, DocumentPageState> {
|
rx.StartWithExtension(this).startWith(value);
|
||||||
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 DocumentPageBlocEvents {
|
||||||
|
void selectDocument(Document document);
|
||||||
abstract class DocumentPageEvent {}
|
void unselectDocument();
|
||||||
|
|
||||||
class SelectDocumentEvent extends DocumentPageEvent {
|
|
||||||
final Document document;
|
|
||||||
SelectDocumentEvent(
|
|
||||||
this.document,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class UnselectDocumentEvent extends DocumentPageEvent {}
|
abstract class DocumentPageBlocStates {
|
||||||
|
Stream<bool> get isDocumentSelected;
|
||||||
class FilterCategoryEvent extends DocumentPageEvent {
|
Stream<(Document, Uri)?> get currentDocument;
|
||||||
final Query query;
|
|
||||||
FilterCategoryEvent(this.query);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [DocumentPageState]
|
@RxBloc()
|
||||||
|
class DocumentPageBloc extends $DocumentPageBloc {
|
||||||
|
late final DocumentPageModel model;
|
||||||
|
|
||||||
class DocumentPageState {
|
DocumentPageBloc(BuildContext context) {
|
||||||
final bool isCategorySelected;
|
model = DocumentPageModel(this);
|
||||||
final bool isDocumentSelected;
|
model.initState(context);
|
||||||
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,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<(Document, Uri)?> _mapToCurrentDocumentState() => _$selectDocumentEvent
|
||||||
|
.switchMap((event) async* {
|
||||||
|
log('Evento selectDocument recebido: ${event.description}');
|
||||||
|
final uri = await GetPDF().call(event.id);
|
||||||
|
yield (event, uri);
|
||||||
|
})
|
||||||
|
.rxdartStartWith(null)
|
||||||
|
.mergeWith([_$unselectDocumentEvent.map((_) => null)]);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<bool> _mapToIsDocumentSelectedState() => _mapToCurrentDocumentState()
|
||||||
|
.map((document) => document != null)
|
||||||
|
.rxdartStartWith(false)
|
||||||
|
.map((isSelected) => isSelected ?? false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// -----------------------------------------------
|
/// -----------------------------------------------
|
||||||
|
@ -425,14 +437,14 @@ class DocumentManagerScreen extends StatelessScreen {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: EnhancedListView<Document, Category, Null>(
|
child: EnhancedListView<Document, Search, Category, Query>(
|
||||||
key: model.vehicleScreenManager,
|
key: model.vehicleScreenManager,
|
||||||
headerBuilder: model.itemHeaderBuilder<Category>,
|
headerBuilder: model.itemHeaderBuilder<Search>,
|
||||||
headerItems: model.generateHeaderItems<Category>,
|
headerItems: model.generateHeaderItems<Search>,
|
||||||
bodyBuilder: model.itemBodyBuilder<Document>,
|
bodyBuilder: model.itemBodyBuilder<Document>,
|
||||||
bodyItems: model.generateBodyItems<Document>,
|
bodyItems: model.generateBodyItems<Document, Query>,
|
||||||
footerBuilder: null,
|
footerBuilder: model.itemFooterBuilder<Category>,
|
||||||
footerItems: null,
|
footerItems: model.generateFooterItems<Category>,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
] //
|
] //
|
||||||
|
@ -448,11 +460,11 @@ class DocumentViewScreen extends StatefulScreen {
|
||||||
const DocumentViewScreen({
|
const DocumentViewScreen({
|
||||||
super.key,
|
super.key,
|
||||||
required this.doc,
|
required this.doc,
|
||||||
required this.uri,
|
required this.bloc,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Document doc;
|
final (Document, Uri) doc;
|
||||||
final Uri uri;
|
final DocumentPageBlocType bloc;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ScreenState<DocumentViewScreen> createState() => _DocumentViewScreenState();
|
ScreenState<DocumentViewScreen> createState() => _DocumentViewScreenState();
|
||||||
|
@ -461,18 +473,59 @@ class DocumentViewScreen extends StatefulScreen {
|
||||||
class _DocumentViewScreenState extends ScreenState<DocumentViewScreen> {
|
class _DocumentViewScreenState extends ScreenState<DocumentViewScreen> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
action() {
|
final String title = widget.doc.$1.description;
|
||||||
context.read<DocumentPageBloc>().add(UnselectDocumentEvent());
|
|
||||||
}
|
|
||||||
|
|
||||||
final String title = widget.doc.description;
|
|
||||||
final theme = FlutterFlowTheme.of(context);
|
final theme = FlutterFlowTheme.of(context);
|
||||||
|
final locale = FFLocalizations.of(context);
|
||||||
|
|
||||||
|
backAction() => widget.bloc.events.unselectDocument();
|
||||||
|
infoAction() async => await showDialog(
|
||||||
|
useSafeArea: true,
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return Material(
|
||||||
|
child: DetailsComponentWidget(
|
||||||
|
buttons: [],
|
||||||
|
statusHashMap: [],
|
||||||
|
labelsHashMap: Map<String, String>.from({
|
||||||
|
locale.getVariableText(
|
||||||
|
enText: 'Description',
|
||||||
|
ptText: 'Descrição',
|
||||||
|
): widget.doc.$1.description,
|
||||||
|
locale.getVariableText(
|
||||||
|
enText: 'Type',
|
||||||
|
ptText: 'Tipo',
|
||||||
|
): widget.doc.$1.type,
|
||||||
|
locale.getVariableText(
|
||||||
|
enText: 'Category',
|
||||||
|
ptText: 'Categoria',
|
||||||
|
): widget.doc.$1.category.title,
|
||||||
|
locale.getVariableText(
|
||||||
|
enText: 'Person',
|
||||||
|
ptText: 'Pessoa',
|
||||||
|
): widget.doc.$1.person,
|
||||||
|
locale.getVariableText(
|
||||||
|
enText: 'Property',
|
||||||
|
ptText: 'Propriedade',
|
||||||
|
): widget.doc.$1.property,
|
||||||
|
locale.getVariableText(
|
||||||
|
enText: 'Created At',
|
||||||
|
ptText: 'Criado em',
|
||||||
|
): widget.doc.$1.createdAt,
|
||||||
|
locale.getVariableText(
|
||||||
|
enText: 'Updated At',
|
||||||
|
ptText: 'Atualizado em',
|
||||||
|
): widget.doc.$1.updatedAt,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
return PopScope(
|
return PopScope(
|
||||||
canPop: false,
|
canPop: false,
|
||||||
onPopInvokedWithResult: (didPop, result) => action(),
|
onPopInvokedWithResult: (didPop, result) => backAction(),
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: theme.primaryBackground,
|
backgroundColor: theme.primaryBackground,
|
||||||
appBar: buildAppBar(title, context, action),
|
appBar: buildAppBar(title, context, backAction, infoAction),
|
||||||
body: buildBody(context),
|
body: buildBody(context),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -483,8 +536,8 @@ class _DocumentViewScreenState extends ScreenState<DocumentViewScreen> {
|
||||||
|
|
||||||
return ReadView(
|
return ReadView(
|
||||||
// search: _viewerKey,
|
// search: _viewerKey,
|
||||||
title: widget.doc.description,
|
title: widget.doc.$1.description,
|
||||||
url: widget.uri.toString(),
|
url: widget.doc.$2.toString(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -495,6 +548,10 @@ class _DocumentViewScreenState extends ScreenState<DocumentViewScreen> {
|
||||||
|
|
||||||
abstract interface class Archive extends Entity {}
|
abstract interface class Archive extends Entity {}
|
||||||
|
|
||||||
|
interface class Search extends Archive {
|
||||||
|
Search();
|
||||||
|
}
|
||||||
|
|
||||||
interface class Document extends Archive {
|
interface class Document extends Archive {
|
||||||
final int id;
|
final int id;
|
||||||
final String description;
|
final String description;
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// Generator: RxBlocGeneratorForAnnotation
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
part of 'documents.dart';
|
||||||
|
|
||||||
|
/// Used as a contractor for the bloc, events and states classes
|
||||||
|
/// @nodoc
|
||||||
|
abstract class DocumentPageBlocType extends RxBlocTypeBase {
|
||||||
|
DocumentPageBlocEvents get events;
|
||||||
|
DocumentPageBlocStates get states;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [$DocumentPageBloc] extended by the [DocumentPageBloc]
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $DocumentPageBloc extends RxBlocBase
|
||||||
|
implements
|
||||||
|
DocumentPageBlocEvents,
|
||||||
|
DocumentPageBlocStates,
|
||||||
|
DocumentPageBlocType {
|
||||||
|
final _compositeSubscription = rx.CompositeSubscription();
|
||||||
|
|
||||||
|
/// Тhe [Subject] where events sink to by calling [selectDocument]
|
||||||
|
final _$selectDocumentEvent = rx.PublishSubject<Document>();
|
||||||
|
|
||||||
|
/// Тhe [Subject] where events sink to by calling [unselectDocument]
|
||||||
|
final _$unselectDocumentEvent = rx.PublishSubject<void>();
|
||||||
|
|
||||||
|
/// The state of [isDocumentSelected] implemented in
|
||||||
|
/// [_mapToIsDocumentSelectedState]
|
||||||
|
late final Stream<bool> _isDocumentSelectedState =
|
||||||
|
_mapToIsDocumentSelectedState();
|
||||||
|
|
||||||
|
/// The state of [currentDocument] implemented in [_mapToCurrentDocumentState]
|
||||||
|
late final Stream<(Document, Uri)?> _currentDocumentState =
|
||||||
|
_mapToCurrentDocumentState();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void selectDocument(Document document) => _$selectDocumentEvent.add(document);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void unselectDocument() => _$unselectDocumentEvent.add(null);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<bool> get isDocumentSelected => _isDocumentSelectedState;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<(Document, Uri)?> get currentDocument => _currentDocumentState;
|
||||||
|
|
||||||
|
Stream<bool> _mapToIsDocumentSelectedState();
|
||||||
|
|
||||||
|
Stream<(Document, Uri)?> _mapToCurrentDocumentState();
|
||||||
|
|
||||||
|
@override
|
||||||
|
DocumentPageBlocEvents get events => this;
|
||||||
|
|
||||||
|
@override
|
||||||
|
DocumentPageBlocStates get states => this;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_$selectDocumentEvent.close();
|
||||||
|
_$unselectDocumentEvent.close();
|
||||||
|
_compositeSubscription.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,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:flutter_rx_bloc/flutter_rx_bloc.dart';
|
||||||
import 'package:hub/features/backend/index.dart';
|
import 'package:hub/features/backend/index.dart';
|
||||||
import 'package:hub/features/documents/documents.dart';
|
import 'package:hub/features/documents/documents.dart';
|
||||||
import 'package:hub/features/history/index.dart';
|
import 'package:hub/features/history/index.dart';
|
||||||
|
@ -303,22 +304,25 @@ GoRouter createRouter(AppStateNotifier appStateNotifier) {
|
||||||
name: 'documentPage',
|
name: 'documentPage',
|
||||||
path: '/documentPage',
|
path: '/documentPage',
|
||||||
builder: (context, params) {
|
builder: (context, params) {
|
||||||
return DocumentPage();
|
return RxBlocProvider<DocumentPageBloc>(
|
||||||
},
|
create: (context) => DocumentPageBloc(context),
|
||||||
),
|
child: DocumentPage(),
|
||||||
FFRoute(
|
|
||||||
name: 'documentViewerScreen',
|
|
||||||
path: '/documentViewerScreen',
|
|
||||||
builder: (context, params) {
|
|
||||||
final Document doc = params.getParam('doc', ParamType.Function);
|
|
||||||
final Uri uri = params.getParam('uri', ParamType.Function);
|
|
||||||
return DocumentViewScreen(
|
|
||||||
key: UniqueKey(),
|
|
||||||
doc: doc,
|
|
||||||
uri: uri,
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
// FFRoute(
|
||||||
|
// name: 'documentViewerScreen',
|
||||||
|
// path: '/documentViewerScreen',
|
||||||
|
// builder: (context, params) {
|
||||||
|
// final Document doc = params.getParam('doc', ParamType.Function);
|
||||||
|
// final Uri uri = params.getParam('uri', ParamType.Function);
|
||||||
|
// return DocumentViewScreen(
|
||||||
|
// key: UniqueKey(),
|
||||||
|
// doc: (doc,
|
||||||
|
// uri: uri,
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
// FFRoute(name: 'settingsPage', path: '/settingsPage', builder: (context, params) => params.isEmpty ? const NavBarPage(initialPage: 'settingsPage') : const SettingsPageWidget())
|
// FFRoute(name: 'settingsPage', path: '/settingsPage', builder: (context, params) => params.isEmpty ? const NavBarPage(initialPage: 'settingsPage') : const SettingsPageWidget())
|
||||||
].map((r) => r.toRoute(appStateNotifier)).toList(),
|
].map((r) => r.toRoute(appStateNotifier)).toList(),
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,8 +2,8 @@ part of 'widgets.dart';
|
||||||
|
|
||||||
/// [TypeDefs]
|
/// [TypeDefs]
|
||||||
|
|
||||||
typedef EnhancedListViewKey<B, H, F>
|
typedef EnhancedListViewKey<B, H, F, Q>
|
||||||
= GlobalKey<EnhancedListViewState<B, H, F>>;
|
= GlobalKey<EnhancedListViewState<B, H, F, Q>>;
|
||||||
|
|
||||||
typedef PaginatedListViewHeaderBuilder<H> = Widget Function(
|
typedef PaginatedListViewHeaderBuilder<H> = Widget Function(
|
||||||
Future<List<H?>> Function() headerItems);
|
Future<List<H?>> Function() headerItems);
|
||||||
|
@ -14,8 +14,8 @@ typedef PaginatedListViewFooterBuilder<F> = Widget Function(
|
||||||
|
|
||||||
typedef Query<T> = T?;
|
typedef Query<T> = T?;
|
||||||
|
|
||||||
typedef BodyItemsBuilder<T> = Future<List<T?>> Function(
|
typedef BodyItemsBuilder<T, Q> = Future<List<T?>> Function(
|
||||||
int page, int pageSize, Query query);
|
int page, int pageSize, Q query);
|
||||||
typedef HeaderItemsBuilder<H> = Future<List<H?>> Function();
|
typedef HeaderItemsBuilder<H> = Future<List<H?>> Function();
|
||||||
typedef FooterItemsBuilder<F> = Future<List<F?>> Function();
|
typedef FooterItemsBuilder<F> = Future<List<F?>> Function();
|
||||||
|
|
||||||
|
@ -145,16 +145,17 @@ interface class EnhancedPaginatedList<T> extends PaginatedList<T> {
|
||||||
Future<void> awaitLoad() async => Future.value();
|
Future<void> awaitLoad() async => Future.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract interface class EnhancedListViewBase<T, H, F> extends StatefulWidget {
|
abstract interface class EnhancedListViewBase<T, H, F, Q>
|
||||||
|
extends StatefulWidget {
|
||||||
const EnhancedListViewBase({super.key});
|
const EnhancedListViewBase({super.key});
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract interface class EnhancedListViewBaseState<T>
|
abstract interface class EnhancedListViewBaseState<T>
|
||||||
extends State<EnhancedListViewBase> {}
|
extends State<EnhancedListViewBase> {}
|
||||||
|
|
||||||
class EnhancedListView<BodyType, HeaderType, FooterType>
|
class EnhancedListView<BodyType, HeaderType, FooterType, QueryType>
|
||||||
extends EnhancedListViewBase<BodyType, HeaderType, FooterType> {
|
extends EnhancedListViewBase<BodyType, HeaderType, FooterType, QueryType> {
|
||||||
final BodyItemsBuilder<BodyType> bodyItems;
|
final BodyItemsBuilder<BodyType, QueryType?> bodyItems;
|
||||||
final PaginatedListViewBodyBuilder<BodyType> bodyBuilder;
|
final PaginatedListViewBodyBuilder<BodyType> bodyBuilder;
|
||||||
final HeaderItemsBuilder<HeaderType>? headerItems;
|
final HeaderItemsBuilder<HeaderType>? headerItems;
|
||||||
final PaginatedListViewHeaderBuilder<HeaderType>? headerBuilder;
|
final PaginatedListViewHeaderBuilder<HeaderType>? headerBuilder;
|
||||||
|
@ -172,18 +173,21 @@ class EnhancedListView<BodyType, HeaderType, FooterType>
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
EnhancedListViewState<BodyType, HeaderType, FooterType> createState() =>
|
EnhancedListViewState<BodyType, HeaderType, FooterType, QueryType>
|
||||||
EnhancedListViewState<BodyType, HeaderType, FooterType>();
|
createState() =>
|
||||||
|
EnhancedListViewState<BodyType, HeaderType, FooterType, QueryType>();
|
||||||
}
|
}
|
||||||
|
|
||||||
class EnhancedListViewState<ItemType, HeaderType, FooterType>
|
class EnhancedListViewState<ItemType, HeaderType, FooterType, QueryType>
|
||||||
extends State<EnhancedListView<ItemType, HeaderType, FooterType>> {
|
extends State<
|
||||||
late EnhancedListViewBloc<ItemType, HeaderType, FooterType> bloc;
|
EnhancedListView<ItemType, HeaderType, FooterType, QueryType>> {
|
||||||
|
late EnhancedListViewBloc<ItemType, HeaderType, FooterType, QueryType> bloc;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
bloc = EnhancedListViewBloc<ItemType, HeaderType, FooterType>(
|
|
||||||
|
bloc = EnhancedListViewBloc<ItemType, HeaderType, FooterType, QueryType>(
|
||||||
bodyItemsBuilder: widget.bodyItems,
|
bodyItemsBuilder: widget.bodyItems,
|
||||||
headerItemsBuilder: widget.headerItems ?? () async => [],
|
headerItemsBuilder: widget.headerItems ?? () async => [],
|
||||||
footerItemsBuilder: widget.footerItems ?? () async => [],
|
footerItemsBuilder: widget.footerItems ?? () async => [],
|
||||||
|
@ -193,41 +197,71 @@ class EnhancedListViewState<ItemType, HeaderType, FooterType>
|
||||||
bloc.events.loadFooterItems();
|
bloc.events.loadFooterItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
void filterBodyItems(Query query) {
|
void filterBodyItems(Query query) => bloc.filterBodyItems(query);
|
||||||
bloc.filterBodyItems(query);
|
|
||||||
}
|
|
||||||
|
|
||||||
void filterHeaderItems(Query query) {
|
void filterHeaderItems(Query query) => bloc.filterHeaderItems(query);
|
||||||
bloc.filterHeaderItems(query);
|
|
||||||
}
|
|
||||||
|
|
||||||
void filterFooterItems(Query query) {
|
void filterFooterItems(Query query) => bloc.filterFooterItems(query);
|
||||||
bloc.filterFooterItems(query);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return StreamBuilder<List<ItemType>>(
|
final header = StreamBuilder<List<HeaderType>>(
|
||||||
stream: bloc.states.bodyItems.cast<List<ItemType>>(),
|
stream: bloc.states.headerItems.cast<List<HeaderType>>(),
|
||||||
builder: (context, bodySnapshot) {
|
builder: (context, headerSnapshot) {
|
||||||
return StreamBuilder<List<HeaderType>>(
|
if (headerSnapshot.connectionState == ConnectionState.waiting) {
|
||||||
stream: bloc.states.headerItems.cast<List<HeaderType>>(),
|
return const EnhancedProgressIndicator();
|
||||||
builder: (context, headerSnapshot) {
|
} else if (headerSnapshot.hasError) {
|
||||||
return StreamBuilder<List<FooterType>>(
|
return EnhancedErrorWidget(error: headerSnapshot.error);
|
||||||
stream: bloc.states.footerItems.cast<List<FooterType>>(),
|
} else if (!headerSnapshot.hasData || headerSnapshot.data!.isEmpty) {
|
||||||
builder: (context, footerSnapshot) {
|
return const SizedBox.shrink();
|
||||||
return ListView.builder(
|
} else {
|
||||||
itemCount: bodySnapshot.data?.length ?? 0,
|
return widget.headerBuilder!(() async => headerSnapshot.data!);
|
||||||
itemBuilder: (context, index) {
|
}
|
||||||
return widget.bodyBuilder(
|
},
|
||||||
context, bodySnapshot.data![index], index);
|
);
|
||||||
},
|
final footer = StreamBuilder<List<FooterType>>(
|
||||||
);
|
stream: bloc.states.footerItems.cast<List<FooterType>>(),
|
||||||
|
builder: (context, footerSnapshot) {
|
||||||
|
if (footerSnapshot.connectionState == ConnectionState.waiting) {
|
||||||
|
return const EnhancedProgressIndicator();
|
||||||
|
} else if (footerSnapshot.hasError) {
|
||||||
|
return EnhancedErrorWidget(error: footerSnapshot.error);
|
||||||
|
} else if (!footerSnapshot.hasData || footerSnapshot.data!.isEmpty) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
} else {
|
||||||
|
return widget.footerBuilder!(() async => footerSnapshot.data!);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
final body = Expanded(
|
||||||
|
child: StreamBuilder<List<ItemType>>(
|
||||||
|
stream: bloc.states.bodyItems.cast<List<ItemType>>(),
|
||||||
|
builder: (context, bodySnapshot) {
|
||||||
|
if (bodySnapshot.connectionState == ConnectionState.waiting) {
|
||||||
|
return const EnhancedProgressIndicator();
|
||||||
|
} else if (bodySnapshot.hasError) {
|
||||||
|
return EnhancedErrorWidget(error: bodySnapshot.error);
|
||||||
|
} else if (!bodySnapshot.hasData || bodySnapshot.data!.isEmpty) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
} else {
|
||||||
|
return ListView.builder(
|
||||||
|
itemCount: bodySnapshot.data?.length ?? 0,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return widget.bodyBuilder(
|
||||||
|
context, bodySnapshot.data![index], index);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
);
|
},
|
||||||
},
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
if (widget.headerBuilder != null) header,
|
||||||
|
body,
|
||||||
|
if (widget.footerBuilder != null) footer,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,13 +345,13 @@ abstract class EnhancedListViewRepository<T> {
|
||||||
int page, int pageSize, PaginatedListViewBodyBuilder<T> builder);
|
int page, int pageSize, PaginatedListViewBodyBuilder<T> builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class EnhancedListViewEvents<T, H, F> {
|
abstract class EnhancedListViewEvents<T, H, F, Q> {
|
||||||
void loadBodyItems({bool reset = false});
|
void loadBodyItems({bool reset = false, dynamic query = null});
|
||||||
void loadHeaderItems();
|
void loadHeaderItems();
|
||||||
void loadFooterItems();
|
void loadFooterItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class EnhancedListViewStates<T, H, F> {
|
abstract class EnhancedListViewStates<T, H, F, Q> {
|
||||||
Stream<List<T>> get bodyItems;
|
Stream<List<T>> get bodyItems;
|
||||||
Stream<List<H>> get headerItems;
|
Stream<List<H>> get headerItems;
|
||||||
Stream<List<F>> get footerItems;
|
Stream<List<F>> get footerItems;
|
||||||
|
@ -326,15 +360,16 @@ abstract class EnhancedListViewStates<T, H, F> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@RxBloc()
|
@RxBloc()
|
||||||
class EnhancedListViewBloc<T, H, F> extends $EnhancedListViewBloc<T, H, F> {
|
class EnhancedListViewBloc<T, H, F, Q>
|
||||||
|
extends $EnhancedListViewBloc<T, H, F, Q?> {
|
||||||
EnhancedListViewBloc({
|
EnhancedListViewBloc({
|
||||||
required this.bodyItemsBuilder,
|
required this.bodyItemsBuilder,
|
||||||
required this.headerItemsBuilder,
|
required this.headerItemsBuilder,
|
||||||
required this.footerItemsBuilder,
|
required this.footerItemsBuilder,
|
||||||
}) {
|
}) {
|
||||||
_$loadBodyItemsEvent
|
_$loadBodyItemsEvent
|
||||||
.startWith(true)
|
.startWith((query: null, reset: true))
|
||||||
.switchMap((reset) => _fetchBodyItems(reset))
|
.switchMap((item) => _fetchBodyItems(item.reset, item.query))
|
||||||
.bind(_bodyItems)
|
.bind(_bodyItems)
|
||||||
.addTo(_compositeSubscription);
|
.addTo(_compositeSubscription);
|
||||||
|
|
||||||
|
@ -349,20 +384,23 @@ class EnhancedListViewBloc<T, H, F> extends $EnhancedListViewBloc<T, H, F> {
|
||||||
.addTo(_compositeSubscription);
|
.addTo(_compositeSubscription);
|
||||||
}
|
}
|
||||||
|
|
||||||
final BodyItemsBuilder<T> bodyItemsBuilder;
|
final BodyItemsBuilder<T, Q?> bodyItemsBuilder;
|
||||||
final HeaderItemsBuilder<H> headerItemsBuilder;
|
final HeaderItemsBuilder<H> headerItemsBuilder;
|
||||||
final FooterItemsBuilder<F> footerItemsBuilder;
|
final FooterItemsBuilder<F> footerItemsBuilder;
|
||||||
|
|
||||||
final _bodyItems = BehaviorSubject<List<T>>.seeded([]);
|
final _bodyItems = BehaviorSubject<List<T>>.seeded([]);
|
||||||
|
|
||||||
final _headerItems = BehaviorSubject<List<H>>.seeded([]);
|
final _headerItems = BehaviorSubject<List<H>>.seeded([]);
|
||||||
|
|
||||||
final _footerItems = BehaviorSubject<List<F>>.seeded([]);
|
final _footerItems = BehaviorSubject<List<F>>.seeded([]);
|
||||||
|
|
||||||
final _isLoading = BehaviorSubject<bool>.seeded(false);
|
final _isLoading = BehaviorSubject<bool>.seeded(false);
|
||||||
final _errors = BehaviorSubject<String>();
|
final _errors = BehaviorSubject<String>();
|
||||||
|
|
||||||
Stream<List<T>> _fetchBodyItems(bool reset) async* {
|
Stream<List<T>> _fetchBodyItems(bool reset, Q? query) async* {
|
||||||
try {
|
try {
|
||||||
_isLoading.add(true);
|
_isLoading.add(true);
|
||||||
final items = await bodyItemsBuilder(1, 10, null);
|
final items = await bodyItemsBuilder(1, 10, query);
|
||||||
yield items.whereType<T>().toList();
|
yield items.whereType<T>().toList();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_errors.add(e.toString());
|
_errors.add(e.toString());
|
||||||
|
@ -395,15 +433,16 @@ class EnhancedListViewBloc<T, H, F> extends $EnhancedListViewBloc<T, H, F> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void filterBodyItems(Query query) {
|
void filterBodyItems(Q query) {
|
||||||
_$loadBodyItemsEvent.add(true);
|
print('filterBodyItems Q: ${query == null}');
|
||||||
|
_$loadBodyItemsEvent.add((query: query, reset: true));
|
||||||
}
|
}
|
||||||
|
|
||||||
void filterHeaderItems(Query query) {
|
void filterHeaderItems(Q query) {
|
||||||
_$loadHeaderItemsEvent.add(null);
|
_$loadHeaderItemsEvent.add(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
void filterFooterItems(Query query) {
|
void filterFooterItems(Q query) {
|
||||||
_$loadFooterItemsEvent.add(null);
|
_$loadFooterItemsEvent.add(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,10 @@ part of 'widgets.dart';
|
||||||
mixin Template {
|
mixin Template {
|
||||||
PreferredSizeWidget buildAppBar(
|
PreferredSizeWidget buildAppBar(
|
||||||
String title,
|
String title,
|
||||||
BuildContext context,
|
BuildContext context, [
|
||||||
dynamic Function()? backAction,
|
dynamic Function()? backAction,
|
||||||
) {
|
dynamic Function()? frontAction,
|
||||||
|
]) {
|
||||||
final theme = FlutterFlowTheme.of(context);
|
final theme = FlutterFlowTheme.of(context);
|
||||||
return AppBar(
|
return AppBar(
|
||||||
backgroundColor: FlutterFlowTheme.of(context).primaryBackground,
|
backgroundColor: FlutterFlowTheme.of(context).primaryBackground,
|
||||||
|
@ -25,12 +26,36 @@ mixin Template {
|
||||||
leading: _backButton(context, theme, backAction),
|
leading: _backButton(context, theme, backAction),
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
elevation: 0.0,
|
elevation: 0.0,
|
||||||
actions: [],
|
actions: _frontButton(context, theme, frontAction),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _backButton(BuildContext context, FlutterFlowTheme theme,
|
List<Widget> _frontButton(BuildContext context, FlutterFlowTheme theme,
|
||||||
|
dynamic Function()? action) {
|
||||||
|
if (action == null) return [];
|
||||||
|
return [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () async => await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: Text('Info'),
|
||||||
|
content: Text('This is a sample app.'),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: Text('Close'))
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
icon: Icon(
|
||||||
|
Symbols.info_i_rounded,
|
||||||
|
color: Colors.black,
|
||||||
|
))
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget? _backButton(BuildContext context, FlutterFlowTheme theme,
|
||||||
dynamic Function()? onPressed) {
|
dynamic Function()? onPressed) {
|
||||||
|
if (onPressed == null) return null;
|
||||||
return FlutterFlowIconButton(
|
return FlutterFlowIconButton(
|
||||||
key: ValueKey<String>('BackNavigationAppBar'),
|
key: ValueKey<String>('BackNavigationAppBar'),
|
||||||
borderColor: Colors.transparent,
|
borderColor: Colors.transparent,
|
||||||
|
|
|
@ -6,6 +6,7 @@ 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/flutter_flow/index.dart';
|
import 'package:hub/flutter_flow/index.dart';
|
||||||
|
import 'package:material_symbols_icons/symbols.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';
|
||||||
|
|
|
@ -14,9 +14,9 @@ abstract class EnhancedListViewBlocType extends RxBlocTypeBase {
|
||||||
EnhancedListViewStates get states;
|
EnhancedListViewStates get states;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [$EnhancedListViewBloc<T, H, F>] extended by the [EnhancedListViewBloc<T, H, F>]
|
/// [$EnhancedListViewBloc<T, H, F, Q>] extended by the [EnhancedListViewBloc<T, H, F, Q>]
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
abstract class $EnhancedListViewBloc<T, H, F> extends RxBlocBase
|
abstract class $EnhancedListViewBloc<T, H, F, Q> extends RxBlocBase
|
||||||
implements
|
implements
|
||||||
EnhancedListViewEvents,
|
EnhancedListViewEvents,
|
||||||
EnhancedListViewStates,
|
EnhancedListViewStates,
|
||||||
|
@ -24,7 +24,7 @@ abstract class $EnhancedListViewBloc<T, H, F> extends RxBlocBase
|
||||||
final _compositeSubscription = CompositeSubscription();
|
final _compositeSubscription = CompositeSubscription();
|
||||||
|
|
||||||
/// Тhe [Subject] where events sink to by calling [loadBodyItems]
|
/// Тhe [Subject] where events sink to by calling [loadBodyItems]
|
||||||
final _$loadBodyItemsEvent = PublishSubject<bool>();
|
final _$loadBodyItemsEvent = PublishSubject<({bool reset, dynamic query})>();
|
||||||
|
|
||||||
/// Тhe [Subject] where events sink to by calling [loadHeaderItems]
|
/// Тhe [Subject] where events sink to by calling [loadHeaderItems]
|
||||||
final _$loadHeaderItemsEvent = PublishSubject<void>();
|
final _$loadHeaderItemsEvent = PublishSubject<void>();
|
||||||
|
@ -48,7 +48,14 @@ abstract class $EnhancedListViewBloc<T, H, F> extends RxBlocBase
|
||||||
late final Stream<String> _errorsState = _mapToErrorsState();
|
late final Stream<String> _errorsState = _mapToErrorsState();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void loadBodyItems({bool reset = false}) => _$loadBodyItemsEvent.add(reset);
|
void loadBodyItems({
|
||||||
|
bool reset = false,
|
||||||
|
dynamic query = null,
|
||||||
|
}) =>
|
||||||
|
_$loadBodyItemsEvent.add((
|
||||||
|
reset: reset,
|
||||||
|
query: query,
|
||||||
|
));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void loadHeaderItems() => _$loadHeaderItemsEvent.add(null);
|
void loadHeaderItems() => _$loadHeaderItemsEvent.add(null);
|
||||||
|
@ -96,3 +103,6 @@ abstract class $EnhancedListViewBloc<T, H, F> extends RxBlocBase
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ignore: unused_element
|
||||||
|
typedef _LoadBodyItemsEventArgs = ({bool reset, dynamic query});
|
||||||
|
|
Loading…
Reference in New Issue