This commit is contained in:
jantunesmessias 2025-02-17 11:55:18 -03:00
parent 65a70cd8de
commit 2487801ee1
8 changed files with 1108 additions and 438 deletions

View File

@ -21,6 +21,7 @@ class DocumentPageBloc extends Bloc<DocumentPageEvent, DocumentPageState> {
Future<void> _filterCategoryEvent( Future<void> _filterCategoryEvent(
FilterCategoryEvent event, Emitter<DocumentPageState> emit) async { FilterCategoryEvent event, Emitter<DocumentPageState> emit) async {
log('generateDocuments -> ${state.isCategorySelected}');
state.isCategorySelected state.isCategorySelected
? _unselectCategory(event, emit) ? _unselectCategory(event, emit)
: _selectCategory(event, emit); : _selectCategory(event, emit);
@ -32,8 +33,12 @@ class DocumentPageBloc extends Bloc<DocumentPageEvent, DocumentPageState> {
isCategorySelected: true, isCategorySelected: true,
)); ));
final s = model.managerKey.currentState!; final listViewState = model.enhancedListViewKey.currentState!;
state.isCategorySelected ? s.filter(event.query) : s.filter(event.query); listViewState.safeSetState(() async {
return await listViewState.filterItems(event.query);
});
// state.isCategorySelected ? s.filter(event.query) : s.filter(event.query);
} }
Future<void> _unselectCategory( Future<void> _unselectCategory(
@ -42,9 +47,10 @@ class DocumentPageBloc extends Bloc<DocumentPageEvent, DocumentPageState> {
isCategorySelected: false, isCategorySelected: false,
)); ));
final s = model.managerKey.currentState!; final listViewState = model.enhancedListViewKey.currentState!;
final Query q = Document.fromDesc(''); listViewState.safeSetState(() async {
s.filter(q); return await listViewState.filterItems(null);
});
} }
Future<void> _selectDocument( Future<void> _selectDocument(

View File

@ -4,7 +4,8 @@ class DocumentPageModel extends FlutterFlowModel<DocumentPage> {
DocumentPageModel(); DocumentPageModel();
late final GlobalKey<State<FutureBuilder>> pageKey; late final GlobalKey<State<FutureBuilder>> pageKey;
late final SearchKey managerKey; late final EnhancedRemoteListViewKey<Document, Category, Null>
enhancedListViewKey;
late final DocumentKey viewerKey; late final DocumentKey viewerKey;
late final PagingController<int, Document> _pagingController; late final PagingController<int, Document> _pagingController;
@ -13,7 +14,7 @@ class DocumentPageModel extends FlutterFlowModel<DocumentPage> {
@override @override
void initState(BuildContext context) { void initState(BuildContext context) {
pageKey = GlobalKey<State<FutureBuilder>>(); pageKey = GlobalKey<State<FutureBuilder>>();
managerKey = SearchKey(); enhancedListViewKey = EnhancedRemoteListViewKey<Document, Category, Null>();
viewerKey = DocumentKey(); viewerKey = DocumentKey();
_pagingController = PagingController<int, Document>(firstPageKey: 1); _pagingController = PagingController<int, Document>(firstPageKey: 1);
@ -33,8 +34,8 @@ class DocumentPageModel extends FlutterFlowModel<DocumentPage> {
context.read<DocumentPageBloc>().add(SelectDocumentEvent(document)); context.read<DocumentPageBloc>().add(SelectDocumentEvent(document));
} }
/// [documentItemBuilder] /// [itemBodyBuilder]
DocumentItem documentItemBuilder<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(
@ -47,8 +48,8 @@ class DocumentPageModel extends FlutterFlowModel<DocumentPage> {
return CategoryItem(category: item! as Category); return CategoryItem(category: item! as Category);
} }
/// [listHeaderBuilder] /// [itemHeaderBuilder]
Widget listHeaderBuilder<T>(Future<List<T?>> Function() gen) => Widget itemHeaderBuilder<T>(Future<List<T?>> Function() gen) =>
Builder(builder: (context) { Builder(builder: (context) {
return Column( return Column(
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
@ -75,81 +76,64 @@ class DocumentPageModel extends FlutterFlowModel<DocumentPage> {
); );
}); });
/// [generateDocuments] /// [generateBodyItems]
Future<(bool, List<Document?>?)> generateDocuments( Future<List<T?>> generateBodyItems<T extends Document>(
pageKey, Query query) async { int pageKey, int pageSize, dynamic query) async {
final List<Document?> error = [null]; log('generateDocuments: $query');
final List<T?> error = [null];
print('Query: ${query is Document}'); print('Query: ${query is Document}');
final GetDocuments getDocuments = FreAccessWSGlobal.getDocuments; final GetDocuments getDocuments = FreAccessWSGlobal.getDocuments;
final ApiCallResponse newItems = await getDocuments.call(pageKey, query); final ApiCallResponse newItems = await getDocuments.call(pageKey, query);
if (newItems.jsonBody == null) return (false, error); if (newItems.jsonBody == null || newItems.jsonBody['error'] == true) {
if (newItems.jsonBody['error'] == true) return (false, error); 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 (true, docs); final List<dynamic> list = newItems.jsonBody['value']['list'];
// listViewKey.currentState!.count = newItems.jsonBody['value']['count'] ?? 0; final List<Document> docs = list.map((item) {
log('-> generateDocuments: $item');
return Document(
id: item['id'],
description: item['description'],
type: item['type'],
category: Category(
id: item['category']['id'],
color: item['category']['color'].toColor(),
title: item['category']['description'],
),
person: item['person'] ?? '',
property: item['property'] ?? '',
createdAt: item['createdAt'],
updatedAt: item['updatedAt'],
);
}).toList();
return docs as List<T?>;
} }
/// [generateCategories] /// [generateHeaderItems]
Future<List<Category?>> generateCategories() async { Future<List<T?>> generateHeaderItems<T extends Category>() async {
final List<Category?> error = [null]; final List<T?> error = [null];
final GetCategories getCategories = FreAccessWSGlobal.getCategories; final GetCategories getCategories = FreAccessWSGlobal.getCategories;
final ApiCallResponse newItems = await getCategories.call(); final ApiCallResponse newItems = await getCategories.call();
if (newItems.jsonBody['error'] == true) return error; if (newItems.jsonBody == null || newItems.jsonBody['error'] == true) {
if (newItems.jsonBody == null) return error; 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; final List<dynamic> list = newItems.jsonBody['value'];
final List<Category> categories = list.map((item) {
return Category(
id: item['id'],
color: item['color'].toColor(),
title: item['description'],
);
}).toList();
log('categories: $categories');
return categories as List<T?>;
} }
/// [filter] /// [filter]

View File

@ -31,14 +31,14 @@ class DocumentManagerScreen extends StatelessScreen {
return Column( return Column(
children: [ children: [
Expanded( Expanded(
child: EnhancedRemoteListView<Document, Category>( child: EnhancedListView.remote<Document, Category, Null>(
key: model.managerKey, key: model.enhancedListViewKey,
pagingController: model._pagingController, headerBuilder: model.itemHeaderBuilder<Category>,
headerBuilder: model.listHeaderBuilder, headerItems: model.generateHeaderItems<Category>,
headerItems: model.generateCategories, bodyBuilder: model.itemBodyBuilder<Document>,
bodyBuilder: model.documentItemBuilder, bodyItems: model.generateBodyItems<Document>,
dataProvider: model.generateDocuments, footerBuilder: null,
onFetchError: model.onFetchError, footerItems: null,
), ),
), ),
] // ] //

View File

@ -1,4 +1,4 @@
part of '../widgets.dart'; part of 'widgets.dart';
class EnhancedCarouselView<T> extends StatelessWidget { class EnhancedCarouselView<T> extends StatelessWidget {
final Future<List<T?>> Function() generateItems; final Future<List<T?>> Function() generateItems;

View File

@ -0,0 +1,637 @@
part of 'widgets.dart';
/// [TypeDefs]
typedef EnhancedRemoteListViewKey<B, H, F>
= GlobalKey<EnhancedRemoteListViewState<B, H, F>>;
typedef EnhancedLocalListViewKey<B, H, F>
= GlobalKey<EnhancedLocalListViewState<B, H, F>>;
typedef PaginatedListViewHeaderBuilder<H> = Widget Function(
Future<List<H?>> Function() headerItems);
typedef PaginatedListViewBodyBuilder<T> = Widget Function(
BuildContext context, T item, int index);
typedef PaginatedListViewFooterBuilder<F> = Widget Function(
Future<List<F?>> Function() footerItems);
typedef Query<T> = T?;
typedef BodyItemsBuilder<T> = Future<List<T?>> Function(
int page, int pageSize, Query query);
typedef HeaderItemsBuilder<H> = Future<List<H?>> Function();
typedef FooterItemsBuilder<F> = Future<List<F?>> Function();
/// [Extensions]
extension PaginatedListMergeExtensions<T>
on Stream<Result<EnhancedPaginatedList<T>>> {
Stream<EnhancedPaginatedList<T>> mergeWithPaginatedList(
BehaviorSubject<EnhancedPaginatedList<T>> currentList) {
return map(
(result) {
final current = currentList.value;
if (result is ResultSuccess<EnhancedPaginatedList<T>>) {
final newPaginated = result.data;
return current.items.isEmpty
? newPaginated
: current.copyWith(
list: [...current.items, ...newPaginated.items],
currentPage: newPaginated.currentPage,
totalCount: newPaginated.totalCount,
error: newPaginated.error,
);
} else if (result is ResultError<EnhancedPaginatedList<T>>) {
return current.copyWith(error: result.error);
} else {
return current;
}
},
);
}
}
extension PublishSubjectExtensions<T> on PublishSubject<T> {
Stream<T> startWith(T initial) => Rx.concat([Stream.value(initial), this]);
}
extension StreamStartWithExtension<T> on Stream<T> {
Stream<T> startWith(T initial) => Rx.concat([Stream.value(initial), this]);
}
extension QueryBlocStreamExtensions<T> on Stream<bool> {
Stream<Result<EnhancedPaginatedList<T>>> fetchData(
EnhancedListViewRepository<T> repository,
PaginatedListViewBodyBuilder<T> builder,
BehaviorSubject<EnhancedPaginatedList<T>> paginatedList,
) =>
switchMap((reset) {
if (reset) {
paginatedList.add(paginatedList.value.resetAll());
}
final nextPage = paginatedList.value.currentPage + 1;
return repository
.fetchPage(nextPage, paginatedList.value.pageSize, builder)
.asResultStream();
});
}
/// [Widgets]
/// [EnhancedListView]
interface class EnhancedPaginatedList<T> extends PaginatedList<T> {
@override
final Exception? error;
final bool isInitialized;
@override
final bool isLoading;
final List<T> items;
@override
final int pageSize;
@override
final int? totalCount;
final int currentPage;
EnhancedPaginatedList({
required this.items,
required this.pageSize,
this.currentPage = 0,
this.totalCount,
required this.error,
required this.isInitialized,
required this.isLoading,
}) : super(
error: error,
isInitialized: isInitialized,
isLoading: isLoading,
list: items,
pageSize: pageSize,
totalCount: totalCount,
);
EnhancedPaginatedList<T> resetAll() => EnhancedPaginatedList<T>(
error: null,
isInitialized: false,
isLoading: false,
items: const [],
pageSize: pageSize,
totalCount: null,
currentPage: 0,
);
@override
EnhancedPaginatedList<T> copyWith({
List<T>? list,
bool? isLoading,
int? totalCount,
Exception? error,
int? pageSize,
bool? isInitialized,
int? currentPage,
}) =>
EnhancedPaginatedList<T>(
error: error ?? this.error,
isInitialized: isInitialized ?? this.isInitialized,
isLoading: isLoading ?? this.isLoading,
items: list ?? this.items,
pageSize: pageSize ?? this.pageSize,
totalCount: totalCount ?? this.totalCount,
currentPage: currentPage ?? this.currentPage,
);
@override
int get itemCount => items.length;
@override
T? getItem(int index) => index < items.length ? items[index] : null;
Future<void> awaitLoad() async => Future.value();
}
abstract interface class EnhancedListViewBase<T, H, F> extends StatefulWidget {
const EnhancedListViewBase({super.key});
}
abstract interface class EnhancedListViewBaseState<T>
extends State<EnhancedListViewBase> {}
class EnhancedListView {
static EnhancedRemoteListView<BodyType, HeaderType, FooterType>
remote<BodyType, HeaderType, FooterType>({
required Key? key,
required BodyItemsBuilder<BodyType> bodyItems,
required PaginatedListViewBodyBuilder<BodyType> bodyBuilder,
HeaderItemsBuilder<HeaderType>? headerItems,
PaginatedListViewHeaderBuilder<HeaderType>? headerBuilder,
FooterItemsBuilder<FooterType>? footerItems,
PaginatedListViewFooterBuilder<FooterType>? footerBuilder,
}) {
return EnhancedRemoteListView<BodyType, HeaderType, FooterType>(
key: key,
bodyItems: bodyItems,
bodyBuilder: bodyBuilder,
headerItems: headerItems,
headerBuilder: headerBuilder,
footerItems: footerItems,
footerBuilder: footerBuilder,
);
}
static EnhancedLocalListView<T, H, F> local<T, H, F>({
required Key? key,
required List<T> list,
required Widget Function(T) itemBuilder,
required bool Function(T, String) filter,
List<T> Function(String)? onSearch,
Widget? header,
}) {
return EnhancedLocalListView<T, H, F>(
key: key,
list: list,
itemBuilder: itemBuilder,
filter: filter,
onSearch: onSearch,
header: header,
);
}
}
/// [EnhancedLocalListView]
class EnhancedLocalListView<T, H, F> extends EnhancedListViewBase<T, H, F> {
final List<T> list;
final Widget Function(T) itemBuilder;
final bool Function(T, String) filter;
final Widget header;
final List<T> Function(String)? onSearch;
EnhancedLocalListView({
Key? key,
required this.list,
required this.itemBuilder,
required this.filter,
List<T> Function(String)? onSearch,
Widget? header,
}) : header = header ?? const SizedBox.shrink(),
onSearch = onSearch ??
((String query) =>
list.where((documents) => filter(documents, query)).toList()),
super(key: key);
@override
EnhancedLocalListViewState<T, H, F> createState() =>
EnhancedLocalListViewState<T, H, F>();
}
class EnhancedLocalListViewState<T, H, F>
extends State<EnhancedLocalListView<T, H, F>> {
TextEditingController editingController = TextEditingController();
late List<T> filteredItems;
@override
void initState() {
filteredItems = widget.list;
super.initState();
}
@override
Widget build(BuildContext context) {
void filter(value) {
setState(() {
filteredItems = widget.onSearch!(value);
});
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: filteredItems.length + 1,
itemBuilder: (context, index) {
if (index == 0) return widget.header;
return widget.itemBuilder(filteredItems[index - 1]);
},
),
),
Padding(
padding: const EdgeInsets.all(30.0),
child: TextFormField(
controller: editingController,
onChanged: filter,
cursorColor: Colors.black,
cursorWidth: 2.0,
cursorRadius: Radius.circular(2.0),
style: TextStyle(
color: Colors.black,
fontSize: 16.0,
),
keyboardType: TextInputType.text,
textInputAction: TextInputAction.search,
autocorrect: true,
textCapitalization: TextCapitalization.sentences,
decoration: InputDecoration(
prefixIcon: Icon(Icons.search, color: Colors.black),
labelText: 'Pesquisar',
labelStyle: TextStyle(
color: Colors.black,
fontSize: 16.0,
),
hintText: 'Digite sua pesquisa',
hintStyle: TextStyle(
color: Colors.grey,
fontSize: 14.0,
),
filled: true,
fillColor: Colors.white,
contentPadding:
EdgeInsets.symmetric(vertical: 10.0, horizontal: 15.0),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(15.0)),
borderSide: BorderSide(color: Colors.black),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(15.0)),
borderSide: BorderSide(color: Colors.blue),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(15.0)),
borderSide: BorderSide(color: Colors.red),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(15.0)),
borderSide: BorderSide(color: Colors.red, width: 2.0),
),
),
),
),
],
);
}
}
/// [EnhancedRemoteListView]
class EnhancedRemoteListView<BodyType, HeaderType, FooterType>
extends EnhancedListViewBase<BodyType, HeaderType, FooterType> {
final BodyItemsBuilder<BodyType> bodyItems;
final PaginatedListViewBodyBuilder<BodyType> bodyBuilder;
final HeaderItemsBuilder<HeaderType>? headerItems;
final PaginatedListViewHeaderBuilder<HeaderType>? headerBuilder;
final FooterItemsBuilder<FooterType>? footerItems;
final PaginatedListViewFooterBuilder<FooterType>? footerBuilder;
const EnhancedRemoteListView({
Key? key,
required this.bodyItems,
required this.bodyBuilder,
this.headerItems,
this.headerBuilder,
this.footerItems,
this.footerBuilder,
}) : super(key: key);
@override
EnhancedRemoteListViewState<BodyType, HeaderType, FooterType> createState() =>
EnhancedRemoteListViewState<BodyType, HeaderType, FooterType>();
}
class EnhancedRemoteListViewState<ItemType, HeaderType, FooterType>
extends State<EnhancedRemoteListView<ItemType, HeaderType, FooterType>> {
final ScrollController _scrollController = ScrollController();
bool _isLoadingMore = false;
List<ItemType?> _items = [];
int _currentPage = 1;
Query<ItemType> query;
@override
void initState() {
super.initState();
_scrollController.addListener(_onScroll);
_loadInitialItems();
}
void _onScroll() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent &&
!_isLoadingMore) {
_loadMoreItems();
}
}
Future<void> _loadInitialItems() async {
final newItems = await widget.bodyItems(1, 10, query);
setState(() {
_items = newItems;
});
}
Future<void> _loadMoreItems() async {
setState(() {
_isLoadingMore = true;
});
final newItems = await widget.bodyItems(_currentPage + 1, 10, query);
setState(() {
_isLoadingMore = false;
if (newItems.isNotEmpty) {
_items.addAll(newItems);
_currentPage++;
}
});
}
Future<void> filterItems(Query<ItemType> newQuery) async {
log('filterItems: $newQuery');
setState(() {
query = newQuery;
_items = [];
_currentPage = 1;
});
await _loadInitialItems();
}
@override
Widget build(BuildContext context) {
log('key: ${widget.key}');
return ListView.builder(
controller: _scrollController,
itemCount: _items.length +
(widget.headerItems != null ? 1 : 0) +
(widget.footerItems != null ? 1 : 0) +
(_isLoadingMore ? 1 : 0),
itemBuilder: (context, index) {
if (widget.headerItems != null && index == 0) {
return FutureBuilder<List<HeaderType?>>(
future: widget.headerItems!(),
builder: (context, headerSnapshot) {
if (headerSnapshot.connectionState == ConnectionState.waiting) {
return const EnhancedProgressIndicator();
} else if (headerSnapshot.hasError) {
return EnhancedErrorWidget(error: headerSnapshot.error);
} else {
return widget
.headerBuilder!(() => Future.value(headerSnapshot.data));
}
},
);
}
if (widget.footerItems != null &&
index == _items.length + (widget.headerItems != null ? 1 : 0)) {
return FutureBuilder<List<FooterType?>>(
future: widget.footerItems!(),
builder: (context, footerSnapshot) {
if (footerSnapshot.connectionState == ConnectionState.waiting) {
return const EnhancedProgressIndicator();
} else if (footerSnapshot.hasError) {
return EnhancedErrorWidget(
error: footerSnapshot.error as Exception);
} else {
return widget
.footerBuilder!(() => Future.value(footerSnapshot.data));
}
},
);
}
if (_isLoadingMore &&
index ==
_items.length +
(widget.headerItems != null ? 1 : 0) +
(widget.footerItems != null ? 1 : 0)) {
return const EnhancedProgressIndicator();
}
final item = _items[index - (widget.headerItems != null ? 1 : 0)];
return widget.bodyBuilder(context, item as ItemType, index);
},
);
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
}
/// [Utils]
class EnhancedListTile<T extends Widget> extends StatelessWidget {
const EnhancedListTile(
{required this.leading, required this.title, super.key});
final T leading;
final T title;
@override
Widget build(BuildContext context) {
return Card(
child: ListTile(
leading: leading,
title: title,
),
);
}
}
class EnhancedErrorWidget extends StatelessWidget {
final Object? error;
const EnhancedErrorWidget({required this.error, super.key});
@override
Widget build(BuildContext context) {
log('error: $error');
return Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
error.toString(),
style: const TextStyle(color: Colors.red),
),
);
}
}
class EnhancedProgressIndicator extends StatelessWidget {
const EnhancedProgressIndicator({super.key});
@override
Widget build(BuildContext context) => const Center(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 12),
child: CircularProgressIndicator(),
),
);
}
class NetworkError implements Exception {
final String message;
NetworkError(this.message);
}
class ParsingError implements Exception {
final String message;
ParsingError(this.message);
}
class AuthorizationError implements Exception {
final String message;
AuthorizationError(this.message);
}
/// [State Managment]
Stream<bool> get loadingState => Stream<bool>.empty();
Stream<Exception> get errorState => Stream<Exception>.empty();
abstract class EnhancedListViewRepository<T> {
Future<EnhancedPaginatedList<T>> fetchPage(
int page, int pageSize, PaginatedListViewBodyBuilder<T> builder);
}
abstract class EnhancedListViewBlocStates<T> {
Stream<bool> get isLoading;
Stream<String> get errors;
Stream<EnhancedPaginatedList<T>> get paginatedList;
@RxBlocIgnoreState()
Future<void> get refreshDone;
}
abstract class EnhancedListViewEvents<T> {
void loadPage({bool reset = false});
}
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({
required EnhancedListViewRepository<T> repository,
required PaginatedListViewBodyBuilder<T> builder,
required T item,
int initialPageSize = 50,
}) {
_$loadPageEvent
.startWith(true)
.fetchData(
repository,
(context, item, index) => builder(context, item, index),
_paginatedList,
)
.setResultStateHandler(this)
.mergeWithPaginatedList(_paginatedList)
.bind(_paginatedList)
.addTo(_compositeSubscription);
}
final _paginatedList = BehaviorSubject<EnhancedPaginatedList<T>>.seeded(
EnhancedPaginatedList<T>(
items: [],
pageSize: 1,
currentPage: 1,
error: Exception(),
isInitialized: true,
isLoading: false,
totalCount: 0,
),
);
@override
Future<void> get refreshDone async => _paginatedList.value.awaitLoad();
@override
Stream<EnhancedPaginatedList<T>> _mapToPaginatedListState() => _paginatedList;
@override
Stream<String> _mapToErrorsState() =>
errorState.map((error) => error.toString());
@override
Stream<bool> _mapToIsLoadingState() => loadingState;
@override
void dispose() {
_paginatedList.close();
super.dispose();
}
}

View File

@ -1,325 +1,325 @@
part of '../widgets.dart'; part of 'widgets.dart';
typedef SearchKey = GlobalKey<EnhancedRemoteListViewState>; // typedef SearchKey = GlobalKey<EnhancedRemoteListViewState>;
typedef Query<X extends Archive> = X?; // typedef Query<X extends Archive> = X?;
/// ----------------------------------------------- // /// -----------------------------------------------
/// [EnhancedListView] // /// [EnhancedListView]
/// ----------------------------------------------- // /// -----------------------------------------------
abstract interface class EnhancedListView<T> extends StatefulWidget { // abstract interface class EnhancedListView<T> extends StatefulWidget {
const EnhancedListView({super.key}); // const EnhancedListView({super.key});
} // }
abstract interface class EnhancedListViewState<T> // abstract interface class EnhancedListViewState<T>
extends State<EnhancedListView> {} // extends State<EnhancedListView> {}
/// ----------------------------------------------- // /// -----------------------------------------------
/// [EnhancedLocalListView] // /// [EnhancedLocalListView]
/// ----------------------------------------------- // /// -----------------------------------------------
class EnhancedLocalListView<T> extends EnhancedListView<T> { // class EnhancedLocalListView<T> extends EnhancedListView<T> {
final List<T> list; // final List<T> list;
final Widget Function(T) itemBuilder; // final Widget Function(T) itemBuilder;
final bool Function(T, String) filter; // final bool Function(T, String) filter;
final Widget header; // final Widget header;
final List<T> Function(String)? onSearch; // final List<T> Function(String)? onSearch;
EnhancedLocalListView({ // EnhancedLocalListView({
Key? key, // Key? key,
required this.list, // required this.list,
required this.itemBuilder, // required this.itemBuilder,
required this.filter, // required this.filter,
List<T> Function(String)? onSearch, // List<T> Function(String)? onSearch,
Widget? header, // Widget? header,
}) : header = header ?? const SizedBox.shrink(), // }) : header = header ?? const SizedBox.shrink(),
onSearch = onSearch ?? // onSearch = onSearch ??
((String query) => // ((String query) =>
list.where((documents) => filter(documents, query)).toList()), // list.where((documents) => filter(documents, query)).toList()),
super(key: key); // super(key: key);
// return documents.where((documents) => filter(documents, query)).toList(); // // return documents.where((documents) => filter(documents, query)).toList();
@override // @override
EnhancedLocalListViewState<T> createState() => // EnhancedLocalListViewState<T> createState() =>
EnhancedLocalListViewState<T>(); // EnhancedLocalListViewState<T>();
} // }
class EnhancedLocalListViewState<T> extends State<EnhancedLocalListView<T>> { // class EnhancedLocalListViewState<T> extends State<EnhancedLocalListView<T>> {
TextEditingController editingController = TextEditingController(); // TextEditingController editingController = TextEditingController();
late List<T> filteredItems; // late List<T> filteredItems;
@override // @override
void initState() { // void initState() {
filteredItems = widget.list; // filteredItems = widget.list;
super.initState(); // super.initState();
} // }
@override // @override
Widget build(BuildContext context) { // Widget build(BuildContext context) {
void filter(value) { // void filter(value) {
safeSetState(() { // safeSetState(() {
filteredItems = widget.onSearch!(value); // filteredItems = widget.onSearch!(value);
}); // });
} // }
return Column( // return Column(
crossAxisAlignment: CrossAxisAlignment.start, // crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center, // mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max, // mainAxisSize: MainAxisSize.max,
children: <Widget>[ // children: <Widget>[
Expanded( // Expanded(
child: ListView.builder( // child: ListView.builder(
shrinkWrap: true, // shrinkWrap: true,
itemCount: filteredItems.length + 1, // itemCount: filteredItems.length + 1,
itemBuilder: (context, index) { // itemBuilder: (context, index) {
if (index == 0) return widget.header; // if (index == 0) return widget.header;
return widget.itemBuilder(filteredItems[index - 1]); // return widget.itemBuilder(filteredItems[index - 1]);
}, // },
), // ),
), // ),
Padding( // Padding(
padding: const EdgeInsets.all(30.0), // padding: const EdgeInsets.all(30.0),
child: TextFormField( // child: TextFormField(
controller: editingController, // controller: editingController,
onChanged: filter, // onChanged: filter,
cursorColor: Colors.black, // cursorColor: Colors.black,
cursorWidth: 2.0, // cursorWidth: 2.0,
cursorRadius: Radius.circular(2.0), // cursorRadius: Radius.circular(2.0),
style: TextStyle( // style: TextStyle(
color: Colors.black, // color: Colors.black,
fontSize: 16.0, // fontSize: 16.0,
), // ),
keyboardType: TextInputType.text, // keyboardType: TextInputType.text,
textInputAction: TextInputAction.search, // textInputAction: TextInputAction.search,
autocorrect: true, // autocorrect: true,
textCapitalization: TextCapitalization.sentences, // textCapitalization: TextCapitalization.sentences,
decoration: InputDecoration( // decoration: InputDecoration(
prefixIcon: Icon(Icons.search, color: Colors.black), // prefixIcon: Icon(Icons.search, color: Colors.black),
labelText: 'Pesquisar', // labelText: 'Pesquisar',
labelStyle: TextStyle( // labelStyle: TextStyle(
color: Colors.black, // color: Colors.black,
fontSize: 16.0, // fontSize: 16.0,
), // ),
hintText: 'Digite sua pesquisa', // hintText: 'Digite sua pesquisa',
hintStyle: TextStyle( // hintStyle: TextStyle(
color: Colors.grey, // color: Colors.grey,
fontSize: 14.0, // fontSize: 14.0,
), // ),
filled: true, // filled: true,
fillColor: Colors.white, // fillColor: Colors.white,
contentPadding: // contentPadding:
EdgeInsets.symmetric(vertical: 10.0, horizontal: 15.0), // EdgeInsets.symmetric(vertical: 10.0, horizontal: 15.0),
enabledBorder: OutlineInputBorder( // enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(15.0)), // borderRadius: BorderRadius.all(Radius.circular(15.0)),
borderSide: BorderSide(color: Colors.black), // borderSide: BorderSide(color: Colors.black),
), // ),
focusedBorder: OutlineInputBorder( // focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(15.0)), // borderRadius: BorderRadius.all(Radius.circular(15.0)),
borderSide: BorderSide(color: Colors.blue), // borderSide: BorderSide(color: Colors.blue),
), // ),
errorBorder: OutlineInputBorder( // errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(15.0)), // borderRadius: BorderRadius.all(Radius.circular(15.0)),
borderSide: BorderSide(color: Colors.red), // borderSide: BorderSide(color: Colors.red),
), // ),
focusedErrorBorder: OutlineInputBorder( // focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(15.0)), // borderRadius: BorderRadius.all(Radius.circular(15.0)),
borderSide: BorderSide(color: Colors.red, width: 2.0), // borderSide: BorderSide(color: Colors.red, width: 2.0),
), // ),
), // ),
)), // )),
], // ],
); // );
} // }
} // }
/// ----------------------------------------------- // /// -----------------------------------------------
/// [EnhancedRemoteListView] // /// [EnhancedRemoteListView]
/// ----------------------------------------------- // /// -----------------------------------------------
// ignore: must_be_immutable // // ignore: must_be_immutable
class EnhancedRemoteListView<T, Y> extends EnhancedListView<T> { // class EnhancedRemoteListView<T, Y> extends EnhancedListView<T> {
final Widget Function(BuildContext, T, int) bodyBuilder; // final Widget Function(BuildContext, T, int) bodyBuilder;
final Future<List<Y?>> Function() headerItems; // final Future<List<Y?>> Function() headerItems;
Widget Function<T>(Future<List<T?>> Function() gen) headerBuilder; // Widget Function<T>(Future<List<T?>> Function() gen) headerBuilder;
final PagingController<int, T> pagingController; // final PagingController<int, T> pagingController;
final Future<(bool, List<T?>?)> Function(int pageKey, Query query) // final Future<(bool, List<T?>?)> Function(int pageKey, Query query)
dataProvider; // dataProvider;
final void Function(Object, StackTrace) onFetchError; // final void Function(Object, StackTrace) onFetchError;
EnhancedRemoteListView({ // EnhancedRemoteListView({
Key? key, // Key? key,
// required this.fetchItems, // // required this.fetchItems,
required this.bodyBuilder, // required this.bodyBuilder,
required this.headerItems, // required this.headerItems,
required this.headerBuilder, // required this.headerBuilder,
required this.pagingController, // required this.pagingController,
required this.dataProvider, // required this.dataProvider,
required this.onFetchError, // required this.onFetchError,
}) : super(key: key); // }) : super(key: key);
@override // @override
EnhancedRemoteListViewState<T, Y> createState() => // EnhancedRemoteListViewState<T, Y> createState() =>
EnhancedRemoteListViewState<T, Y>(); // EnhancedRemoteListViewState<T, Y>();
} // }
class EnhancedRemoteListViewState<T, Y> // class EnhancedRemoteListViewState<T, Y>
extends State<EnhancedRemoteListView<T, Y>> with Pageable { // extends State<EnhancedRemoteListView<T, Y>> with Pageable {
TextEditingController editingController = TextEditingController(); // TextEditingController editingController = TextEditingController();
bool isLoading = false; // bool isLoading = false;
Query query = Document.fromDesc(''); // Query query = Document.fromDesc('');
@override // @override
void initState() { // void initState() {
widget.pagingController.addPageRequestListener( // widget.pagingController.addPageRequestListener(
(page) => fetchPage( // (page) => fetchPage(
dataProvider: () async => await widget.dataProvider(page, query), // dataProvider: () async => await widget.dataProvider(page, query),
onDataUnavailable: () => showNoMoreDataSnackBar(context), // onDataUnavailable: () => showNoMoreDataSnackBar(context),
onDataAvailable: (data) => // onDataAvailable: (data) =>
widget.pagingController.appendLastPage(data), // widget.pagingController.appendLastPage(data),
onFetchError: (e, s) => widget.onFetchError), // onFetchError: (e, s) => widget.onFetchError),
); // );
widget.pagingController.addStatusListener(_showError); // widget.pagingController.addStatusListener(_showError);
super.initState(); // super.initState();
} // }
Future<void> _showError(PagingStatus status) async { // Future<void> _showError(PagingStatus status) async {
if (status == PagingStatus.subsequentPageError) { // if (status == PagingStatus.subsequentPageError) {
final message = FFLocalizations.of(context).getVariableText( // final message = FFLocalizations.of(context).getVariableText(
enText: 'Something went wrong while fetching a new page.', // enText: 'Something went wrong while fetching a new page.',
ptText: 'Algo deu errado ao buscar uma nova página.', // ptText: 'Algo deu errado ao buscar uma nova página.',
); // );
final retry = FFLocalizations.of(context).getVariableText( // final retry = FFLocalizations.of(context).getVariableText(
enText: 'Retry', // enText: 'Retry',
ptText: 'Recarregar', // ptText: 'Recarregar',
); // );
ScaffoldMessenger.of(context).showSnackBar( // ScaffoldMessenger.of(context).showSnackBar(
SnackBar( // SnackBar(
content: Text(message), // content: Text(message),
action: SnackBarAction( // action: SnackBarAction(
label: retry, // label: retry,
onPressed: () => widget.pagingController.retryLastFailedRequest(), // onPressed: () => widget.pagingController.retryLastFailedRequest(),
), // ),
), // ),
); // );
} // }
} // }
void filter(Query data) async { // void filter(Query data) async {
if (data is Category) { // if (data is Category) {
safeSetState(() => query = Category( // safeSetState(() => query = Category(
id: data.id, // id: data.id,
color: data.color, // color: data.color,
title: data.title, // title: data.title,
)); // ));
widget.pagingController.refresh(); // widget.pagingController.refresh();
} else if (data is Document) { // } else if (data is Document) {
log('filter: ${data.description}'); // log('filter: ${data.description}');
safeSetState(() => query = data); // safeSetState(() => query = data);
widget.pagingController.refresh(); // widget.pagingController.refresh();
} else { // } else {
safeSetState(() { // safeSetState(() {
query = Document.fromDesc(''); // query = Document.fromDesc('');
}); // });
widget.pagingController.refresh(); // widget.pagingController.refresh();
} // }
} // }
@override // @override
Widget build(BuildContext context) { // Widget build(BuildContext context) {
final noDataFound = FFLocalizations.of(context).getVariableText( // final noDataFound = FFLocalizations.of(context).getVariableText(
ptText: "Nenhum item encontrado!", // ptText: "Nenhum item encontrado!",
enText: "No item found", // enText: "No item found",
); // );
final theme = FlutterFlowTheme.of(context); // final theme = FlutterFlowTheme.of(context);
final locale = FFLocalizations.of(context); // final locale = FFLocalizations.of(context);
return Column( // return Column(
crossAxisAlignment: CrossAxisAlignment.start, // crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center, // mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max, // mainAxisSize: MainAxisSize.max,
children: <Widget>[ // children: <Widget>[
buildPaginatedListView<int, T, Y>( // buildPaginatedListView<int, T, Y>(
noDataFound, // noDataFound,
widget.pagingController, // widget.pagingController,
widget.headerItems, // widget.headerItems,
widget.headerBuilder, // widget.headerBuilder,
widget.bodyBuilder, // widget.bodyBuilder,
), // ),
Padding( // Padding(
padding: const EdgeInsets.all(8.0), // padding: const EdgeInsets.all(8.0),
child: TextFormField( // child: TextFormField(
controller: editingController, // controller: editingController,
onChanged: (value) => EasyDebounce.debounce( // onChanged: (value) => EasyDebounce.debounce(
'_model.keyTextFieldTextController', // '_model.keyTextFieldTextController',
const Duration(milliseconds: 500), // const Duration(milliseconds: 500),
() => filter(Document.fromDesc(value)), // () => filter(Document.fromDesc(value)),
), // ),
cursorColor: theme.primaryText, // cursorColor: theme.primaryText,
showCursor: false, // showCursor: false,
cursorWidth: 2.0, // cursorWidth: 2.0,
cursorRadius: Radius.circular(100), // cursorRadius: Radius.circular(100),
style: TextStyle( // style: TextStyle(
color: theme.primaryText, // color: theme.primaryText,
fontSize: 16.0, // fontSize: 16.0,
decorationColor: Colors.amber, // decorationColor: Colors.amber,
), // ),
keyboardType: TextInputType.text, // keyboardType: TextInputType.text,
textInputAction: TextInputAction.search, // textInputAction: TextInputAction.search,
autocorrect: true, // autocorrect: true,
textCapitalization: TextCapitalization.sentences, // textCapitalization: TextCapitalization.sentences,
decoration: InputDecoration( // decoration: InputDecoration(
prefixIcon: Icon(Icons.search, color: theme.primary), // prefixIcon: Icon(Icons.search, color: theme.primary),
labelText: locale.getVariableText( // labelText: locale.getVariableText(
ptText: 'Pesquisar', // ptText: 'Pesquisar',
enText: 'Search', // enText: 'Search',
), // ),
labelStyle: TextStyle( // labelStyle: TextStyle(
color: theme.primaryText, // color: theme.primaryText,
fontSize: 16.0, // fontSize: 16.0,
), // ),
hintText: locale.getVariableText( // hintText: locale.getVariableText(
ptText: 'Digite sua pesquisa', // ptText: 'Digite sua pesquisa',
enText: 'Enter your search', // enText: 'Enter your search',
), // ),
hintStyle: TextStyle( // hintStyle: TextStyle(
color: theme.accent2, // color: theme.accent2,
fontSize: 14.0, // fontSize: 14.0,
), // ),
filled: true, // filled: true,
fillColor: Colors.transparent, // fillColor: Colors.transparent,
helperStyle: TextStyle( // helperStyle: TextStyle(
color: theme.primaryText, // color: theme.primaryText,
decorationColor: theme.primaryText, // decorationColor: theme.primaryText,
), // ),
focusColor: theme.primaryText, // focusColor: theme.primaryText,
contentPadding: // contentPadding:
EdgeInsets.symmetric(vertical: 10.0, horizontal: 15.0), // EdgeInsets.symmetric(vertical: 10.0, horizontal: 15.0),
enabledBorder: UnderlineInputBorder( // enabledBorder: UnderlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(15.0)), // borderRadius: BorderRadius.all(Radius.circular(15.0)),
borderSide: BorderSide(color: theme.primaryText), // borderSide: BorderSide(color: theme.primaryText),
), // ),
focusedBorder: UnderlineInputBorder( // focusedBorder: UnderlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(15.0)), // borderRadius: BorderRadius.all(Radius.circular(15.0)),
borderSide: BorderSide(color: theme.primaryText), // borderSide: BorderSide(color: theme.primaryText),
), // ),
errorBorder: UnderlineInputBorder( // errorBorder: UnderlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(15.0)), // borderRadius: BorderRadius.all(Radius.circular(15.0)),
borderSide: BorderSide(color: theme.primaryText), // borderSide: BorderSide(color: theme.primaryText),
), // ),
focusedErrorBorder: UnderlineInputBorder( // focusedErrorBorder: UnderlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(15.0)), // borderRadius: BorderRadius.all(Radius.circular(15.0)),
borderSide: BorderSide(color: theme.primaryText, width: 2.0), // borderSide: BorderSide(color: theme.primaryText, width: 2.0),
), // ),
), // ),
), // ),
), // ),
], // ],
); // );
} // }
} // }

View File

@ -9,12 +9,15 @@ import 'package:http/http.dart' as http;
import 'package:hub/features/documents/index.dart'; 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:hub/shared/mixins/pegeable_mixin.dart';
import 'package:hub/shared/utils/index.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.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';
import 'package:rx_bloc_list/rx_bloc_list.dart';
import 'package:rxdart/rxdart.dart';
import 'package:rx_bloc/rx_bloc.dart';
/// [Base]
part 'page.dart'; part 'page.dart';
part 'component.dart'; part 'component.dart';
part 'screen.dart'; part 'screen.dart';
@ -25,5 +28,7 @@ part 'entity.dart';
part 'list_view.dart'; part 'list_view.dart';
part 'carousel_view.dart'; part 'carousel_view.dart';
part 'read_view.dart'; part 'read_view.dart';
part 'enhanced_list_view.dart';
/// [Component]'s
part 'text.dart'; part 'text.dart';

View File

@ -1,77 +1,102 @@
# Informações básicas do projeto
name: hub name: hub
description: A new Flutter project. description: . # Descrição do projeto (adicione mais detalhes se necessário)
publish_to: "none" # Destino de publicação
publish_to: "none"
# Versão do aplicativo
version: 1.3.5+24 version: 1.3.5+24
# Restrições de versão do SDK Dart
environment: environment:
sdk: ">=3.0.0 <4.0.0" sdk: ">=3.5.0-0.0.dev <4.0.0"
# Dependências do aplicativo
dependencies: dependencies:
# Dependências essenciais do Flutter
flutter: flutter:
sdk: flutter sdk: flutter
flutter_localizations: flutter_localizations:
sdk: flutter sdk: flutter
pdfx: ^2.8.0
auto_size_text: ^3.0.0 # Gerenciamento de Estado
provider: 6.1.2
flutter_bloc: ^9.0.0
flutter_riverpod: ^2.5.1
rx_bloc: ^6.0.1
flutter_rx_bloc: ^7.0.0
rx_bloc_list: ^5.0.1
rxdart: ^0.28.0
rx_bloc_test: ^5.0.0
bloc_test: ^10.0.0
bloc_concurrency: ^0.3.0
hydrated_bloc: ^10.0.0
# Programação Funcional
dart_either: ^2.0.0
result_dart: ^2.0.0
fpdart: ^1.1.1
# Pacotes de UI
auto_size_text: 3.0.0
barcode_widget: ^2.0.4 barcode_widget: ^2.0.4
infinite_scroll_pagination: ^4.1.0
cached_network_image: ^3.4.0 cached_network_image: ^3.4.0
firebase_core: ^3.4.0
flutter_inappwebview: ^6.0.0 flutter_inappwebview: ^6.0.0
webview_flutter: ^4.8.0 webview_flutter: ^4.8.0
rxdart: ^0.28.0 flutter_spinkit: 5.2.1
collection: ^1.18.0 flutter_staggered_grid_view: 0.7.0
app_links: ^6.3.2 flutter_svg: ^2.0.15
# crop_your_image: 1.1.0 font_awesome_flutter: ^10.8.0
csv: 6.0.0 google_fonts: 6.2.1
device_info_plus: ^10.1.2 #11.2.2 material_symbols_icons: ^4.2784.0
fluttertoast: ^8.2.8
cupertino_icons: ^1.0.0
qr_flutter: ^4.1.0
percent_indicator: ^4.2.3
page_transition: ^2.2.1
share_plus: ^10.1.4
pdfx: ^2.8.0
dropdown_button2: ^2.3.9
# Firebase
firebase_core: ^3.4.0
firebase_messaging: ^15.1.0 firebase_messaging: ^15.1.0
dropdown_button2: 2.3.9 firebase_analytics: ^11.3.0
firebase_crashlytics: ^4.0.1
# Utilidades
app_links: ^6.3.3
collection: ^1.18.0
csv: 6.0.0
device_info_plus: ^10.1.2
easy_debounce: 2.0.3 easy_debounce: 2.0.3
equatable: ^2.0.6 equatable: ^2.0.6
file_picker: ^8.0.7 file_picker: ^8.0.7
# flutter_expandable_fab: ^2.1.0
firebase_analytics: ^11.3.0
flutter_animate: ^4.5.2 flutter_animate: ^4.5.2
# flutter_cache_manager: ^3.4.1
# flutter_plugin_android_lifecycle: ^2.0.23
share_plus: ^10.1.4
# connectivity_plus: ^6.0.5
flutter_secure_storage: ^10.0.0-beta.2 flutter_secure_storage: ^10.0.0-beta.2
flutter_secure_storage_linux: ^2.0.0 flutter_secure_storage_linux: ^2.0.0
flutter_secure_storage_macos: ^4.0.0 flutter_secure_storage_macos: ^4.0.0
flutter_secure_storage_platform_interface: ^2.0.1 flutter_secure_storage_platform_interface: ^2.0.1
flutter_secure_storage_web: ^2.0.0 flutter_secure_storage_web: ^2.0.0
flutter_secure_storage_windows: ^4.0.0 flutter_secure_storage_windows: ^4.0.0
flutter_spinkit: 5.2.1
flutter_staggered_grid_view: 0.7.0
flutter_svg: ^2.0.15
font_awesome_flutter: ^10.8.0
from_css_color: 2.0.0 from_css_color: 2.0.0
go_router: ^14.3.0 go_router: ^14.3.0
google_fonts: 6.2.1 http: ^1.3.0
http: 1.3.0
image_picker: 1.1.2 image_picker: 1.1.2
image_picker_android: ^0.8.12+15 image_picker_android: ^0.8.12+15
image_picker_for_web: ^3.0.5 image_picker_for_web: ^3.0.5
persistent_bottom_nav_bar: ^6.2.1
image_picker_ios: ^0.8.12+1 image_picker_ios: ^0.8.12+1
image_picker_platform_interface: ^2.10.1 image_picker_platform_interface: ^2.10.1
local_auth: ^2.2.0 local_auth: ^2.2.0
intl: ^0.19.0 intl: ^0.19.0
# camera: ^0.11.0+2
json_path: ^0.7.4 json_path: ^0.7.4
mime_type: ^1.0.1 mime_type: ^1.0.1
page_transition: ^2.2.1
path_provider: ^2.1.4 path_provider: ^2.1.4
path_provider_android: ^2.2.12 path_provider_android: ^2.2.12
google_mlkit_face_detection: ^0.12.0 google_mlkit_face_detection: ^0.12.0
path_provider_foundation: ^2.4.1 path_provider_foundation: ^2.4.1
path_provider_platform_interface: 2.1.2 path_provider_platform_interface: 2.1.2
percent_indicator: ^4.2.3
plugin_platform_interface: 2.1.8 plugin_platform_interface: 2.1.8
provider: 6.1.2
shared_preferences: ^2.3.2 shared_preferences: ^2.3.2
shared_preferences_android: ^2.3.3 shared_preferences_android: ^2.3.3
shared_preferences_foundation: ^2.5.3 shared_preferences_foundation: ^2.5.3
@ -85,69 +110,80 @@ dependencies:
url_launcher_android: ^6.3.12 url_launcher_android: ^6.3.12
url_launcher_ios: ^6.3.1 url_launcher_ios: ^6.3.1
url_launcher_platform_interface: 2.3.2 url_launcher_platform_interface: 2.3.2
infinite_scroll_pagination: ^4.1.0
# video_player: 2.8.7
# video_player_android: 2.5.0
# video_player_avfoundation: 2.6.1
# video_player_platform_interface: 6.2.2
# video_player_web: 2.3.1
material_symbols_icons: ^4.2784.0
fluttertoast: ^8.2.8
cupertino_icons: ^1.0.0
flutter_bloc: ^9.0.0
flutter_riverpod: ^2.5.1
qr_flutter: ^4.1.0
permission_handler: ^11.3.1 permission_handler: ^11.3.1
firebase_crashlytics: ^4.0.1
awesome_notifications: ^0.10.0 awesome_notifications: ^0.10.0
app_tracking_transparency: ^2.0.6 app_tracking_transparency: ^2.0.6
# dio: ^5.7.0
# crypto: ^3.0.5
freezed_annotation: ^2.4.4 freezed_annotation: ^2.4.4
package_info_plus: ^8.1.1 package_info_plus: ^8.1.1
# json_annotation: ^4.9.0 sliver_tools: ^0.2.12
json_annotation: ^4.9.0
# Dependências a partir de repositório Git (pacotes personalizados)
# base:
# git:
# url: 'git@github.com:FRE-Informatica/flutter-freaccess-base.git'
# path: 'packages/base'
# components:
# git:
# url: 'git@github.com:FRE-Informatica/flutter-freaccess-base.git'
# path: 'packages/components'
# templates:
# git:
# url: 'git@github.com:FRE-Informatica/flutter-freaccess-base.git'
# path: 'packages/templates'
# theme:
# git:
# url: 'git@github.com:FRE-Informatica/flutter-freaccess-base.git'
# path: 'packages/theme'
# Substituição de versões específicas de pacotes, se necessário
dependency_overrides: dependency_overrides:
http: 1.3.0 http: ^1.3.0
uuid: ^4.0.0 uuid: ^4.0.0
win32: 5.5.1 win32: 5.5.1
# Dependências para desenvolvimento e testes
dev_dependencies: dev_dependencies:
bloc_lint: ^0.1.0
rx_bloc_generator: ^8.0.1
flutter_launcher_icons: ^0.14.1 flutter_launcher_icons: ^0.14.1
flutter_lints: ^5.0.0 flutter_lints: ^5.0.0
image: ^4.3.0 image: ^4.3.0
lints: ^5.0.0 lints: ^5.0.0
# build_runner: ^2.4.13
mockito: ^5.4.4 mockito: ^5.4.4
integration_test: integration_test:
sdk: flutter sdk: flutter
flutter_test: flutter_test:
sdk: flutter sdk: flutter
build_runner: ^2.4.13 build_runner: ^2.4.14
freezed: ^2.5.7 freezed: ^3.0.0-0.0.dev
json_serializable: ^6.9.0 json_serializable: ^6.9.4
test: ^1.25.7 test: ^1.25.7
patrol: ^3.13.2 patrol: ^3.13.2
patrol_finders: ^2.6.0 patrol_finders: ^2.6.0
# Configuração do flutter_launcher_icons
flutter_launcher_icons: flutter_launcher_icons:
android: "launcher_icon" android: "launcher_icon" # Nome da pasta/ícone para Android
ios: true ios: true # Geração de ícones para iOS
web: web:
generate: true generate: true # Geração de ícones para Web
image_path: "assets/images/app_launcher_icon.svg" image_path: "assets/images/app_launcher_icon.svg"
adaptive_icon_background: "assets/images/adaptive_background_icon.svg" adaptive_icon_background: "assets/images/adaptive_background_icon.svg"
adaptive_icon_foreground: "assets/images/adaptive_foreground_icon.svg" adaptive_icon_foreground: "assets/images/adaptive_foreground_icon.svg"
# Configurações específicas do Flutter
flutter: flutter:
uses-material-design: true uses-material-design: true # Habilita o uso do Material Design
# Definição de assets (imagens, fontes, etc.)
assets: assets:
- assets/fonts/ - assets/fonts/
- assets/images/ - assets/images/
- assets/images/dark/ - assets/images/dark/
- assets/images/light/ - assets/images/light/
- assets/files/
# Configuração de fontes customizadas
fonts: fonts:
- family: "SF Pro" - family: "SF Pro"
fonts: fonts:
@ -160,10 +196,12 @@ fonts:
- family: Icons - family: Icons
fonts: fonts:
- asset: assets/fonts/icons.ttf - asset: assets/fonts/icons.ttf
- family: Menu - family: Menu
fonts: fonts:
- asset: assets/fonts/menu.ttf - asset: assets/fonts/menu.ttf
# Configuração do Patrol (ferramenta para testes de integração)
patrol: patrol:
app_name: FRE ACCESS HUB app_name: FRE ACCESS HUB
android: android: