feat: setting product infinity scroll
This commit is contained in:
parent
7ca96d19db
commit
f0ff078e0e
@ -1,3 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:enaklo_pos/data/datasources/product_remote_datasource.dart';
|
||||
import 'package:enaklo_pos/data/models/response/product_response_model.dart';
|
||||
@ -8,19 +10,137 @@ part 'get_products_state.dart';
|
||||
part 'get_products_bloc.freezed.dart';
|
||||
|
||||
class GetProductsBloc extends Bloc<GetProductsEvent, GetProductsState> {
|
||||
final ProductRemoteDatasource datasource;
|
||||
GetProductsBloc(
|
||||
this.datasource,
|
||||
) : super(const _Initial()) {
|
||||
on<_Fetch>((event, emit) async {
|
||||
emit(const _Loading());
|
||||
final response = await datasource.getProducts();
|
||||
response.fold(
|
||||
(l) => emit(_Error(l)),
|
||||
(r) {
|
||||
emit(_Success(r.data!.products!));
|
||||
final ProductRemoteDatasource _productRemoteDatasource;
|
||||
|
||||
// Debouncing untuk mencegah multiple load more calls
|
||||
Timer? _loadMoreDebounce;
|
||||
bool _isLoadingMore = false;
|
||||
|
||||
GetProductsBloc(this._productRemoteDatasource)
|
||||
: super(GetProductsState.initial()) {
|
||||
on<_Fetch>(_onGetProduct);
|
||||
on<_LoadMore>(_onLoadMore);
|
||||
on<_Refresh>(_onRefresh);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
_loadMoreDebounce?.cancel();
|
||||
return super.close();
|
||||
}
|
||||
|
||||
// Debounce transformer untuk load more
|
||||
// EventTransformer<T> _debounceTransformer<T>() {
|
||||
// return (events, mapper) {
|
||||
// return events
|
||||
// .debounceTime(const Duration(milliseconds: 300))
|
||||
// .asyncExpand(mapper);
|
||||
// };
|
||||
// }
|
||||
|
||||
// Initial load
|
||||
Future<void> _onGetProduct(
|
||||
_Fetch event,
|
||||
Emitter<GetProductsState> emit,
|
||||
) async {
|
||||
emit(const _Loading());
|
||||
_isLoadingMore = false; // Reset loading state
|
||||
|
||||
final result = await _productRemoteDatasource.getProducts(
|
||||
page: 1,
|
||||
limit: 10,
|
||||
);
|
||||
|
||||
await result.fold(
|
||||
(failure) async => emit(_Error(failure)),
|
||||
(response) async {
|
||||
final products = response.data?.products ?? [];
|
||||
final hasReachedMax = products.length < 10;
|
||||
|
||||
emit(_Success(
|
||||
products: products,
|
||||
hasReachedMax: hasReachedMax,
|
||||
currentPage: 1,
|
||||
isLoadingMore: false,
|
||||
));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Load more with enhanced debouncing
|
||||
Future<void> _onLoadMore(
|
||||
_LoadMore event,
|
||||
Emitter<GetProductsState> emit,
|
||||
) async {
|
||||
final currentState = state;
|
||||
|
||||
// Enhanced validation
|
||||
if (currentState is! _Success ||
|
||||
currentState.hasReachedMax ||
|
||||
_isLoadingMore ||
|
||||
currentState.isLoadingMore) {
|
||||
return;
|
||||
}
|
||||
|
||||
_isLoadingMore = true;
|
||||
|
||||
// Emit loading more state
|
||||
emit(currentState.copyWith(isLoadingMore: true));
|
||||
|
||||
final nextPage = currentState.currentPage + 1;
|
||||
|
||||
try {
|
||||
final result = await _productRemoteDatasource.getProducts(
|
||||
page: nextPage,
|
||||
limit: 10,
|
||||
);
|
||||
|
||||
await result.fold(
|
||||
(failure) async {
|
||||
// On error, revert loading state but don't show error
|
||||
// Just silently fail and allow retry
|
||||
emit(currentState.copyWith(isLoadingMore: false));
|
||||
_isLoadingMore = false;
|
||||
},
|
||||
(response) async {
|
||||
final newProducts = response.data?.products ?? [];
|
||||
|
||||
// Prevent duplicate products
|
||||
final currentProductIds =
|
||||
currentState.products.map((p) => p.id).toSet();
|
||||
final filteredNewProducts = newProducts
|
||||
.where((product) => !currentProductIds.contains(product.id))
|
||||
.toList();
|
||||
|
||||
final allProducts = List<Product>.from(currentState.products)
|
||||
..addAll(filteredNewProducts);
|
||||
|
||||
final hasReachedMax = newProducts.length < 10;
|
||||
|
||||
emit(_Success(
|
||||
products: allProducts,
|
||||
hasReachedMax: hasReachedMax,
|
||||
currentPage: nextPage,
|
||||
isLoadingMore: false,
|
||||
));
|
||||
|
||||
_isLoadingMore = false;
|
||||
},
|
||||
);
|
||||
});
|
||||
} catch (e) {
|
||||
// Handle unexpected errors
|
||||
emit(currentState.copyWith(isLoadingMore: false));
|
||||
_isLoadingMore = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh data
|
||||
Future<void> _onRefresh(
|
||||
_Refresh event,
|
||||
Emitter<GetProductsState> emit,
|
||||
) async {
|
||||
_isLoadingMore = false;
|
||||
_loadMoreDebounce?.cancel();
|
||||
add(const _Fetch());
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,39 +18,45 @@ final _privateConstructorUsedError = UnsupportedError(
|
||||
mixin _$GetProductsEvent {
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function() started,
|
||||
required TResult Function() fetch,
|
||||
required TResult Function() loadMore,
|
||||
required TResult Function() refresh,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function()? started,
|
||||
TResult? Function()? fetch,
|
||||
TResult? Function()? loadMore,
|
||||
TResult? Function()? refresh,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function()? started,
|
||||
TResult Function()? fetch,
|
||||
TResult Function()? loadMore,
|
||||
TResult Function()? refresh,
|
||||
required TResult orElse(),
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(_Started value) started,
|
||||
required TResult Function(_Fetch value) fetch,
|
||||
required TResult Function(_LoadMore value) loadMore,
|
||||
required TResult Function(_Refresh value) refresh,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult? Function(_Started value)? started,
|
||||
TResult? Function(_Fetch value)? fetch,
|
||||
TResult? Function(_LoadMore value)? loadMore,
|
||||
TResult? Function(_Refresh value)? refresh,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(_Started value)? started,
|
||||
TResult Function(_Fetch value)? fetch,
|
||||
TResult Function(_LoadMore value)? loadMore,
|
||||
TResult Function(_Refresh value)? refresh,
|
||||
required TResult orElse(),
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@ -77,111 +83,6 @@ class _$GetProductsEventCopyWithImpl<$Res, $Val extends GetProductsEvent>
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$StartedImplCopyWith<$Res> {
|
||||
factory _$$StartedImplCopyWith(
|
||||
_$StartedImpl value, $Res Function(_$StartedImpl) then) =
|
||||
__$$StartedImplCopyWithImpl<$Res>;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$StartedImplCopyWithImpl<$Res>
|
||||
extends _$GetProductsEventCopyWithImpl<$Res, _$StartedImpl>
|
||||
implements _$$StartedImplCopyWith<$Res> {
|
||||
__$$StartedImplCopyWithImpl(
|
||||
_$StartedImpl _value, $Res Function(_$StartedImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of GetProductsEvent
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$StartedImpl implements _Started {
|
||||
const _$StartedImpl();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'GetProductsEvent.started()';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType && other is _$StartedImpl);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => runtimeType.hashCode;
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function() started,
|
||||
required TResult Function() fetch,
|
||||
}) {
|
||||
return started();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function()? started,
|
||||
TResult? Function()? fetch,
|
||||
}) {
|
||||
return started?.call();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function()? started,
|
||||
TResult Function()? fetch,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (started != null) {
|
||||
return started();
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(_Started value) started,
|
||||
required TResult Function(_Fetch value) fetch,
|
||||
}) {
|
||||
return started(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult? Function(_Started value)? started,
|
||||
TResult? Function(_Fetch value)? fetch,
|
||||
}) {
|
||||
return started?.call(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(_Started value)? started,
|
||||
TResult Function(_Fetch value)? fetch,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (started != null) {
|
||||
return started(this);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _Started implements GetProductsEvent {
|
||||
const factory _Started() = _$StartedImpl;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$FetchImplCopyWith<$Res> {
|
||||
factory _$$FetchImplCopyWith(
|
||||
@ -223,8 +124,9 @@ class _$FetchImpl implements _Fetch {
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function() started,
|
||||
required TResult Function() fetch,
|
||||
required TResult Function() loadMore,
|
||||
required TResult Function() refresh,
|
||||
}) {
|
||||
return fetch();
|
||||
}
|
||||
@ -232,8 +134,9 @@ class _$FetchImpl implements _Fetch {
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function()? started,
|
||||
TResult? Function()? fetch,
|
||||
TResult? Function()? loadMore,
|
||||
TResult? Function()? refresh,
|
||||
}) {
|
||||
return fetch?.call();
|
||||
}
|
||||
@ -241,8 +144,9 @@ class _$FetchImpl implements _Fetch {
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function()? started,
|
||||
TResult Function()? fetch,
|
||||
TResult Function()? loadMore,
|
||||
TResult Function()? refresh,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (fetch != null) {
|
||||
@ -254,8 +158,9 @@ class _$FetchImpl implements _Fetch {
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(_Started value) started,
|
||||
required TResult Function(_Fetch value) fetch,
|
||||
required TResult Function(_LoadMore value) loadMore,
|
||||
required TResult Function(_Refresh value) refresh,
|
||||
}) {
|
||||
return fetch(this);
|
||||
}
|
||||
@ -263,8 +168,9 @@ class _$FetchImpl implements _Fetch {
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult? Function(_Started value)? started,
|
||||
TResult? Function(_Fetch value)? fetch,
|
||||
TResult? Function(_LoadMore value)? loadMore,
|
||||
TResult? Function(_Refresh value)? refresh,
|
||||
}) {
|
||||
return fetch?.call(this);
|
||||
}
|
||||
@ -272,8 +178,9 @@ class _$FetchImpl implements _Fetch {
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(_Started value)? started,
|
||||
TResult Function(_Fetch value)? fetch,
|
||||
TResult Function(_LoadMore value)? loadMore,
|
||||
TResult Function(_Refresh value)? refresh,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (fetch != null) {
|
||||
@ -287,13 +194,237 @@ abstract class _Fetch implements GetProductsEvent {
|
||||
const factory _Fetch() = _$FetchImpl;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$LoadMoreImplCopyWith<$Res> {
|
||||
factory _$$LoadMoreImplCopyWith(
|
||||
_$LoadMoreImpl value, $Res Function(_$LoadMoreImpl) then) =
|
||||
__$$LoadMoreImplCopyWithImpl<$Res>;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$LoadMoreImplCopyWithImpl<$Res>
|
||||
extends _$GetProductsEventCopyWithImpl<$Res, _$LoadMoreImpl>
|
||||
implements _$$LoadMoreImplCopyWith<$Res> {
|
||||
__$$LoadMoreImplCopyWithImpl(
|
||||
_$LoadMoreImpl _value, $Res Function(_$LoadMoreImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of GetProductsEvent
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$LoadMoreImpl implements _LoadMore {
|
||||
const _$LoadMoreImpl();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'GetProductsEvent.loadMore()';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType && other is _$LoadMoreImpl);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => runtimeType.hashCode;
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function() fetch,
|
||||
required TResult Function() loadMore,
|
||||
required TResult Function() refresh,
|
||||
}) {
|
||||
return loadMore();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function()? fetch,
|
||||
TResult? Function()? loadMore,
|
||||
TResult? Function()? refresh,
|
||||
}) {
|
||||
return loadMore?.call();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function()? fetch,
|
||||
TResult Function()? loadMore,
|
||||
TResult Function()? refresh,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (loadMore != null) {
|
||||
return loadMore();
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(_Fetch value) fetch,
|
||||
required TResult Function(_LoadMore value) loadMore,
|
||||
required TResult Function(_Refresh value) refresh,
|
||||
}) {
|
||||
return loadMore(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult? Function(_Fetch value)? fetch,
|
||||
TResult? Function(_LoadMore value)? loadMore,
|
||||
TResult? Function(_Refresh value)? refresh,
|
||||
}) {
|
||||
return loadMore?.call(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(_Fetch value)? fetch,
|
||||
TResult Function(_LoadMore value)? loadMore,
|
||||
TResult Function(_Refresh value)? refresh,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (loadMore != null) {
|
||||
return loadMore(this);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _LoadMore implements GetProductsEvent {
|
||||
const factory _LoadMore() = _$LoadMoreImpl;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$RefreshImplCopyWith<$Res> {
|
||||
factory _$$RefreshImplCopyWith(
|
||||
_$RefreshImpl value, $Res Function(_$RefreshImpl) then) =
|
||||
__$$RefreshImplCopyWithImpl<$Res>;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$RefreshImplCopyWithImpl<$Res>
|
||||
extends _$GetProductsEventCopyWithImpl<$Res, _$RefreshImpl>
|
||||
implements _$$RefreshImplCopyWith<$Res> {
|
||||
__$$RefreshImplCopyWithImpl(
|
||||
_$RefreshImpl _value, $Res Function(_$RefreshImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of GetProductsEvent
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$RefreshImpl implements _Refresh {
|
||||
const _$RefreshImpl();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'GetProductsEvent.refresh()';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType && other is _$RefreshImpl);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => runtimeType.hashCode;
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function() fetch,
|
||||
required TResult Function() loadMore,
|
||||
required TResult Function() refresh,
|
||||
}) {
|
||||
return refresh();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function()? fetch,
|
||||
TResult? Function()? loadMore,
|
||||
TResult? Function()? refresh,
|
||||
}) {
|
||||
return refresh?.call();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function()? fetch,
|
||||
TResult Function()? loadMore,
|
||||
TResult Function()? refresh,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (refresh != null) {
|
||||
return refresh();
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(_Fetch value) fetch,
|
||||
required TResult Function(_LoadMore value) loadMore,
|
||||
required TResult Function(_Refresh value) refresh,
|
||||
}) {
|
||||
return refresh(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult? Function(_Fetch value)? fetch,
|
||||
TResult? Function(_LoadMore value)? loadMore,
|
||||
TResult? Function(_Refresh value)? refresh,
|
||||
}) {
|
||||
return refresh?.call(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(_Fetch value)? fetch,
|
||||
TResult Function(_LoadMore value)? loadMore,
|
||||
TResult Function(_Refresh value)? refresh,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (refresh != null) {
|
||||
return refresh(this);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _Refresh implements GetProductsEvent {
|
||||
const factory _Refresh() = _$RefreshImpl;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$GetProductsState {
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function() initial,
|
||||
required TResult Function() loading,
|
||||
required TResult Function(List<Product> products) success,
|
||||
required TResult Function(List<Product> products, bool hasReachedMax,
|
||||
int currentPage, bool isLoadingMore)
|
||||
success,
|
||||
required TResult Function(String message) error,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@ -301,7 +432,9 @@ mixin _$GetProductsState {
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function()? initial,
|
||||
TResult? Function()? loading,
|
||||
TResult? Function(List<Product> products)? success,
|
||||
TResult? Function(List<Product> products, bool hasReachedMax,
|
||||
int currentPage, bool isLoadingMore)?
|
||||
success,
|
||||
TResult? Function(String message)? error,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@ -309,7 +442,9 @@ mixin _$GetProductsState {
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function()? initial,
|
||||
TResult Function()? loading,
|
||||
TResult Function(List<Product> products)? success,
|
||||
TResult Function(List<Product> products, bool hasReachedMax,
|
||||
int currentPage, bool isLoadingMore)?
|
||||
success,
|
||||
TResult Function(String message)? error,
|
||||
required TResult orElse(),
|
||||
}) =>
|
||||
@ -405,7 +540,9 @@ class _$InitialImpl implements _Initial {
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function() initial,
|
||||
required TResult Function() loading,
|
||||
required TResult Function(List<Product> products) success,
|
||||
required TResult Function(List<Product> products, bool hasReachedMax,
|
||||
int currentPage, bool isLoadingMore)
|
||||
success,
|
||||
required TResult Function(String message) error,
|
||||
}) {
|
||||
return initial();
|
||||
@ -416,7 +553,9 @@ class _$InitialImpl implements _Initial {
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function()? initial,
|
||||
TResult? Function()? loading,
|
||||
TResult? Function(List<Product> products)? success,
|
||||
TResult? Function(List<Product> products, bool hasReachedMax,
|
||||
int currentPage, bool isLoadingMore)?
|
||||
success,
|
||||
TResult? Function(String message)? error,
|
||||
}) {
|
||||
return initial?.call();
|
||||
@ -427,7 +566,9 @@ class _$InitialImpl implements _Initial {
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function()? initial,
|
||||
TResult Function()? loading,
|
||||
TResult Function(List<Product> products)? success,
|
||||
TResult Function(List<Product> products, bool hasReachedMax,
|
||||
int currentPage, bool isLoadingMore)?
|
||||
success,
|
||||
TResult Function(String message)? error,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
@ -522,7 +663,9 @@ class _$LoadingImpl implements _Loading {
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function() initial,
|
||||
required TResult Function() loading,
|
||||
required TResult Function(List<Product> products) success,
|
||||
required TResult Function(List<Product> products, bool hasReachedMax,
|
||||
int currentPage, bool isLoadingMore)
|
||||
success,
|
||||
required TResult Function(String message) error,
|
||||
}) {
|
||||
return loading();
|
||||
@ -533,7 +676,9 @@ class _$LoadingImpl implements _Loading {
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function()? initial,
|
||||
TResult? Function()? loading,
|
||||
TResult? Function(List<Product> products)? success,
|
||||
TResult? Function(List<Product> products, bool hasReachedMax,
|
||||
int currentPage, bool isLoadingMore)?
|
||||
success,
|
||||
TResult? Function(String message)? error,
|
||||
}) {
|
||||
return loading?.call();
|
||||
@ -544,7 +689,9 @@ class _$LoadingImpl implements _Loading {
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function()? initial,
|
||||
TResult Function()? loading,
|
||||
TResult Function(List<Product> products)? success,
|
||||
TResult Function(List<Product> products, bool hasReachedMax,
|
||||
int currentPage, bool isLoadingMore)?
|
||||
success,
|
||||
TResult Function(String message)? error,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
@ -602,7 +749,11 @@ abstract class _$$SuccessImplCopyWith<$Res> {
|
||||
_$SuccessImpl value, $Res Function(_$SuccessImpl) then) =
|
||||
__$$SuccessImplCopyWithImpl<$Res>;
|
||||
@useResult
|
||||
$Res call({List<Product> products});
|
||||
$Res call(
|
||||
{List<Product> products,
|
||||
bool hasReachedMax,
|
||||
int currentPage,
|
||||
bool isLoadingMore});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@ -619,12 +770,27 @@ class __$$SuccessImplCopyWithImpl<$Res>
|
||||
@override
|
||||
$Res call({
|
||||
Object? products = null,
|
||||
Object? hasReachedMax = null,
|
||||
Object? currentPage = null,
|
||||
Object? isLoadingMore = null,
|
||||
}) {
|
||||
return _then(_$SuccessImpl(
|
||||
null == products
|
||||
products: null == products
|
||||
? _value._products
|
||||
: products // ignore: cast_nullable_to_non_nullable
|
||||
as List<Product>,
|
||||
hasReachedMax: null == hasReachedMax
|
||||
? _value.hasReachedMax
|
||||
: hasReachedMax // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
currentPage: null == currentPage
|
||||
? _value.currentPage
|
||||
: currentPage // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
isLoadingMore: null == isLoadingMore
|
||||
? _value.isLoadingMore
|
||||
: isLoadingMore // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -632,7 +798,12 @@ class __$$SuccessImplCopyWithImpl<$Res>
|
||||
/// @nodoc
|
||||
|
||||
class _$SuccessImpl implements _Success {
|
||||
const _$SuccessImpl(final List<Product> products) : _products = products;
|
||||
const _$SuccessImpl(
|
||||
{required final List<Product> products,
|
||||
required this.hasReachedMax,
|
||||
required this.currentPage,
|
||||
required this.isLoadingMore})
|
||||
: _products = products;
|
||||
|
||||
final List<Product> _products;
|
||||
@override
|
||||
@ -642,9 +813,16 @@ class _$SuccessImpl implements _Success {
|
||||
return EqualUnmodifiableListView(_products);
|
||||
}
|
||||
|
||||
@override
|
||||
final bool hasReachedMax;
|
||||
@override
|
||||
final int currentPage;
|
||||
@override
|
||||
final bool isLoadingMore;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'GetProductsState.success(products: $products)';
|
||||
return 'GetProductsState.success(products: $products, hasReachedMax: $hasReachedMax, currentPage: $currentPage, isLoadingMore: $isLoadingMore)';
|
||||
}
|
||||
|
||||
@override
|
||||
@ -652,12 +830,22 @@ class _$SuccessImpl implements _Success {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$SuccessImpl &&
|
||||
const DeepCollectionEquality().equals(other._products, _products));
|
||||
const DeepCollectionEquality().equals(other._products, _products) &&
|
||||
(identical(other.hasReachedMax, hasReachedMax) ||
|
||||
other.hasReachedMax == hasReachedMax) &&
|
||||
(identical(other.currentPage, currentPage) ||
|
||||
other.currentPage == currentPage) &&
|
||||
(identical(other.isLoadingMore, isLoadingMore) ||
|
||||
other.isLoadingMore == isLoadingMore));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, const DeepCollectionEquality().hash(_products));
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(_products),
|
||||
hasReachedMax,
|
||||
currentPage,
|
||||
isLoadingMore);
|
||||
|
||||
/// Create a copy of GetProductsState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@ -672,10 +860,12 @@ class _$SuccessImpl implements _Success {
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function() initial,
|
||||
required TResult Function() loading,
|
||||
required TResult Function(List<Product> products) success,
|
||||
required TResult Function(List<Product> products, bool hasReachedMax,
|
||||
int currentPage, bool isLoadingMore)
|
||||
success,
|
||||
required TResult Function(String message) error,
|
||||
}) {
|
||||
return success(products);
|
||||
return success(products, hasReachedMax, currentPage, isLoadingMore);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -683,10 +873,12 @@ class _$SuccessImpl implements _Success {
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function()? initial,
|
||||
TResult? Function()? loading,
|
||||
TResult? Function(List<Product> products)? success,
|
||||
TResult? Function(List<Product> products, bool hasReachedMax,
|
||||
int currentPage, bool isLoadingMore)?
|
||||
success,
|
||||
TResult? Function(String message)? error,
|
||||
}) {
|
||||
return success?.call(products);
|
||||
return success?.call(products, hasReachedMax, currentPage, isLoadingMore);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -694,12 +886,14 @@ class _$SuccessImpl implements _Success {
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function()? initial,
|
||||
TResult Function()? loading,
|
||||
TResult Function(List<Product> products)? success,
|
||||
TResult Function(List<Product> products, bool hasReachedMax,
|
||||
int currentPage, bool isLoadingMore)?
|
||||
success,
|
||||
TResult Function(String message)? error,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (success != null) {
|
||||
return success(products);
|
||||
return success(products, hasReachedMax, currentPage, isLoadingMore);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
@ -743,9 +937,16 @@ class _$SuccessImpl implements _Success {
|
||||
}
|
||||
|
||||
abstract class _Success implements GetProductsState {
|
||||
const factory _Success(final List<Product> products) = _$SuccessImpl;
|
||||
const factory _Success(
|
||||
{required final List<Product> products,
|
||||
required final bool hasReachedMax,
|
||||
required final int currentPage,
|
||||
required final bool isLoadingMore}) = _$SuccessImpl;
|
||||
|
||||
List<Product> get products;
|
||||
bool get hasReachedMax;
|
||||
int get currentPage;
|
||||
bool get isLoadingMore;
|
||||
|
||||
/// Create a copy of GetProductsState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@ -824,7 +1025,9 @@ class _$ErrorImpl implements _Error {
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function() initial,
|
||||
required TResult Function() loading,
|
||||
required TResult Function(List<Product> products) success,
|
||||
required TResult Function(List<Product> products, bool hasReachedMax,
|
||||
int currentPage, bool isLoadingMore)
|
||||
success,
|
||||
required TResult Function(String message) error,
|
||||
}) {
|
||||
return error(message);
|
||||
@ -835,7 +1038,9 @@ class _$ErrorImpl implements _Error {
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function()? initial,
|
||||
TResult? Function()? loading,
|
||||
TResult? Function(List<Product> products)? success,
|
||||
TResult? Function(List<Product> products, bool hasReachedMax,
|
||||
int currentPage, bool isLoadingMore)?
|
||||
success,
|
||||
TResult? Function(String message)? error,
|
||||
}) {
|
||||
return error?.call(message);
|
||||
@ -846,7 +1051,9 @@ class _$ErrorImpl implements _Error {
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function()? initial,
|
||||
TResult Function()? loading,
|
||||
TResult Function(List<Product> products)? success,
|
||||
TResult Function(List<Product> products, bool hasReachedMax,
|
||||
int currentPage, bool isLoadingMore)?
|
||||
success,
|
||||
TResult Function(String message)? error,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
|
||||
@ -2,6 +2,7 @@ part of 'get_products_bloc.dart';
|
||||
|
||||
@freezed
|
||||
class GetProductsEvent with _$GetProductsEvent {
|
||||
const factory GetProductsEvent.started() = _Started;
|
||||
const factory GetProductsEvent.fetch() = _Fetch;
|
||||
const factory GetProductsEvent.loadMore() = _LoadMore;
|
||||
const factory GetProductsEvent.refresh() = _Refresh;
|
||||
}
|
||||
|
||||
@ -4,6 +4,11 @@ part of 'get_products_bloc.dart';
|
||||
class GetProductsState with _$GetProductsState {
|
||||
const factory GetProductsState.initial() = _Initial;
|
||||
const factory GetProductsState.loading() = _Loading;
|
||||
const factory GetProductsState.success(List<Product> products) = _Success;
|
||||
const factory GetProductsState.success({
|
||||
required List<Product> products,
|
||||
required bool hasReachedMax,
|
||||
required int currentPage,
|
||||
required bool isLoadingMore,
|
||||
}) = _Success;
|
||||
const factory GetProductsState.error(String message) = _Error;
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@ class ProductPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _ProductPageState extends State<ProductPage> {
|
||||
ScrollController scrollController = ScrollController();
|
||||
@override
|
||||
void initState() {
|
||||
context.read<GetProductsBloc>().add(const GetProductsEvent.fetch());
|
||||
@ -64,48 +65,66 @@ class _ProductPageState extends State<ProductPage> {
|
||||
Expanded(
|
||||
child: BlocBuilder<GetProductsBloc, GetProductsState>(
|
||||
builder: (context, state) {
|
||||
return state.maybeWhen(orElse: () {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
}, success: (products) {
|
||||
return GridView.builder(
|
||||
padding: EdgeInsets.all(16),
|
||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: 200,
|
||||
mainAxisSpacing: 30,
|
||||
crossAxisSpacing: 30,
|
||||
childAspectRatio: 0.85,
|
||||
),
|
||||
itemCount: products.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final item = products[index];
|
||||
return MenuProductItem(
|
||||
data: item,
|
||||
onTapEdit: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(
|
||||
create: (context) => UpdateProductBloc(
|
||||
ProductRemoteDatasource()),
|
||||
),
|
||||
BlocProvider.value(
|
||||
value: context.read<SyncProductBloc>(),
|
||||
),
|
||||
BlocProvider.value(
|
||||
value: context.read<GetProductsBloc>(),
|
||||
),
|
||||
],
|
||||
child: FormProductDialog(product: item),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
return NotificationListener<ScrollNotification>(
|
||||
onNotification: (notification) {
|
||||
return state.maybeWhen(
|
||||
orElse: () => false,
|
||||
success: (products, hasReachedMax, currentPage, _) {
|
||||
if (notification is ScrollEndNotification &&
|
||||
scrollController.position.extentAfter == 0) {
|
||||
context
|
||||
.read<GetProductsBloc>()
|
||||
.add(const GetProductsEvent.loadMore());
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
},
|
||||
child: state.maybeWhen(orElse: () {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
}, success: (products, hasReachedMax, currentPage, _) {
|
||||
return GridView.builder(
|
||||
padding: EdgeInsets.all(16),
|
||||
controller: scrollController,
|
||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: 200,
|
||||
mainAxisSpacing: 30,
|
||||
crossAxisSpacing: 30,
|
||||
childAspectRatio: 0.85,
|
||||
),
|
||||
itemCount: products.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final item = products[index];
|
||||
return MenuProductItem(
|
||||
data: item,
|
||||
onTapEdit: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(
|
||||
create: (context) => UpdateProductBloc(
|
||||
ProductRemoteDatasource()),
|
||||
),
|
||||
BlocProvider.value(
|
||||
value: context.read<SyncProductBloc>(),
|
||||
),
|
||||
BlocProvider.value(
|
||||
value: context.read<GetProductsBloc>(),
|
||||
),
|
||||
],
|
||||
child: FormProductDialog(product: item),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user