feat: order infinty scroll

This commit is contained in:
efrilm 2025-08-06 15:46:22 +07:00
parent be803bbe4f
commit 7ee7db225c
7 changed files with 764 additions and 132 deletions

View File

@ -66,7 +66,13 @@ class _PaymentAddOrderDialogState extends State<PaymentAddOrderDialog> {
Center(child: const CircularProgressIndicator()),
loading: () =>
Center(child: const CircularProgressIndicator()),
loaded: (orders, totalOrder) {
loaded: (
orders,
totalOrder,
_,
__,
___,
) {
final availableOrders = orders;
if (selectOrder == null && availableOrders.isNotEmpty) {

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:enaklo_pos/data/datasources/order_remote_datasource.dart';
import 'package:enaklo_pos/data/models/response/order_response_model.dart';
@ -9,24 +11,15 @@ part 'order_loader_bloc.freezed.dart';
class OrderLoaderBloc extends Bloc<OrderLoaderEvent, OrderLoaderState> {
final OrderRemoteDatasource _orderRemoteDatasource;
Timer? _loadMoreDebounce;
bool _isLoadingMore = false;
OrderLoaderBloc(this._orderRemoteDatasource)
: super(OrderLoaderState.initial()) {
on<_GetByStatus>((event, emit) async {
emit(const _Loading());
final result = await _orderRemoteDatasource.getOrder(
status: event.status,
limit: 20,
dateFrom: event.dateFrom,
dateTo: event.dateTo,
);
result.fold(
(l) => emit(_Error(l)),
(r) => emit(_Loaded(
r.data?.orders ?? [],
r.data?.totalCount ?? 0,
)),
);
});
on<_GetByStatus>(_onGetOrderByStatus);
on<_LoadMore>(_onLoadMore);
on<_Refresh>(_onRefresh);
on<_GetById>((event, emit) async {
emit(const _Loading());
final result =
@ -37,4 +30,138 @@ class OrderLoaderBloc extends Bloc<OrderLoaderEvent, OrderLoaderState> {
);
});
}
@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> _onGetOrderByStatus(
_GetByStatus event,
Emitter<OrderLoaderState> emit,
) async {
emit(const _Loading());
_isLoadingMore = false; // Reset loading state
final result = await _orderRemoteDatasource.getOrder(
page: 1,
limit: 10,
status: event.status,
dateFrom: event.dateFrom,
dateTo: event.dateTo,
);
await result.fold(
(failure) async => emit(_Error(failure)),
(response) async {
final orders = response.data?.orders ?? [];
final hasReachedMax = orders.length < 10;
emit(_Loaded(
orders: orders,
totalOrder: response.data?.totalCount ?? 0,
hasReachedMax: hasReachedMax,
currentPage: 1,
isLoadingMore: false,
));
},
);
}
// Load more with enhanced debouncing
Future<void> _onLoadMore(
_LoadMore event,
Emitter<OrderLoaderState> 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 _orderRemoteDatasource.getOrder(
page: nextPage,
limit: 10,
status: event.status,
dateFrom: event.dateFrom,
dateTo: event.dateTo,
);
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 newOrders = response.data?.orders ?? [];
// Prevent duplicate orders
final currentOrderIds = currentState.orders.map((p) => p.id).toSet();
final filteredNewOrders = newOrders
.where((order) => !currentOrderIds.contains(order.id))
.toList();
final allOrders = List<Order>.from(currentState.orders)
..addAll(filteredNewOrders);
final hasReachedMax = newOrders.length < 10;
emit(_Loaded(
orders: allOrders,
totalOrder: response.data?.totalCount ?? 0,
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<OrderLoaderState> emit,
) async {
_isLoadingMore = false;
_loadMoreDebounce?.cancel();
add(
_GetByStatus(
event.status,
dateFrom: DateTime.now(),
dateTo: DateTime.now(),
),
);
}
}

View File

@ -21,6 +21,9 @@ mixin _$OrderLoaderEvent {
required TResult Function(String status, DateTime dateFrom, DateTime dateTo)
getByStatus,
required TResult Function(String id) getById,
required TResult Function(String status, DateTime dateFrom, DateTime dateTo)
loadMore,
required TResult Function(String status) refresh,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
@ -28,6 +31,9 @@ mixin _$OrderLoaderEvent {
TResult? Function(String status, DateTime dateFrom, DateTime dateTo)?
getByStatus,
TResult? Function(String id)? getById,
TResult? Function(String status, DateTime dateFrom, DateTime dateTo)?
loadMore,
TResult? Function(String status)? refresh,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
@ -35,6 +41,9 @@ mixin _$OrderLoaderEvent {
TResult Function(String status, DateTime dateFrom, DateTime dateTo)?
getByStatus,
TResult Function(String id)? getById,
TResult Function(String status, DateTime dateFrom, DateTime dateTo)?
loadMore,
TResult Function(String status)? refresh,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@ -42,18 +51,24 @@ mixin _$OrderLoaderEvent {
TResult map<TResult extends Object?>({
required TResult Function(_GetByStatus value) getByStatus,
required TResult Function(_GetById value) getById,
required TResult Function(_LoadMore value) loadMore,
required TResult Function(_Refresh value) refresh,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_GetByStatus value)? getByStatus,
TResult? Function(_GetById value)? getById,
TResult? Function(_LoadMore value)? loadMore,
TResult? Function(_Refresh value)? refresh,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_GetByStatus value)? getByStatus,
TResult Function(_GetById value)? getById,
TResult Function(_LoadMore value)? loadMore,
TResult Function(_Refresh value)? refresh,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@ -169,6 +184,9 @@ class _$GetByStatusImpl implements _GetByStatus {
required TResult Function(String status, DateTime dateFrom, DateTime dateTo)
getByStatus,
required TResult Function(String id) getById,
required TResult Function(String status, DateTime dateFrom, DateTime dateTo)
loadMore,
required TResult Function(String status) refresh,
}) {
return getByStatus(status, dateFrom, dateTo);
}
@ -179,6 +197,9 @@ class _$GetByStatusImpl implements _GetByStatus {
TResult? Function(String status, DateTime dateFrom, DateTime dateTo)?
getByStatus,
TResult? Function(String id)? getById,
TResult? Function(String status, DateTime dateFrom, DateTime dateTo)?
loadMore,
TResult? Function(String status)? refresh,
}) {
return getByStatus?.call(status, dateFrom, dateTo);
}
@ -189,6 +210,9 @@ class _$GetByStatusImpl implements _GetByStatus {
TResult Function(String status, DateTime dateFrom, DateTime dateTo)?
getByStatus,
TResult Function(String id)? getById,
TResult Function(String status, DateTime dateFrom, DateTime dateTo)?
loadMore,
TResult Function(String status)? refresh,
required TResult orElse(),
}) {
if (getByStatus != null) {
@ -202,6 +226,8 @@ class _$GetByStatusImpl implements _GetByStatus {
TResult map<TResult extends Object?>({
required TResult Function(_GetByStatus value) getByStatus,
required TResult Function(_GetById value) getById,
required TResult Function(_LoadMore value) loadMore,
required TResult Function(_Refresh value) refresh,
}) {
return getByStatus(this);
}
@ -211,6 +237,8 @@ class _$GetByStatusImpl implements _GetByStatus {
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_GetByStatus value)? getByStatus,
TResult? Function(_GetById value)? getById,
TResult? Function(_LoadMore value)? loadMore,
TResult? Function(_Refresh value)? refresh,
}) {
return getByStatus?.call(this);
}
@ -220,6 +248,8 @@ class _$GetByStatusImpl implements _GetByStatus {
TResult maybeMap<TResult extends Object?>({
TResult Function(_GetByStatus value)? getByStatus,
TResult Function(_GetById value)? getById,
TResult Function(_LoadMore value)? loadMore,
TResult Function(_Refresh value)? refresh,
required TResult orElse(),
}) {
if (getByStatus != null) {
@ -316,6 +346,9 @@ class _$GetByIdImpl implements _GetById {
required TResult Function(String status, DateTime dateFrom, DateTime dateTo)
getByStatus,
required TResult Function(String id) getById,
required TResult Function(String status, DateTime dateFrom, DateTime dateTo)
loadMore,
required TResult Function(String status) refresh,
}) {
return getById(id);
}
@ -326,6 +359,9 @@ class _$GetByIdImpl implements _GetById {
TResult? Function(String status, DateTime dateFrom, DateTime dateTo)?
getByStatus,
TResult? Function(String id)? getById,
TResult? Function(String status, DateTime dateFrom, DateTime dateTo)?
loadMore,
TResult? Function(String status)? refresh,
}) {
return getById?.call(id);
}
@ -336,6 +372,9 @@ class _$GetByIdImpl implements _GetById {
TResult Function(String status, DateTime dateFrom, DateTime dateTo)?
getByStatus,
TResult Function(String id)? getById,
TResult Function(String status, DateTime dateFrom, DateTime dateTo)?
loadMore,
TResult Function(String status)? refresh,
required TResult orElse(),
}) {
if (getById != null) {
@ -349,6 +388,8 @@ class _$GetByIdImpl implements _GetById {
TResult map<TResult extends Object?>({
required TResult Function(_GetByStatus value) getByStatus,
required TResult Function(_GetById value) getById,
required TResult Function(_LoadMore value) loadMore,
required TResult Function(_Refresh value) refresh,
}) {
return getById(this);
}
@ -358,6 +399,8 @@ class _$GetByIdImpl implements _GetById {
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_GetByStatus value)? getByStatus,
TResult? Function(_GetById value)? getById,
TResult? Function(_LoadMore value)? loadMore,
TResult? Function(_Refresh value)? refresh,
}) {
return getById?.call(this);
}
@ -367,6 +410,8 @@ class _$GetByIdImpl implements _GetById {
TResult maybeMap<TResult extends Object?>({
TResult Function(_GetByStatus value)? getByStatus,
TResult Function(_GetById value)? getById,
TResult Function(_LoadMore value)? loadMore,
TResult Function(_Refresh value)? refresh,
required TResult orElse(),
}) {
if (getById != null) {
@ -388,13 +433,353 @@ abstract class _GetById implements OrderLoaderEvent {
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class _$$LoadMoreImplCopyWith<$Res> {
factory _$$LoadMoreImplCopyWith(
_$LoadMoreImpl value, $Res Function(_$LoadMoreImpl) then) =
__$$LoadMoreImplCopyWithImpl<$Res>;
@useResult
$Res call({String status, DateTime dateFrom, DateTime dateTo});
}
/// @nodoc
class __$$LoadMoreImplCopyWithImpl<$Res>
extends _$OrderLoaderEventCopyWithImpl<$Res, _$LoadMoreImpl>
implements _$$LoadMoreImplCopyWith<$Res> {
__$$LoadMoreImplCopyWithImpl(
_$LoadMoreImpl _value, $Res Function(_$LoadMoreImpl) _then)
: super(_value, _then);
/// Create a copy of OrderLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? status = null,
Object? dateFrom = null,
Object? dateTo = null,
}) {
return _then(_$LoadMoreImpl(
null == status
? _value.status
: status // ignore: cast_nullable_to_non_nullable
as String,
dateFrom: null == dateFrom
? _value.dateFrom
: dateFrom // ignore: cast_nullable_to_non_nullable
as DateTime,
dateTo: null == dateTo
? _value.dateTo
: dateTo // ignore: cast_nullable_to_non_nullable
as DateTime,
));
}
}
/// @nodoc
class _$LoadMoreImpl implements _LoadMore {
const _$LoadMoreImpl(this.status,
{required this.dateFrom, required this.dateTo});
@override
final String status;
@override
final DateTime dateFrom;
@override
final DateTime dateTo;
@override
String toString() {
return 'OrderLoaderEvent.loadMore(status: $status, dateFrom: $dateFrom, dateTo: $dateTo)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$LoadMoreImpl &&
(identical(other.status, status) || other.status == status) &&
(identical(other.dateFrom, dateFrom) ||
other.dateFrom == dateFrom) &&
(identical(other.dateTo, dateTo) || other.dateTo == dateTo));
}
@override
int get hashCode => Object.hash(runtimeType, status, dateFrom, dateTo);
/// Create a copy of OrderLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$LoadMoreImplCopyWith<_$LoadMoreImpl> get copyWith =>
__$$LoadMoreImplCopyWithImpl<_$LoadMoreImpl>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(String status, DateTime dateFrom, DateTime dateTo)
getByStatus,
required TResult Function(String id) getById,
required TResult Function(String status, DateTime dateFrom, DateTime dateTo)
loadMore,
required TResult Function(String status) refresh,
}) {
return loadMore(status, dateFrom, dateTo);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(String status, DateTime dateFrom, DateTime dateTo)?
getByStatus,
TResult? Function(String id)? getById,
TResult? Function(String status, DateTime dateFrom, DateTime dateTo)?
loadMore,
TResult? Function(String status)? refresh,
}) {
return loadMore?.call(status, dateFrom, dateTo);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(String status, DateTime dateFrom, DateTime dateTo)?
getByStatus,
TResult Function(String id)? getById,
TResult Function(String status, DateTime dateFrom, DateTime dateTo)?
loadMore,
TResult Function(String status)? refresh,
required TResult orElse(),
}) {
if (loadMore != null) {
return loadMore(status, dateFrom, dateTo);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_GetByStatus value) getByStatus,
required TResult Function(_GetById value) getById,
required TResult Function(_LoadMore value) loadMore,
required TResult Function(_Refresh value) refresh,
}) {
return loadMore(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_GetByStatus value)? getByStatus,
TResult? Function(_GetById value)? getById,
TResult? Function(_LoadMore value)? loadMore,
TResult? Function(_Refresh value)? refresh,
}) {
return loadMore?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_GetByStatus value)? getByStatus,
TResult Function(_GetById value)? getById,
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 OrderLoaderEvent {
const factory _LoadMore(final String status,
{required final DateTime dateFrom,
required final DateTime dateTo}) = _$LoadMoreImpl;
String get status;
DateTime get dateFrom;
DateTime get dateTo;
/// Create a copy of OrderLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$LoadMoreImplCopyWith<_$LoadMoreImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class _$$RefreshImplCopyWith<$Res> {
factory _$$RefreshImplCopyWith(
_$RefreshImpl value, $Res Function(_$RefreshImpl) then) =
__$$RefreshImplCopyWithImpl<$Res>;
@useResult
$Res call({String status});
}
/// @nodoc
class __$$RefreshImplCopyWithImpl<$Res>
extends _$OrderLoaderEventCopyWithImpl<$Res, _$RefreshImpl>
implements _$$RefreshImplCopyWith<$Res> {
__$$RefreshImplCopyWithImpl(
_$RefreshImpl _value, $Res Function(_$RefreshImpl) _then)
: super(_value, _then);
/// Create a copy of OrderLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? status = null,
}) {
return _then(_$RefreshImpl(
null == status
? _value.status
: status // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
class _$RefreshImpl implements _Refresh {
const _$RefreshImpl(this.status);
@override
final String status;
@override
String toString() {
return 'OrderLoaderEvent.refresh(status: $status)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$RefreshImpl &&
(identical(other.status, status) || other.status == status));
}
@override
int get hashCode => Object.hash(runtimeType, status);
/// Create a copy of OrderLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$RefreshImplCopyWith<_$RefreshImpl> get copyWith =>
__$$RefreshImplCopyWithImpl<_$RefreshImpl>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(String status, DateTime dateFrom, DateTime dateTo)
getByStatus,
required TResult Function(String id) getById,
required TResult Function(String status, DateTime dateFrom, DateTime dateTo)
loadMore,
required TResult Function(String status) refresh,
}) {
return refresh(status);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(String status, DateTime dateFrom, DateTime dateTo)?
getByStatus,
TResult? Function(String id)? getById,
TResult? Function(String status, DateTime dateFrom, DateTime dateTo)?
loadMore,
TResult? Function(String status)? refresh,
}) {
return refresh?.call(status);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(String status, DateTime dateFrom, DateTime dateTo)?
getByStatus,
TResult Function(String id)? getById,
TResult Function(String status, DateTime dateFrom, DateTime dateTo)?
loadMore,
TResult Function(String status)? refresh,
required TResult orElse(),
}) {
if (refresh != null) {
return refresh(status);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_GetByStatus value) getByStatus,
required TResult Function(_GetById value) getById,
required TResult Function(_LoadMore value) loadMore,
required TResult Function(_Refresh value) refresh,
}) {
return refresh(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_GetByStatus value)? getByStatus,
TResult? Function(_GetById value)? getById,
TResult? Function(_LoadMore value)? loadMore,
TResult? Function(_Refresh value)? refresh,
}) {
return refresh?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_GetByStatus value)? getByStatus,
TResult Function(_GetById value)? getById,
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 OrderLoaderEvent {
const factory _Refresh(final String status) = _$RefreshImpl;
String get status;
/// Create a copy of OrderLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$RefreshImplCopyWith<_$RefreshImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
mixin _$OrderLoaderState {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(List<Order> orders, int totalOrder) loaded,
required TResult Function(List<Order> orders, int totalOrder,
bool hasReachedMax, int currentPage, bool isLoadingMore)
loaded,
required TResult Function(String message) error,
required TResult Function(Order order) loadedDetail,
}) =>
@ -403,7 +788,9 @@ mixin _$OrderLoaderState {
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(List<Order> orders, int totalOrder)? loaded,
TResult? Function(List<Order> orders, int totalOrder, bool hasReachedMax,
int currentPage, bool isLoadingMore)?
loaded,
TResult? Function(String message)? error,
TResult? Function(Order order)? loadedDetail,
}) =>
@ -412,7 +799,9 @@ mixin _$OrderLoaderState {
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function(List<Order> orders, int totalOrder)? loaded,
TResult Function(List<Order> orders, int totalOrder, bool hasReachedMax,
int currentPage, bool isLoadingMore)?
loaded,
TResult Function(String message)? error,
TResult Function(Order order)? loadedDetail,
required TResult orElse(),
@ -512,7 +901,9 @@ class _$InitialImpl implements _Initial {
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(List<Order> orders, int totalOrder) loaded,
required TResult Function(List<Order> orders, int totalOrder,
bool hasReachedMax, int currentPage, bool isLoadingMore)
loaded,
required TResult Function(String message) error,
required TResult Function(Order order) loadedDetail,
}) {
@ -524,7 +915,9 @@ class _$InitialImpl implements _Initial {
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(List<Order> orders, int totalOrder)? loaded,
TResult? Function(List<Order> orders, int totalOrder, bool hasReachedMax,
int currentPage, bool isLoadingMore)?
loaded,
TResult? Function(String message)? error,
TResult? Function(Order order)? loadedDetail,
}) {
@ -536,7 +929,9 @@ class _$InitialImpl implements _Initial {
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function(List<Order> orders, int totalOrder)? loaded,
TResult Function(List<Order> orders, int totalOrder, bool hasReachedMax,
int currentPage, bool isLoadingMore)?
loaded,
TResult Function(String message)? error,
TResult Function(Order order)? loadedDetail,
required TResult orElse(),
@ -635,7 +1030,9 @@ class _$LoadingImpl implements _Loading {
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(List<Order> orders, int totalOrder) loaded,
required TResult Function(List<Order> orders, int totalOrder,
bool hasReachedMax, int currentPage, bool isLoadingMore)
loaded,
required TResult Function(String message) error,
required TResult Function(Order order) loadedDetail,
}) {
@ -647,7 +1044,9 @@ class _$LoadingImpl implements _Loading {
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(List<Order> orders, int totalOrder)? loaded,
TResult? Function(List<Order> orders, int totalOrder, bool hasReachedMax,
int currentPage, bool isLoadingMore)?
loaded,
TResult? Function(String message)? error,
TResult? Function(Order order)? loadedDetail,
}) {
@ -659,7 +1058,9 @@ class _$LoadingImpl implements _Loading {
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function(List<Order> orders, int totalOrder)? loaded,
TResult Function(List<Order> orders, int totalOrder, bool hasReachedMax,
int currentPage, bool isLoadingMore)?
loaded,
TResult Function(String message)? error,
TResult Function(Order order)? loadedDetail,
required TResult orElse(),
@ -721,7 +1122,12 @@ abstract class _$$LoadedImplCopyWith<$Res> {
_$LoadedImpl value, $Res Function(_$LoadedImpl) then) =
__$$LoadedImplCopyWithImpl<$Res>;
@useResult
$Res call({List<Order> orders, int totalOrder});
$Res call(
{List<Order> orders,
int totalOrder,
bool hasReachedMax,
int currentPage,
bool isLoadingMore});
}
/// @nodoc
@ -739,16 +1145,31 @@ class __$$LoadedImplCopyWithImpl<$Res>
$Res call({
Object? orders = null,
Object? totalOrder = null,
Object? hasReachedMax = null,
Object? currentPage = null,
Object? isLoadingMore = null,
}) {
return _then(_$LoadedImpl(
null == orders
orders: null == orders
? _value._orders
: orders // ignore: cast_nullable_to_non_nullable
as List<Order>,
null == totalOrder
totalOrder: null == totalOrder
? _value.totalOrder
: totalOrder // ignore: cast_nullable_to_non_nullable
as int,
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,
));
}
}
@ -756,7 +1177,12 @@ class __$$LoadedImplCopyWithImpl<$Res>
/// @nodoc
class _$LoadedImpl implements _Loaded {
const _$LoadedImpl(final List<Order> orders, this.totalOrder)
const _$LoadedImpl(
{required final List<Order> orders,
required this.totalOrder,
required this.hasReachedMax,
required this.currentPage,
required this.isLoadingMore})
: _orders = orders;
final List<Order> _orders;
@ -769,10 +1195,16 @@ class _$LoadedImpl implements _Loaded {
@override
final int totalOrder;
@override
final bool hasReachedMax;
@override
final int currentPage;
@override
final bool isLoadingMore;
@override
String toString() {
return 'OrderLoaderState.loaded(orders: $orders, totalOrder: $totalOrder)';
return 'OrderLoaderState.loaded(orders: $orders, totalOrder: $totalOrder, hasReachedMax: $hasReachedMax, currentPage: $currentPage, isLoadingMore: $isLoadingMore)';
}
@override
@ -782,12 +1214,23 @@ class _$LoadedImpl implements _Loaded {
other is _$LoadedImpl &&
const DeepCollectionEquality().equals(other._orders, _orders) &&
(identical(other.totalOrder, totalOrder) ||
other.totalOrder == totalOrder));
other.totalOrder == totalOrder) &&
(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(_orders), totalOrder);
runtimeType,
const DeepCollectionEquality().hash(_orders),
totalOrder,
hasReachedMax,
currentPage,
isLoadingMore);
/// Create a copy of OrderLoaderState
/// with the given fields replaced by the non-null parameter values.
@ -802,11 +1245,14 @@ class _$LoadedImpl implements _Loaded {
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(List<Order> orders, int totalOrder) loaded,
required TResult Function(List<Order> orders, int totalOrder,
bool hasReachedMax, int currentPage, bool isLoadingMore)
loaded,
required TResult Function(String message) error,
required TResult Function(Order order) loadedDetail,
}) {
return loaded(orders, totalOrder);
return loaded(
orders, totalOrder, hasReachedMax, currentPage, isLoadingMore);
}
@override
@ -814,11 +1260,14 @@ class _$LoadedImpl implements _Loaded {
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(List<Order> orders, int totalOrder)? loaded,
TResult? Function(List<Order> orders, int totalOrder, bool hasReachedMax,
int currentPage, bool isLoadingMore)?
loaded,
TResult? Function(String message)? error,
TResult? Function(Order order)? loadedDetail,
}) {
return loaded?.call(orders, totalOrder);
return loaded?.call(
orders, totalOrder, hasReachedMax, currentPage, isLoadingMore);
}
@override
@ -826,13 +1275,16 @@ class _$LoadedImpl implements _Loaded {
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function(List<Order> orders, int totalOrder)? loaded,
TResult Function(List<Order> orders, int totalOrder, bool hasReachedMax,
int currentPage, bool isLoadingMore)?
loaded,
TResult Function(String message)? error,
TResult Function(Order order)? loadedDetail,
required TResult orElse(),
}) {
if (loaded != null) {
return loaded(orders, totalOrder);
return loaded(
orders, totalOrder, hasReachedMax, currentPage, isLoadingMore);
}
return orElse();
}
@ -879,11 +1331,18 @@ class _$LoadedImpl implements _Loaded {
}
abstract class _Loaded implements OrderLoaderState {
const factory _Loaded(final List<Order> orders, final int totalOrder) =
_$LoadedImpl;
const factory _Loaded(
{required final List<Order> orders,
required final int totalOrder,
required final bool hasReachedMax,
required final int currentPage,
required final bool isLoadingMore}) = _$LoadedImpl;
List<Order> get orders;
int get totalOrder;
bool get hasReachedMax;
int get currentPage;
bool get isLoadingMore;
/// Create a copy of OrderLoaderState
/// with the given fields replaced by the non-null parameter values.
@ -962,7 +1421,9 @@ class _$ErrorImpl implements _Error {
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(List<Order> orders, int totalOrder) loaded,
required TResult Function(List<Order> orders, int totalOrder,
bool hasReachedMax, int currentPage, bool isLoadingMore)
loaded,
required TResult Function(String message) error,
required TResult Function(Order order) loadedDetail,
}) {
@ -974,7 +1435,9 @@ class _$ErrorImpl implements _Error {
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(List<Order> orders, int totalOrder)? loaded,
TResult? Function(List<Order> orders, int totalOrder, bool hasReachedMax,
int currentPage, bool isLoadingMore)?
loaded,
TResult? Function(String message)? error,
TResult? Function(Order order)? loadedDetail,
}) {
@ -986,7 +1449,9 @@ class _$ErrorImpl implements _Error {
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function(List<Order> orders, int totalOrder)? loaded,
TResult Function(List<Order> orders, int totalOrder, bool hasReachedMax,
int currentPage, bool isLoadingMore)?
loaded,
TResult Function(String message)? error,
TResult Function(Order order)? loadedDetail,
required TResult orElse(),
@ -1120,7 +1585,9 @@ class _$LoadedDetailImpl implements _LoadedDetail {
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(List<Order> orders, int totalOrder) loaded,
required TResult Function(List<Order> orders, int totalOrder,
bool hasReachedMax, int currentPage, bool isLoadingMore)
loaded,
required TResult Function(String message) error,
required TResult Function(Order order) loadedDetail,
}) {
@ -1132,7 +1599,9 @@ class _$LoadedDetailImpl implements _LoadedDetail {
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(List<Order> orders, int totalOrder)? loaded,
TResult? Function(List<Order> orders, int totalOrder, bool hasReachedMax,
int currentPage, bool isLoadingMore)?
loaded,
TResult? Function(String message)? error,
TResult? Function(Order order)? loadedDetail,
}) {
@ -1144,7 +1613,9 @@ class _$LoadedDetailImpl implements _LoadedDetail {
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function(List<Order> orders, int totalOrder)? loaded,
TResult Function(List<Order> orders, int totalOrder, bool hasReachedMax,
int currentPage, bool isLoadingMore)?
loaded,
TResult Function(String message)? error,
TResult Function(Order order)? loadedDetail,
required TResult orElse(),

View File

@ -8,4 +8,10 @@ class OrderLoaderEvent with _$OrderLoaderEvent {
required DateTime dateTo,
}) = _GetByStatus;
const factory OrderLoaderEvent.getById(String id) = _GetById;
const factory OrderLoaderEvent.loadMore(
String status, {
required DateTime dateFrom,
required DateTime dateTo,
}) = _LoadMore;
const factory OrderLoaderEvent.refresh(String status) = _Refresh;
}

View File

@ -4,8 +4,13 @@ part of 'order_loader_bloc.dart';
class OrderLoaderState with _$OrderLoaderState {
const factory OrderLoaderState.initial() = _Initial;
const factory OrderLoaderState.loading() = _Loading;
const factory OrderLoaderState.loaded(List<Order> orders, int totalOrder) =
_Loaded;
const factory OrderLoaderState.loaded({
required List<Order> orders,
required int totalOrder,
required bool hasReachedMax,
required int currentPage,
required bool isLoadingMore,
}) = _Loaded;
const factory OrderLoaderState.error(String message) = _Error;
const factory OrderLoaderState.loadedDetail(Order order) = _LoadedDetail;
}

View File

@ -29,6 +29,7 @@ class SalesPage extends StatefulWidget {
}
class _SalesPageState extends State<SalesPage> {
ScrollController scrollController = ScrollController();
DateTime startDate = DateTime.now();
DateTime endDate = DateTime.now();
Order? orderDetail;
@ -65,69 +66,82 @@ class _SalesPageState extends State<SalesPage> {
children: [
Expanded(
flex: 2,
child: Material(
color: AppColors.white,
child: Column(
children: [
SalesTitle(
title: widget.status == 'pending'
? "Pending Pesanan"
: "Daftar Pesanan",
startDate: startDate,
endDate: endDate,
onChanged: (value) {
setState(() {
searchQuery = value;
});
},
onDateRangeChanged: (start, end) {
setState(() {
startDate = start;
endDate = end;
});
child: NotificationListener<ScrollNotification>(
onNotification: (notification) {
if (notification is ScrollEndNotification &&
scrollController.position.extentAfter == 0) {
context.read<OrderLoaderBloc>().add(
OrderLoaderEvent.loadMore(widget.status,
dateFrom: startDate, dateTo: endDate));
return true;
}
context.read<OrderLoaderBloc>().add(
OrderLoaderEvent.getByStatus(widget.status,
dateFrom: startDate, dateTo: endDate));
},
),
Expanded(
child: BlocBuilder<OrderLoaderBloc, OrderLoaderState>(
builder: (context, state) {
return state.maybeWhen(
orElse: () => const Center(
child: CircularProgressIndicator(),
),
loading: () => const Center(
child: CircularProgressIndicator(),
),
error: (message) => Center(
child: Text(
message,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.bold,
return true;
},
child: Material(
color: AppColors.white,
child: Column(
children: [
SalesTitle(
title: widget.status == 'pending'
? "Pending Pesanan"
: "Daftar Pesanan",
startDate: startDate,
endDate: endDate,
onChanged: (value) {
setState(() {
searchQuery = value;
});
},
onDateRangeChanged: (start, end) {
setState(() {
startDate = start;
endDate = end;
});
context.read<OrderLoaderBloc>().add(
OrderLoaderEvent.getByStatus(widget.status,
dateFrom: startDate, dateTo: endDate));
},
),
Expanded(
child: BlocBuilder<OrderLoaderBloc, OrderLoaderState>(
builder: (context, state) {
return state.maybeWhen(
orElse: () => const Center(
child: CircularProgressIndicator(),
),
loading: () => const Center(
child: CircularProgressIndicator(),
),
error: (message) => Center(
child: Text(
message,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.bold,
),
),
),
),
loaded: (orders, totalOrder) {
final filtered = _filterOrders(orders);
if (filtered.isEmpty) {
return Center(
child: Text(
"Belum ada transaksi saat ini. ",
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.bold,
loaded: (orders, totalOrder, hasReachedMax,
currentPage, isLoadingMore) {
final filtered = _filterOrders(orders);
if (filtered.isEmpty) {
return Center(
child: Text(
"Belum ada transaksi saat ini. ",
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.bold,
),
),
),
);
} else {
return SingleChildScrollView(
child: Column(
children: List.generate(
filtered.length,
(index) => GestureDetector(
);
} else {
return ListView.builder(
itemCount: filtered.length,
controller: scrollController,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
setState(() {
orderDetail = filtered[index];
@ -141,17 +155,17 @@ class _SalesPageState extends State<SalesPage> {
isActive:
orders[index] == orderDetail,
),
),
),
),
);
}
},
);
},
);
},
);
}
},
);
},
),
),
),
],
],
),
),
),
),
@ -202,18 +216,6 @@ class _SalesPageState extends State<SalesPage> {
);
}),
SpaceWidth(8),
Button.outlined(
onPressed: () {
context.push(
PaymentPage(
order: orderDetail!,
),
);
},
label: 'Bayar',
icon: Icon(Icons.payment),
),
SpaceWidth(8),
Button.outlined(
onPressed: () {
context.push(
@ -223,7 +225,22 @@ class _SalesPageState extends State<SalesPage> {
);
},
label: 'Split Bill',
icon: Icon(Icons.payment),
icon: Icon(
Icons.calculate_outlined,
),
),
SpaceWidth(8),
Button.filled(
width: 120,
onPressed: () {
context.push(
PaymentPage(
order: orderDetail!,
),
);
},
label: 'Bayar',
icon: Icon(Icons.payment, color: Colors.white),
),
],
if (widget.status == 'completed')

View File

@ -91,7 +91,7 @@ class SalesTitle extends StatelessWidget {
builder: (context, state) {
return state.maybeWhen(
orElse: () => const SizedBox.shrink(),
loaded: (orders, totalOrder) => Text(
loaded: (orders, totalOrder, _, __, ___) => Text(
'$totalOrder Pesanan',
style: TextStyle(
color: AppColors.black,