feat: autorefresh
Build Android APK (split per ABI) / build-apk (push) Waiting to run
Build & Deploy iOS to TestFlight / build-and-deploy (push) Waiting to run

This commit is contained in:
efrilm
2026-10-27 17:51:26 +07:00
parent e7c77051ab
commit a2f3af27b1
6 changed files with 102 additions and 11 deletions
@@ -12,6 +12,11 @@ part 'order_loader_bloc.freezed.dart';
@injectable @injectable
class OrderLoaderBloc extends Bloc<OrderLoaderEvent, OrderLoaderState> { class OrderLoaderBloc extends Bloc<OrderLoaderEvent, OrderLoaderState> {
final IOrderRepository _repository; final IOrderRepository _repository;
int _fetchRequestId = 0;
int _summaryRequestId = 0;
bool _isLoadingMore = false;
OrderLoaderBloc(this._repository) : super(OrderLoaderState.initial()) { OrderLoaderBloc(this._repository) : super(OrderLoaderState.initial()) {
on<OrderLoaderEvent>(_onOrderLoaderEvent); on<OrderLoaderEvent>(_onOrderLoaderEvent);
} }
@@ -34,19 +39,43 @@ class OrderLoaderBloc extends Bloc<OrderLoaderEvent, OrderLoaderState> {
emit(state.copyWith(outletId: e.outletId)); emit(state.copyWith(outletId: e.outletId));
}, },
fetched: (e) async { fetched: (e) async {
var newState = state;
if (e.isRefresh) { if (e.isRefresh) {
newState = state.copyWith(isFetching: true); _fetchRequestId++;
emit(state.copyWith(isFetching: true));
emit(newState); } else if (state.isFetching || _isLoadingMore) {
return;
} }
newState = await _mapFetchedToState(state, isRefresh: e.isRefresh); final requestId = _fetchRequestId;
_isLoadingMore = !e.isRefresh;
emit(newState); try {
final result = await _mapFetchedToState(
state,
isRefresh: e.isRefresh,
);
// Filter berubah selama request berjalan, buang hasil lama
if (requestId != _fetchRequestId) return;
// Hanya ambil field list, supaya status/filter/summary terbaru
// tidak tertimpa snapshot lama
emit(
state.copyWith(
orders: result.orders,
failureOptionOrder: result.failureOptionOrder,
page: result.page,
hasReachedMax: result.hasReachedMax,
isFetching: false,
),
);
} finally {
if (!e.isRefresh) _isLoadingMore = false;
}
}, },
summaryFetched: (e) async { summaryFetched: (e) async {
final requestId = ++_summaryRequestId;
emit(state.copyWith(isFetchingSummary: true)); emit(state.copyWith(isFetchingSummary: true));
final results = await Future.wait([ final results = await Future.wait([
@@ -63,6 +92,8 @@ class OrderLoaderBloc extends Bloc<OrderLoaderEvent, OrderLoaderState> {
), ),
]); ]);
if (requestId != _summaryRequestId) return;
emit( emit(
state.copyWith( state.copyWith(
isFetchingSummary: false, isFetchingSummary: false,
+1 -1
View File
@@ -234,7 +234,7 @@
"@active": {}, "@active": {},
"inactive": "Tidak Aktif", "inactive": "Tidak Aktif",
"@inactive": {}, "@inactive": {},
"total_amount": "Jumlah Total", "total_amount": "Total",
"@total_amount": {}, "@total_amount": {},
"table": "Meja", "table": "Meja",
"@table": {}, "@table": {},
+1 -1
View File
@@ -343,7 +343,7 @@ class AppLocalizationsId extends AppLocalizations {
String get inactive => 'Tidak Aktif'; String get inactive => 'Tidak Aktif';
@override @override
String get total_amount => 'Jumlah Total'; String get total_amount => 'Total';
@override @override
String get table => 'Meja'; String get table => 'Meja';
+33 -1
View File
@@ -12,6 +12,7 @@ import '../../../common/theme/theme.dart';
import '../../../injection.dart'; import '../../../injection.dart';
import '../../components/button/button.dart'; import '../../components/button/button.dart';
import '../../components/spacer/spacer.dart'; import '../../components/spacer/spacer.dart';
import '../../router/app_router.gr.dart';
import 'widgets/feature.dart'; import 'widgets/feature.dart';
import 'widgets/header.dart'; import 'widgets/header.dart';
import 'widgets/home_top_products.dart'; import 'widgets/home_top_products.dart';
@@ -53,6 +54,9 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
late AnimationController _headerAnimationController; late AnimationController _headerAnimationController;
late AnimationController _contentAnimationController; late AnimationController _contentAnimationController;
TabsRouter? _tabsRouter;
bool _isActiveTab = true;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@@ -77,8 +81,29 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
}); });
} }
@override
void didChangeDependencies() {
super.didChangeDependencies();
final tabsRouter = AutoTabsRouter.of(context);
if (_tabsRouter != tabsRouter) {
_tabsRouter?.removeListener(_onTabChanged);
_tabsRouter = tabsRouter..addListener(_onTabChanged);
}
}
// Refresh setiap kali tab Home dibuka lagi dari bottom nav
void _onTabChanged() {
final isActiveTab = _tabsRouter?.current.name == HomeRoute.name;
if (isActiveTab && !_isActiveTab) _refreshAll(context);
_isActiveTab = isActiveTab;
}
@override @override
void dispose() { void dispose() {
_tabsRouter?.removeListener(_onTabChanged);
_headerAnimationController.dispose(); _headerAnimationController.dispose();
_contentAnimationController.dispose(); _contentAnimationController.dispose();
super.dispose(); super.dispose();
@@ -88,6 +113,13 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
context.read<HomeBloc>().add(HomeEvent.fetchedDashboard()); context.read<HomeBloc>().add(HomeEvent.fetchedDashboard());
} }
void _refreshAll(BuildContext context) {
_refetchDashboard(context);
context.read<ExclusiveSummaryLoaderBloc>().add(
const ExclusiveSummaryLoaderEvent.fetched(),
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocListener<SelectedOutletBloc, SelectedOutletState>( return BlocListener<SelectedOutletBloc, SelectedOutletState>(
@@ -100,7 +132,7 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
backgroundColor: AppColor.white, backgroundColor: AppColor.white,
color: AppColor.primary, color: AppColor.primary,
onRefresh: () { onRefresh: () {
_refetchDashboard(context); _refreshAll(context);
return Future.value(); return Future.value();
}, },
child: BlocBuilder<HomeBloc, HomeState>( child: BlocBuilder<HomeBloc, HomeState>(
@@ -50,6 +50,9 @@ class _OrderPageState extends State<OrderPage> with TickerProviderStateMixin {
late Animation<Offset> _slideAnimation; late Animation<Offset> _slideAnimation;
final ScrollController _scrollController = ScrollController(); final ScrollController _scrollController = ScrollController();
TabsRouter? _tabsRouter;
bool _isActiveTab = true;
// Filter state // Filter state
final List<String> filterOptions = ['All', 'Pending', 'Completed']; final List<String> filterOptions = ['All', 'Pending', 'Completed'];
@@ -80,8 +83,33 @@ class _OrderPageState extends State<OrderPage> with TickerProviderStateMixin {
_slideController.forward(); _slideController.forward();
} }
@override
void didChangeDependencies() {
super.didChangeDependencies();
final tabsRouter = AutoTabsRouter.of(context);
if (_tabsRouter != tabsRouter) {
_tabsRouter?.removeListener(_onTabChanged);
_tabsRouter = tabsRouter..addListener(_onTabChanged);
}
}
// Refresh setiap kali tab Order dibuka lagi dari bottom nav
void _onTabChanged() {
final isActiveTab = _tabsRouter?.current.name == OrderRoute.name;
if (isActiveTab && !_isActiveTab) {
context.read<OrderLoaderBloc>()
..add(OrderLoaderEvent.fetched(isRefresh: true))
..add(const OrderLoaderEvent.summaryFetched());
}
_isActiveTab = isActiveTab;
}
@override @override
void dispose() { void dispose() {
_tabsRouter?.removeListener(_onTabChanged);
_fadeController.dispose(); _fadeController.dispose();
_slideController.dispose(); _slideController.dispose();
super.dispose(); super.dispose();
+1 -1
View File
@@ -3,7 +3,7 @@ description: "A new Flutter project."
publish_to: "none" publish_to: "none"
version: 1.0.4+18 version: 1.0.4+19
environment: environment:
sdk: ^3.8.1 sdk: ^3.8.1