dev #1
@ -13,17 +13,27 @@ import 'auth_local_datasource.dart';
|
|||||||
class ProductRemoteDatasource {
|
class ProductRemoteDatasource {
|
||||||
final Dio dio = DioClient.instance;
|
final Dio dio = DioClient.instance;
|
||||||
|
|
||||||
Future<Either<String, ProductResponseModel>> getProducts() async {
|
Future<Either<String, ProductResponseModel>> getProducts({
|
||||||
|
int page = 1,
|
||||||
|
int limit = Variables.defaultLimit,
|
||||||
|
String? categoryId,
|
||||||
|
}) async {
|
||||||
try {
|
try {
|
||||||
final authData = await AuthLocalDataSource().getAuthData();
|
final authData = await AuthLocalDataSource().getAuthData();
|
||||||
final url = '${Variables.baseUrl}/api/v1/products';
|
final url = '${Variables.baseUrl}/api/v1/products';
|
||||||
|
|
||||||
|
Map<String, dynamic> queryParameters = {
|
||||||
|
'page': page,
|
||||||
|
'limit': limit,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (categoryId != null) {
|
||||||
|
queryParameters['category_id'] = categoryId;
|
||||||
|
}
|
||||||
|
|
||||||
final response = await dio.get(
|
final response = await dio.get(
|
||||||
url,
|
url,
|
||||||
queryParameters: {
|
queryParameters: queryParameters,
|
||||||
'page': 1,
|
|
||||||
'limit': 30,
|
|
||||||
},
|
|
||||||
options: Options(
|
options: Options(
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': 'Bearer ${authData.token}',
|
'Authorization': 'Bearer ${authData.token}',
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/product_remote_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/product_remote_datasource.dart';
|
||||||
import 'package:enaklo_pos/data/models/response/product_response_model.dart';
|
import 'package:enaklo_pos/data/models/response/product_response_model.dart';
|
||||||
@ -9,15 +11,136 @@ part 'product_loader_bloc.freezed.dart';
|
|||||||
|
|
||||||
class ProductLoaderBloc extends Bloc<ProductLoaderEvent, ProductLoaderState> {
|
class ProductLoaderBloc extends Bloc<ProductLoaderEvent, ProductLoaderState> {
|
||||||
final ProductRemoteDatasource _productRemoteDatasource;
|
final ProductRemoteDatasource _productRemoteDatasource;
|
||||||
|
|
||||||
|
// Debouncing untuk mencegah multiple load more calls
|
||||||
|
Timer? _loadMoreDebounce;
|
||||||
|
bool _isLoadingMore = false;
|
||||||
|
|
||||||
ProductLoaderBloc(this._productRemoteDatasource)
|
ProductLoaderBloc(this._productRemoteDatasource)
|
||||||
: super(ProductLoaderState.initial()) {
|
: super(ProductLoaderState.initial()) {
|
||||||
on<_GetProduct>((event, emit) async {
|
on<_GetProduct>(_onGetProduct);
|
||||||
emit(const _Loading());
|
on<_LoadMore>(_onLoadMore);
|
||||||
final result = await _productRemoteDatasource.getProducts();
|
on<_Refresh>(_onRefresh);
|
||||||
result.fold(
|
}
|
||||||
(l) => emit(_Error(l)),
|
|
||||||
(r) => emit(_Loaded(r.data?.products ?? [])),
|
@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(
|
||||||
|
_GetProduct event,
|
||||||
|
Emitter<ProductLoaderState> 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(_Loaded(
|
||||||
|
products: products,
|
||||||
|
hasReachedMax: hasReachedMax,
|
||||||
|
currentPage: 1,
|
||||||
|
isLoadingMore: false,
|
||||||
|
));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load more with enhanced debouncing
|
||||||
|
Future<void> _onLoadMore(
|
||||||
|
_LoadMore event,
|
||||||
|
Emitter<ProductLoaderState> emit,
|
||||||
|
) async {
|
||||||
|
final currentState = state;
|
||||||
|
|
||||||
|
// Enhanced validation
|
||||||
|
if (currentState is! _Loaded ||
|
||||||
|
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(_Loaded(
|
||||||
|
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<ProductLoaderState> emit,
|
||||||
|
) async {
|
||||||
|
_isLoadingMore = false;
|
||||||
|
_loadMoreDebounce?.cancel();
|
||||||
|
add(const _GetProduct());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,32 +19,44 @@ mixin _$ProductLoaderEvent {
|
|||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() getProduct,
|
required TResult Function() getProduct,
|
||||||
|
required TResult Function() loadMore,
|
||||||
|
required TResult Function() refresh,
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? getProduct,
|
TResult? Function()? getProduct,
|
||||||
|
TResult? Function()? loadMore,
|
||||||
|
TResult? Function()? refresh,
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? getProduct,
|
TResult Function()? getProduct,
|
||||||
|
TResult Function()? loadMore,
|
||||||
|
TResult Function()? refresh,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult map<TResult extends Object?>({
|
TResult map<TResult extends Object?>({
|
||||||
required TResult Function(_GetProduct value) getProduct,
|
required TResult Function(_GetProduct value) getProduct,
|
||||||
|
required TResult Function(_LoadMore value) loadMore,
|
||||||
|
required TResult Function(_Refresh value) refresh,
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? mapOrNull<TResult extends Object?>({
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
TResult? Function(_GetProduct value)? getProduct,
|
TResult? Function(_GetProduct value)? getProduct,
|
||||||
|
TResult? Function(_LoadMore value)? loadMore,
|
||||||
|
TResult? Function(_Refresh value)? refresh,
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeMap<TResult extends Object?>({
|
TResult maybeMap<TResult extends Object?>({
|
||||||
TResult Function(_GetProduct value)? getProduct,
|
TResult Function(_GetProduct value)? getProduct,
|
||||||
|
TResult Function(_LoadMore value)? loadMore,
|
||||||
|
TResult Function(_Refresh value)? refresh,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@ -113,6 +125,8 @@ class _$GetProductImpl implements _GetProduct {
|
|||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() getProduct,
|
required TResult Function() getProduct,
|
||||||
|
required TResult Function() loadMore,
|
||||||
|
required TResult Function() refresh,
|
||||||
}) {
|
}) {
|
||||||
return getProduct();
|
return getProduct();
|
||||||
}
|
}
|
||||||
@ -121,6 +135,8 @@ class _$GetProductImpl implements _GetProduct {
|
|||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? getProduct,
|
TResult? Function()? getProduct,
|
||||||
|
TResult? Function()? loadMore,
|
||||||
|
TResult? Function()? refresh,
|
||||||
}) {
|
}) {
|
||||||
return getProduct?.call();
|
return getProduct?.call();
|
||||||
}
|
}
|
||||||
@ -129,6 +145,8 @@ class _$GetProductImpl implements _GetProduct {
|
|||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? getProduct,
|
TResult Function()? getProduct,
|
||||||
|
TResult Function()? loadMore,
|
||||||
|
TResult Function()? refresh,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (getProduct != null) {
|
if (getProduct != null) {
|
||||||
@ -141,6 +159,8 @@ class _$GetProductImpl implements _GetProduct {
|
|||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult map<TResult extends Object?>({
|
TResult map<TResult extends Object?>({
|
||||||
required TResult Function(_GetProduct value) getProduct,
|
required TResult Function(_GetProduct value) getProduct,
|
||||||
|
required TResult Function(_LoadMore value) loadMore,
|
||||||
|
required TResult Function(_Refresh value) refresh,
|
||||||
}) {
|
}) {
|
||||||
return getProduct(this);
|
return getProduct(this);
|
||||||
}
|
}
|
||||||
@ -149,6 +169,8 @@ class _$GetProductImpl implements _GetProduct {
|
|||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? mapOrNull<TResult extends Object?>({
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
TResult? Function(_GetProduct value)? getProduct,
|
TResult? Function(_GetProduct value)? getProduct,
|
||||||
|
TResult? Function(_LoadMore value)? loadMore,
|
||||||
|
TResult? Function(_Refresh value)? refresh,
|
||||||
}) {
|
}) {
|
||||||
return getProduct?.call(this);
|
return getProduct?.call(this);
|
||||||
}
|
}
|
||||||
@ -157,6 +179,8 @@ class _$GetProductImpl implements _GetProduct {
|
|||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeMap<TResult extends Object?>({
|
TResult maybeMap<TResult extends Object?>({
|
||||||
TResult Function(_GetProduct value)? getProduct,
|
TResult Function(_GetProduct value)? getProduct,
|
||||||
|
TResult Function(_LoadMore value)? loadMore,
|
||||||
|
TResult Function(_Refresh value)? refresh,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (getProduct != null) {
|
if (getProduct != null) {
|
||||||
@ -170,13 +194,237 @@ abstract class _GetProduct implements ProductLoaderEvent {
|
|||||||
const factory _GetProduct() = _$GetProductImpl;
|
const factory _GetProduct() = _$GetProductImpl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$LoadMoreImplCopyWith<$Res> {
|
||||||
|
factory _$$LoadMoreImplCopyWith(
|
||||||
|
_$LoadMoreImpl value, $Res Function(_$LoadMoreImpl) then) =
|
||||||
|
__$$LoadMoreImplCopyWithImpl<$Res>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$LoadMoreImplCopyWithImpl<$Res>
|
||||||
|
extends _$ProductLoaderEventCopyWithImpl<$Res, _$LoadMoreImpl>
|
||||||
|
implements _$$LoadMoreImplCopyWith<$Res> {
|
||||||
|
__$$LoadMoreImplCopyWithImpl(
|
||||||
|
_$LoadMoreImpl _value, $Res Function(_$LoadMoreImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of ProductLoaderEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$LoadMoreImpl implements _LoadMore {
|
||||||
|
const _$LoadMoreImpl();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'ProductLoaderEvent.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() getProduct,
|
||||||
|
required TResult Function() loadMore,
|
||||||
|
required TResult Function() refresh,
|
||||||
|
}) {
|
||||||
|
return loadMore();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function()? getProduct,
|
||||||
|
TResult? Function()? loadMore,
|
||||||
|
TResult? Function()? refresh,
|
||||||
|
}) {
|
||||||
|
return loadMore?.call();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function()? getProduct,
|
||||||
|
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(_GetProduct value) getProduct,
|
||||||
|
required TResult Function(_LoadMore value) loadMore,
|
||||||
|
required TResult Function(_Refresh value) refresh,
|
||||||
|
}) {
|
||||||
|
return loadMore(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(_GetProduct value)? getProduct,
|
||||||
|
TResult? Function(_LoadMore value)? loadMore,
|
||||||
|
TResult? Function(_Refresh value)? refresh,
|
||||||
|
}) {
|
||||||
|
return loadMore?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(_GetProduct value)? getProduct,
|
||||||
|
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 ProductLoaderEvent {
|
||||||
|
const factory _LoadMore() = _$LoadMoreImpl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$RefreshImplCopyWith<$Res> {
|
||||||
|
factory _$$RefreshImplCopyWith(
|
||||||
|
_$RefreshImpl value, $Res Function(_$RefreshImpl) then) =
|
||||||
|
__$$RefreshImplCopyWithImpl<$Res>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$RefreshImplCopyWithImpl<$Res>
|
||||||
|
extends _$ProductLoaderEventCopyWithImpl<$Res, _$RefreshImpl>
|
||||||
|
implements _$$RefreshImplCopyWith<$Res> {
|
||||||
|
__$$RefreshImplCopyWithImpl(
|
||||||
|
_$RefreshImpl _value, $Res Function(_$RefreshImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of ProductLoaderEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$RefreshImpl implements _Refresh {
|
||||||
|
const _$RefreshImpl();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'ProductLoaderEvent.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() getProduct,
|
||||||
|
required TResult Function() loadMore,
|
||||||
|
required TResult Function() refresh,
|
||||||
|
}) {
|
||||||
|
return refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function()? getProduct,
|
||||||
|
TResult? Function()? loadMore,
|
||||||
|
TResult? Function()? refresh,
|
||||||
|
}) {
|
||||||
|
return refresh?.call();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function()? getProduct,
|
||||||
|
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(_GetProduct value) getProduct,
|
||||||
|
required TResult Function(_LoadMore value) loadMore,
|
||||||
|
required TResult Function(_Refresh value) refresh,
|
||||||
|
}) {
|
||||||
|
return refresh(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(_GetProduct value)? getProduct,
|
||||||
|
TResult? Function(_LoadMore value)? loadMore,
|
||||||
|
TResult? Function(_Refresh value)? refresh,
|
||||||
|
}) {
|
||||||
|
return refresh?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(_GetProduct value)? getProduct,
|
||||||
|
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 ProductLoaderEvent {
|
||||||
|
const factory _Refresh() = _$RefreshImpl;
|
||||||
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$ProductLoaderState {
|
mixin _$ProductLoaderState {
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() initial,
|
required TResult Function() initial,
|
||||||
required TResult Function() loading,
|
required TResult Function() loading,
|
||||||
required TResult Function(List<Product> products) loaded,
|
required TResult Function(List<Product> products, bool hasReachedMax,
|
||||||
|
int currentPage, bool isLoadingMore)
|
||||||
|
loaded,
|
||||||
required TResult Function(String message) error,
|
required TResult Function(String message) error,
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@ -184,7 +432,9 @@ mixin _$ProductLoaderState {
|
|||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? initial,
|
TResult? Function()? initial,
|
||||||
TResult? Function()? loading,
|
TResult? Function()? loading,
|
||||||
TResult? Function(List<Product> products)? loaded,
|
TResult? Function(List<Product> products, bool hasReachedMax,
|
||||||
|
int currentPage, bool isLoadingMore)?
|
||||||
|
loaded,
|
||||||
TResult? Function(String message)? error,
|
TResult? Function(String message)? error,
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@ -192,7 +442,9 @@ mixin _$ProductLoaderState {
|
|||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? initial,
|
TResult Function()? initial,
|
||||||
TResult Function()? loading,
|
TResult Function()? loading,
|
||||||
TResult Function(List<Product> products)? loaded,
|
TResult Function(List<Product> products, bool hasReachedMax,
|
||||||
|
int currentPage, bool isLoadingMore)?
|
||||||
|
loaded,
|
||||||
TResult Function(String message)? error,
|
TResult Function(String message)? error,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) =>
|
}) =>
|
||||||
@ -288,7 +540,9 @@ class _$InitialImpl implements _Initial {
|
|||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() initial,
|
required TResult Function() initial,
|
||||||
required TResult Function() loading,
|
required TResult Function() loading,
|
||||||
required TResult Function(List<Product> products) loaded,
|
required TResult Function(List<Product> products, bool hasReachedMax,
|
||||||
|
int currentPage, bool isLoadingMore)
|
||||||
|
loaded,
|
||||||
required TResult Function(String message) error,
|
required TResult Function(String message) error,
|
||||||
}) {
|
}) {
|
||||||
return initial();
|
return initial();
|
||||||
@ -299,7 +553,9 @@ class _$InitialImpl implements _Initial {
|
|||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? initial,
|
TResult? Function()? initial,
|
||||||
TResult? Function()? loading,
|
TResult? Function()? loading,
|
||||||
TResult? Function(List<Product> products)? loaded,
|
TResult? Function(List<Product> products, bool hasReachedMax,
|
||||||
|
int currentPage, bool isLoadingMore)?
|
||||||
|
loaded,
|
||||||
TResult? Function(String message)? error,
|
TResult? Function(String message)? error,
|
||||||
}) {
|
}) {
|
||||||
return initial?.call();
|
return initial?.call();
|
||||||
@ -310,7 +566,9 @@ class _$InitialImpl implements _Initial {
|
|||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? initial,
|
TResult Function()? initial,
|
||||||
TResult Function()? loading,
|
TResult Function()? loading,
|
||||||
TResult Function(List<Product> products)? loaded,
|
TResult Function(List<Product> products, bool hasReachedMax,
|
||||||
|
int currentPage, bool isLoadingMore)?
|
||||||
|
loaded,
|
||||||
TResult Function(String message)? error,
|
TResult Function(String message)? error,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
@ -405,7 +663,9 @@ class _$LoadingImpl implements _Loading {
|
|||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() initial,
|
required TResult Function() initial,
|
||||||
required TResult Function() loading,
|
required TResult Function() loading,
|
||||||
required TResult Function(List<Product> products) loaded,
|
required TResult Function(List<Product> products, bool hasReachedMax,
|
||||||
|
int currentPage, bool isLoadingMore)
|
||||||
|
loaded,
|
||||||
required TResult Function(String message) error,
|
required TResult Function(String message) error,
|
||||||
}) {
|
}) {
|
||||||
return loading();
|
return loading();
|
||||||
@ -416,7 +676,9 @@ class _$LoadingImpl implements _Loading {
|
|||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? initial,
|
TResult? Function()? initial,
|
||||||
TResult? Function()? loading,
|
TResult? Function()? loading,
|
||||||
TResult? Function(List<Product> products)? loaded,
|
TResult? Function(List<Product> products, bool hasReachedMax,
|
||||||
|
int currentPage, bool isLoadingMore)?
|
||||||
|
loaded,
|
||||||
TResult? Function(String message)? error,
|
TResult? Function(String message)? error,
|
||||||
}) {
|
}) {
|
||||||
return loading?.call();
|
return loading?.call();
|
||||||
@ -427,7 +689,9 @@ class _$LoadingImpl implements _Loading {
|
|||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? initial,
|
TResult Function()? initial,
|
||||||
TResult Function()? loading,
|
TResult Function()? loading,
|
||||||
TResult Function(List<Product> products)? loaded,
|
TResult Function(List<Product> products, bool hasReachedMax,
|
||||||
|
int currentPage, bool isLoadingMore)?
|
||||||
|
loaded,
|
||||||
TResult Function(String message)? error,
|
TResult Function(String message)? error,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
@ -485,7 +749,11 @@ abstract class _$$LoadedImplCopyWith<$Res> {
|
|||||||
_$LoadedImpl value, $Res Function(_$LoadedImpl) then) =
|
_$LoadedImpl value, $Res Function(_$LoadedImpl) then) =
|
||||||
__$$LoadedImplCopyWithImpl<$Res>;
|
__$$LoadedImplCopyWithImpl<$Res>;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call({List<Product> products});
|
$Res call(
|
||||||
|
{List<Product> products,
|
||||||
|
bool hasReachedMax,
|
||||||
|
int currentPage,
|
||||||
|
bool isLoadingMore});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@ -502,12 +770,27 @@ class __$$LoadedImplCopyWithImpl<$Res>
|
|||||||
@override
|
@override
|
||||||
$Res call({
|
$Res call({
|
||||||
Object? products = null,
|
Object? products = null,
|
||||||
|
Object? hasReachedMax = null,
|
||||||
|
Object? currentPage = null,
|
||||||
|
Object? isLoadingMore = null,
|
||||||
}) {
|
}) {
|
||||||
return _then(_$LoadedImpl(
|
return _then(_$LoadedImpl(
|
||||||
null == products
|
products: null == products
|
||||||
? _value._products
|
? _value._products
|
||||||
: products // ignore: cast_nullable_to_non_nullable
|
: products // ignore: cast_nullable_to_non_nullable
|
||||||
as List<Product>,
|
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,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -515,7 +798,12 @@ class __$$LoadedImplCopyWithImpl<$Res>
|
|||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
|
||||||
class _$LoadedImpl implements _Loaded {
|
class _$LoadedImpl implements _Loaded {
|
||||||
const _$LoadedImpl(final List<Product> products) : _products = products;
|
const _$LoadedImpl(
|
||||||
|
{required final List<Product> products,
|
||||||
|
required this.hasReachedMax,
|
||||||
|
required this.currentPage,
|
||||||
|
required this.isLoadingMore})
|
||||||
|
: _products = products;
|
||||||
|
|
||||||
final List<Product> _products;
|
final List<Product> _products;
|
||||||
@override
|
@override
|
||||||
@ -525,9 +813,16 @@ class _$LoadedImpl implements _Loaded {
|
|||||||
return EqualUnmodifiableListView(_products);
|
return EqualUnmodifiableListView(_products);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
final bool hasReachedMax;
|
||||||
|
@override
|
||||||
|
final int currentPage;
|
||||||
|
@override
|
||||||
|
final bool isLoadingMore;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'ProductLoaderState.loaded(products: $products)';
|
return 'ProductLoaderState.loaded(products: $products, hasReachedMax: $hasReachedMax, currentPage: $currentPage, isLoadingMore: $isLoadingMore)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -535,12 +830,22 @@ class _$LoadedImpl implements _Loaded {
|
|||||||
return identical(this, other) ||
|
return identical(this, other) ||
|
||||||
(other.runtimeType == runtimeType &&
|
(other.runtimeType == runtimeType &&
|
||||||
other is _$LoadedImpl &&
|
other is _$LoadedImpl &&
|
||||||
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
|
@override
|
||||||
int get hashCode =>
|
int get hashCode => Object.hash(
|
||||||
Object.hash(runtimeType, const DeepCollectionEquality().hash(_products));
|
runtimeType,
|
||||||
|
const DeepCollectionEquality().hash(_products),
|
||||||
|
hasReachedMax,
|
||||||
|
currentPage,
|
||||||
|
isLoadingMore);
|
||||||
|
|
||||||
/// Create a copy of ProductLoaderState
|
/// Create a copy of ProductLoaderState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@ -555,10 +860,12 @@ class _$LoadedImpl implements _Loaded {
|
|||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() initial,
|
required TResult Function() initial,
|
||||||
required TResult Function() loading,
|
required TResult Function() loading,
|
||||||
required TResult Function(List<Product> products) loaded,
|
required TResult Function(List<Product> products, bool hasReachedMax,
|
||||||
|
int currentPage, bool isLoadingMore)
|
||||||
|
loaded,
|
||||||
required TResult Function(String message) error,
|
required TResult Function(String message) error,
|
||||||
}) {
|
}) {
|
||||||
return loaded(products);
|
return loaded(products, hasReachedMax, currentPage, isLoadingMore);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -566,10 +873,12 @@ class _$LoadedImpl implements _Loaded {
|
|||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? initial,
|
TResult? Function()? initial,
|
||||||
TResult? Function()? loading,
|
TResult? Function()? loading,
|
||||||
TResult? Function(List<Product> products)? loaded,
|
TResult? Function(List<Product> products, bool hasReachedMax,
|
||||||
|
int currentPage, bool isLoadingMore)?
|
||||||
|
loaded,
|
||||||
TResult? Function(String message)? error,
|
TResult? Function(String message)? error,
|
||||||
}) {
|
}) {
|
||||||
return loaded?.call(products);
|
return loaded?.call(products, hasReachedMax, currentPage, isLoadingMore);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -577,12 +886,14 @@ class _$LoadedImpl implements _Loaded {
|
|||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? initial,
|
TResult Function()? initial,
|
||||||
TResult Function()? loading,
|
TResult Function()? loading,
|
||||||
TResult Function(List<Product> products)? loaded,
|
TResult Function(List<Product> products, bool hasReachedMax,
|
||||||
|
int currentPage, bool isLoadingMore)?
|
||||||
|
loaded,
|
||||||
TResult Function(String message)? error,
|
TResult Function(String message)? error,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (loaded != null) {
|
if (loaded != null) {
|
||||||
return loaded(products);
|
return loaded(products, hasReachedMax, currentPage, isLoadingMore);
|
||||||
}
|
}
|
||||||
return orElse();
|
return orElse();
|
||||||
}
|
}
|
||||||
@ -626,9 +937,16 @@ class _$LoadedImpl implements _Loaded {
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract class _Loaded implements ProductLoaderState {
|
abstract class _Loaded implements ProductLoaderState {
|
||||||
const factory _Loaded(final List<Product> products) = _$LoadedImpl;
|
const factory _Loaded(
|
||||||
|
{required final List<Product> products,
|
||||||
|
required final bool hasReachedMax,
|
||||||
|
required final int currentPage,
|
||||||
|
required final bool isLoadingMore}) = _$LoadedImpl;
|
||||||
|
|
||||||
List<Product> get products;
|
List<Product> get products;
|
||||||
|
bool get hasReachedMax;
|
||||||
|
int get currentPage;
|
||||||
|
bool get isLoadingMore;
|
||||||
|
|
||||||
/// Create a copy of ProductLoaderState
|
/// Create a copy of ProductLoaderState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@ -707,7 +1025,9 @@ class _$ErrorImpl implements _Error {
|
|||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() initial,
|
required TResult Function() initial,
|
||||||
required TResult Function() loading,
|
required TResult Function() loading,
|
||||||
required TResult Function(List<Product> products) loaded,
|
required TResult Function(List<Product> products, bool hasReachedMax,
|
||||||
|
int currentPage, bool isLoadingMore)
|
||||||
|
loaded,
|
||||||
required TResult Function(String message) error,
|
required TResult Function(String message) error,
|
||||||
}) {
|
}) {
|
||||||
return error(message);
|
return error(message);
|
||||||
@ -718,7 +1038,9 @@ class _$ErrorImpl implements _Error {
|
|||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? initial,
|
TResult? Function()? initial,
|
||||||
TResult? Function()? loading,
|
TResult? Function()? loading,
|
||||||
TResult? Function(List<Product> products)? loaded,
|
TResult? Function(List<Product> products, bool hasReachedMax,
|
||||||
|
int currentPage, bool isLoadingMore)?
|
||||||
|
loaded,
|
||||||
TResult? Function(String message)? error,
|
TResult? Function(String message)? error,
|
||||||
}) {
|
}) {
|
||||||
return error?.call(message);
|
return error?.call(message);
|
||||||
@ -729,7 +1051,9 @@ class _$ErrorImpl implements _Error {
|
|||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? initial,
|
TResult Function()? initial,
|
||||||
TResult Function()? loading,
|
TResult Function()? loading,
|
||||||
TResult Function(List<Product> products)? loaded,
|
TResult Function(List<Product> products, bool hasReachedMax,
|
||||||
|
int currentPage, bool isLoadingMore)?
|
||||||
|
loaded,
|
||||||
TResult Function(String message)? error,
|
TResult Function(String message)? error,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
|
|||||||
@ -3,4 +3,6 @@ part of 'product_loader_bloc.dart';
|
|||||||
@freezed
|
@freezed
|
||||||
class ProductLoaderEvent with _$ProductLoaderEvent {
|
class ProductLoaderEvent with _$ProductLoaderEvent {
|
||||||
const factory ProductLoaderEvent.getProduct() = _GetProduct;
|
const factory ProductLoaderEvent.getProduct() = _GetProduct;
|
||||||
|
const factory ProductLoaderEvent.loadMore() = _LoadMore;
|
||||||
|
const factory ProductLoaderEvent.refresh() = _Refresh;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,11 @@ part of 'product_loader_bloc.dart';
|
|||||||
class ProductLoaderState with _$ProductLoaderState {
|
class ProductLoaderState with _$ProductLoaderState {
|
||||||
const factory ProductLoaderState.initial() = _Initial;
|
const factory ProductLoaderState.initial() = _Initial;
|
||||||
const factory ProductLoaderState.loading() = _Loading;
|
const factory ProductLoaderState.loading() = _Loading;
|
||||||
const factory ProductLoaderState.loaded(List<Product> products) = _Loaded;
|
const factory ProductLoaderState.loaded({
|
||||||
|
required List<Product> products,
|
||||||
|
required bool hasReachedMax,
|
||||||
|
required int currentPage,
|
||||||
|
required bool isLoadingMore,
|
||||||
|
}) = _Loaded;
|
||||||
const factory ProductLoaderState.error(String message) = _Error;
|
const factory ProductLoaderState.error(String message) = _Error;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,6 +38,7 @@ class HomePage extends StatefulWidget {
|
|||||||
|
|
||||||
class _HomePageState extends State<HomePage> {
|
class _HomePageState extends State<HomePage> {
|
||||||
final searchController = TextEditingController();
|
final searchController = TextEditingController();
|
||||||
|
final ScrollController scrollController = ScrollController();
|
||||||
String searchQuery = '';
|
String searchQuery = '';
|
||||||
|
|
||||||
test() async {
|
test() async {
|
||||||
@ -140,171 +141,204 @@ class _HomePageState extends State<HomePage> {
|
|||||||
),
|
),
|
||||||
BlocBuilder<ProductLoaderBloc, ProductLoaderState>(
|
BlocBuilder<ProductLoaderBloc, ProductLoaderState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return Expanded(
|
return NotificationListener<ScrollNotification>(
|
||||||
child: CustomTabBarV2(
|
onNotification: (notification) {
|
||||||
tabTitles: const [
|
return state.maybeWhen(
|
||||||
'Semua',
|
orElse: () => false,
|
||||||
'Makanan',
|
loaded: (products, hasReachedMax, currentPage,
|
||||||
'Minuman',
|
isLoadingMore) {
|
||||||
'Paket'
|
if (notification is ScrollEndNotification &&
|
||||||
],
|
scrollController.position.extentAfter ==
|
||||||
tabViews: [
|
0) {
|
||||||
// All Products Tab
|
context.read<ProductLoaderBloc>().add(
|
||||||
SizedBox(
|
const ProductLoaderEvent.loadMore());
|
||||||
child: state.maybeWhen(orElse: () {
|
return true;
|
||||||
return const Center(
|
}
|
||||||
child: CircularProgressIndicator(),
|
|
||||||
);
|
return true;
|
||||||
}, loading: () {
|
},
|
||||||
return const Center(
|
);
|
||||||
child: CircularProgressIndicator(),
|
},
|
||||||
);
|
child: Expanded(
|
||||||
}, loaded: (products) {
|
child: CustomTabBarV2(
|
||||||
final filteredProducts =
|
tabTitles: const [
|
||||||
_filterProducts(products);
|
'Semua',
|
||||||
if (filteredProducts.isEmpty) {
|
'Makanan',
|
||||||
|
'Minuman',
|
||||||
|
'Paket'
|
||||||
|
],
|
||||||
|
tabViews: [
|
||||||
|
// All Products Tab
|
||||||
|
SizedBox(
|
||||||
|
child: state.maybeWhen(orElse: () {
|
||||||
return const Center(
|
return const Center(
|
||||||
child: Text('No Items Found'),
|
child: CircularProgressIndicator(),
|
||||||
);
|
);
|
||||||
}
|
}, loading: () {
|
||||||
return GridView.builder(
|
|
||||||
itemCount: filteredProducts.length,
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
gridDelegate:
|
|
||||||
SliverGridDelegateWithMaxCrossAxisExtent(
|
|
||||||
maxCrossAxisExtent: 180,
|
|
||||||
mainAxisSpacing: 30,
|
|
||||||
crossAxisSpacing: 30,
|
|
||||||
childAspectRatio: 180 / 240,
|
|
||||||
),
|
|
||||||
itemBuilder: (context, index) =>
|
|
||||||
ProductCard(
|
|
||||||
data: filteredProducts[index],
|
|
||||||
onCartButton: () {},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
// Makanan Tab
|
|
||||||
SizedBox(
|
|
||||||
child: state.maybeWhen(orElse: () {
|
|
||||||
return const Center(
|
|
||||||
child: CircularProgressIndicator(),
|
|
||||||
);
|
|
||||||
}, loading: () {
|
|
||||||
return const Center(
|
|
||||||
child: CircularProgressIndicator(),
|
|
||||||
);
|
|
||||||
}, loaded: (products) {
|
|
||||||
if (products.isEmpty) {
|
|
||||||
return const Center(
|
return const Center(
|
||||||
child: Text('No Items'),
|
child: CircularProgressIndicator(),
|
||||||
);
|
);
|
||||||
}
|
}, loaded: (products, hashasReachedMax,
|
||||||
final filteredProducts =
|
currentPage, isLoadingMore) {
|
||||||
_filterProductsByCategory(products, 1);
|
final filteredProducts =
|
||||||
return filteredProducts.isEmpty
|
_filterProducts(products);
|
||||||
? const _IsEmpty()
|
if (filteredProducts.isEmpty) {
|
||||||
: GridView.builder(
|
return const Center(
|
||||||
itemCount: filteredProducts.length,
|
child: Text('No Items Found'),
|
||||||
padding: const EdgeInsets.all(16),
|
);
|
||||||
gridDelegate:
|
}
|
||||||
SliverGridDelegateWithMaxCrossAxisExtent(
|
return GridView.builder(
|
||||||
maxCrossAxisExtent:
|
itemCount: filteredProducts.length,
|
||||||
200, // Lebar maksimal tiap item (bisa kamu ubah)
|
controller: scrollController,
|
||||||
mainAxisSpacing: 30,
|
padding: const EdgeInsets.all(16),
|
||||||
crossAxisSpacing: 30,
|
gridDelegate:
|
||||||
childAspectRatio: 0.85,
|
SliverGridDelegateWithMaxCrossAxisExtent(
|
||||||
),
|
maxCrossAxisExtent: 180,
|
||||||
itemBuilder: (context, index) =>
|
mainAxisSpacing: 30,
|
||||||
ProductCard(
|
crossAxisSpacing: 30,
|
||||||
data: filteredProducts[index],
|
childAspectRatio: 180 / 240,
|
||||||
onCartButton: () {},
|
),
|
||||||
),
|
itemBuilder: (context, index) =>
|
||||||
);
|
ProductCard(
|
||||||
}),
|
data: filteredProducts[index],
|
||||||
),
|
onCartButton: () {},
|
||||||
// Minuman Tab
|
),
|
||||||
SizedBox(
|
);
|
||||||
child: state.maybeWhen(orElse: () {
|
}),
|
||||||
return const Center(
|
),
|
||||||
child: CircularProgressIndicator(),
|
// Makanan Tab
|
||||||
);
|
SizedBox(
|
||||||
}, loading: () {
|
child: state.maybeWhen(orElse: () {
|
||||||
return const Center(
|
|
||||||
child: CircularProgressIndicator(),
|
|
||||||
);
|
|
||||||
}, loaded: (products) {
|
|
||||||
if (products.isEmpty) {
|
|
||||||
return const Center(
|
return const Center(
|
||||||
child: Text('No Items'),
|
child: CircularProgressIndicator(),
|
||||||
);
|
);
|
||||||
}
|
}, loading: () {
|
||||||
final filteredProducts =
|
return const Center(
|
||||||
_filterProductsByCategory(products, 2);
|
child: CircularProgressIndicator(),
|
||||||
return filteredProducts.isEmpty
|
);
|
||||||
? const _IsEmpty()
|
}, loaded: (products, hashasReachedMax,
|
||||||
: GridView.builder(
|
currentPage, isLoadingMore) {
|
||||||
itemCount: filteredProducts.length,
|
if (products.isEmpty) {
|
||||||
padding: const EdgeInsets.all(16),
|
return const Center(
|
||||||
gridDelegate:
|
child: Text('No Items'),
|
||||||
SliverGridDelegateWithMaxCrossAxisExtent(
|
);
|
||||||
maxCrossAxisExtent:
|
}
|
||||||
200, // Lebar maksimal tiap item (bisa kamu ubah)
|
final filteredProducts =
|
||||||
mainAxisSpacing: 30,
|
_filterProductsByCategory(
|
||||||
crossAxisSpacing: 30,
|
products, 1);
|
||||||
childAspectRatio: 0.85,
|
return filteredProducts.isEmpty
|
||||||
),
|
? const _IsEmpty()
|
||||||
itemBuilder: (context, index) {
|
: GridView.builder(
|
||||||
return ProductCard(
|
itemCount:
|
||||||
|
filteredProducts.length,
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
controller: scrollController,
|
||||||
|
gridDelegate:
|
||||||
|
SliverGridDelegateWithMaxCrossAxisExtent(
|
||||||
|
maxCrossAxisExtent:
|
||||||
|
200, // Lebar maksimal tiap item (bisa kamu ubah)
|
||||||
|
mainAxisSpacing: 30,
|
||||||
|
crossAxisSpacing: 30,
|
||||||
|
childAspectRatio: 0.85,
|
||||||
|
),
|
||||||
|
itemBuilder: (context, index) =>
|
||||||
|
ProductCard(
|
||||||
data: filteredProducts[index],
|
data: filteredProducts[index],
|
||||||
onCartButton: () {},
|
onCartButton: () {},
|
||||||
);
|
),
|
||||||
},
|
);
|
||||||
);
|
}),
|
||||||
}),
|
),
|
||||||
),
|
// Minuman Tab
|
||||||
// Snack Tab
|
SizedBox(
|
||||||
SizedBox(
|
child: state.maybeWhen(orElse: () {
|
||||||
child: state.maybeWhen(orElse: () {
|
|
||||||
return const Center(
|
|
||||||
child: CircularProgressIndicator(),
|
|
||||||
);
|
|
||||||
}, loading: () {
|
|
||||||
return const Center(
|
|
||||||
child: CircularProgressIndicator(),
|
|
||||||
);
|
|
||||||
}, loaded: (products) {
|
|
||||||
if (products.isEmpty) {
|
|
||||||
return const Center(
|
return const Center(
|
||||||
child: Text('No Items'),
|
child: CircularProgressIndicator(),
|
||||||
);
|
);
|
||||||
}
|
}, loading: () {
|
||||||
final filteredProducts =
|
return const Center(
|
||||||
_filterProductsByCategory(products, 3);
|
child: CircularProgressIndicator(),
|
||||||
return filteredProducts.isEmpty
|
);
|
||||||
? const _IsEmpty()
|
}, loaded: (products, hashasReachedMax,
|
||||||
: GridView.builder(
|
currentPage, isLoadingMore) {
|
||||||
itemCount: filteredProducts.length,
|
if (products.isEmpty) {
|
||||||
padding: const EdgeInsets.all(16),
|
return const Center(
|
||||||
gridDelegate:
|
child: Text('No Items'),
|
||||||
SliverGridDelegateWithMaxCrossAxisExtent(
|
);
|
||||||
maxCrossAxisExtent:
|
}
|
||||||
200, // Lebar maksimal tiap item (bisa kamu ubah)
|
final filteredProducts =
|
||||||
mainAxisSpacing: 30,
|
_filterProductsByCategory(
|
||||||
crossAxisSpacing: 30,
|
products, 2);
|
||||||
childAspectRatio: 0.85,
|
return filteredProducts.isEmpty
|
||||||
),
|
? const _IsEmpty()
|
||||||
itemBuilder: (context, index) {
|
: GridView.builder(
|
||||||
return ProductCard(
|
itemCount:
|
||||||
data: filteredProducts[index],
|
filteredProducts.length,
|
||||||
onCartButton: () {},
|
padding: const EdgeInsets.all(16),
|
||||||
);
|
controller: scrollController,
|
||||||
},
|
gridDelegate:
|
||||||
);
|
SliverGridDelegateWithMaxCrossAxisExtent(
|
||||||
}),
|
maxCrossAxisExtent:
|
||||||
),
|
200, // Lebar maksimal tiap item (bisa kamu ubah)
|
||||||
],
|
mainAxisSpacing: 30,
|
||||||
|
crossAxisSpacing: 30,
|
||||||
|
childAspectRatio: 0.85,
|
||||||
|
),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return ProductCard(
|
||||||
|
data: filteredProducts[index],
|
||||||
|
onCartButton: () {},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
// Snack Tab
|
||||||
|
SizedBox(
|
||||||
|
child: state.maybeWhen(orElse: () {
|
||||||
|
return const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
);
|
||||||
|
}, loading: () {
|
||||||
|
return const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
);
|
||||||
|
}, loaded: (products, hashasReachedMax,
|
||||||
|
currentPage, isLoadingMore) {
|
||||||
|
if (products.isEmpty) {
|
||||||
|
return const Center(
|
||||||
|
child: Text('No Items'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final filteredProducts =
|
||||||
|
_filterProductsByCategory(
|
||||||
|
products, 3);
|
||||||
|
return filteredProducts.isEmpty
|
||||||
|
? const _IsEmpty()
|
||||||
|
: GridView.builder(
|
||||||
|
itemCount:
|
||||||
|
filteredProducts.length,
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
controller: scrollController,
|
||||||
|
gridDelegate:
|
||||||
|
SliverGridDelegateWithMaxCrossAxisExtent(
|
||||||
|
maxCrossAxisExtent:
|
||||||
|
200, // Lebar maksimal tiap item (bisa kamu ubah)
|
||||||
|
mainAxisSpacing: 30,
|
||||||
|
crossAxisSpacing: 30,
|
||||||
|
childAspectRatio: 0.85,
|
||||||
|
),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return ProductCard(
|
||||||
|
data: filteredProducts[index],
|
||||||
|
onCartButton: () {},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user