home_page.dart ( PC )
unknown
dart
10 months ago
27 kB
11
Indexable
// Dart imports:
import 'dart:async';
// Package imports:
import 'package:carousel_slider/carousel_slider.dart';
import 'package:driver/blocs/DO/bloc.dart';
import 'package:driver/blocs/DO/repository.dart';
import 'package:driver/blocs/shipments/bloc.dart';
import 'package:driver/helper/db_helper.dart';
import 'package:driver/helper/network_info.dart';
import 'package:driver/models/user_driver.dart';
import 'package:driver/pages/drivers/cndn/component/search_filter.dart';
import 'package:driver/pages/drivers/in-progress/in_progress_order.dart';
import 'package:driver/pages/drivers/shipment/shipment_page.dart';
import 'package:driver/pages/xroot/tabPageDriver.dart';
import 'package:driver/widgets/no-connection/carousel_no_connection.dart';
import 'package:driver/widgets/no-connection/no_connection_widget.dart';
import 'package:driver/widgets/orders/order_home_do.dart';
import 'package:driver/widgets/orders/order_home_shipment.dart';
import 'package:elegant_notification/elegant_notification.dart';
import 'package:elegant_notification/resources/arrays.dart';
import 'package:elegant_notification/resources/stacked_options.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
// Project imports:
import 'package:driver/blocs/common/bloc.dart';
import 'package:driver/providers/auth.dart';
import 'package:driver/utils/my_import.dart';
import 'package:driver/widgets/errors/loaddata_error.dart';
import 'package:driver/widgets/images/slider_widget.dart';
import 'package:internet_connection_checker/internet_connection_checker.dart';
import 'package:rxdart/rxdart.dart';
import 'package:uuid/uuid.dart';
import '../../../blocs/summary/bloc.dart';
import '../../../providers/api.dart';
import '../../../widgets/images/no_data_widget.dart';
import '../../../widgets/summary/summary_data_widget.dart';
class HomePage extends StatefulWidget {
final VoidCallback? onSignedOut, onGetAccountInfo, getOpenTrip;
final UserDriver? userDriver;
HomePage({
Key? key,
this.onSignedOut,
this.userDriver,
this.onGetAccountInfo,
this.getOpenTrip,
}) : super(key: key);
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
final _scrollController = ScrollController();
final _searchController = TextEditingController();
var uuid = Uuid();
final CommonBloc _commonBloc = new CommonBloc(repository: CommonRepository());
final OrderBloc orderBloc = new OrderBloc(
repository: DORepository(),
networkInfo: NetworkInfoImpl(InternetConnectionChecker()),
databaseHelper: DatabaseHelper(),
);
final StatsBloc statsBloc = StatsBloc(repository: StatsRepository());
final ShipmentBloc shipmentBloc =
ShipmentBloc(repository: ShipmentRepository());
final NetworkInfo networkInfo = NetworkInfoImpl(InternetConnectionChecker());
String statusMode = ORDER_STATUS_IN_PROGRESS;
String date = '';
String materialId = '';
String searchQuery = "";
String filterQuery = "&date=all";
String sortQuery = "delivery_date|asc";
bool processShipment = true;
bool processShipmentTransporter = true;
List<SliderImageWidget>? imageSliders;
int _current = 0;
late final StreamSubscription notifSub;
Timer? _bgDebounce; // <<— tambah ini
static const _pauseThreshold =
Duration(milliseconds: 800); // sesuaikan 500–1500ms
DateTime? _backgroundSince;
AppLifecycleState? _lastLifecycleState;
bool _trulyBackgrounded = false; // penanda "benar2 background"
@override
void setState(fn) {
if (mounted) {
super.setState(fn);
}
}
@override
void initState() {
super.initState();
initBloc();
getAccountInfo();
getNotificationPermission();
WidgetsBinding.instance.addObserver(this);
notifSub = notifStream.stream
.where((t) => t["type"] == 'order_assigned')
.map((t) => t) // take only the data field
.debounceTime(const Duration(milliseconds: 500))
.exhaustMap((data) {
return Stream.fromFuture(() async {
_onRefresh();
late ElegantNotification notif; // simpan instans di sini
notif = ElegantNotification.success(
width: 360,
isDismissable: true,
stackedOptions: StackedOptions(key: 'top', type: StackedType.below),
animationCurve: Curves.easeInOut,
position: Alignment.topCenter,
animation: AnimationType.fromTop,
title: Text('${data['body'] ?? 'Update'}'),
description: Text('${data['content'] ?? ''}'),
toastDuration: const Duration(milliseconds: 60000),
// HINDARI: onNotificationPressed bersamaan dengan action
action: Center(
child: InkWell(
onTap: () {
debugPrint('Clicked: ${data['body']}');
notif.dismiss(); // ✅ tutup notifikasi ini saja
// ...lanjut aksi lain jika perlu
},
child: const Text('Click ini'),
),
),
shadow: BoxShadow(
color: Colors.green.withOpacity(0.2),
spreadRadius: 2,
blurRadius: 5,
offset: Offset(0, 4),
),
);
notif.show(context); // setelah ini _closeTimer dsb ter‐init
}());
}).listen((_) {}, onError: (e, st) {
debugPrint('[HomePage] refresh error: $e');
debugPrint('[HomePage] refresh stackTrace: $st');
});
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.paused) {
// Mulai debounce: jangan langsung eksekusi
_bgDebounce?.cancel();
_bgDebounce = Timer(_pauseThreshold, () {
// Hanya jalan kalau masih paused setelah delay (bukan cuma slide config)
if (_lastLifecycleState == AppLifecycleState.paused) {
_trulyBackgrounded = true;
_backgroundSince = DateTime.now();
// _backgroundSince = DateTime.now(); // kalau ingin dipakai logika lain
// showCallkitIncoming(const Uuid().v4());
ElegantNotification.success(
width: 360,
isDismissable: true,
animationCurve: Curves.easeInOut,
position: Alignment.topCenter,
animation: AnimationType.fromTop,
title: Text('Update'),
description: Text('Your data has been updated'),
onDismiss: () {},
toastDuration: Duration(milliseconds: 60000),
onNotificationPressed: () {},
shadow: BoxShadow(
color: Colors.green.withOpacity(0.2),
spreadRadius: 2,
blurRadius: 5,
offset: const Offset(0, 4),
),
).show(context);
}
});
}
if (state == AppLifecycleState.inactive ||
state == AppLifecycleState.hidden ||
state == AppLifecycleState.resumed) {
// Begitu user cuma slide config (inactive) atau balik cepat (resumed),
// batalkan aksi paused yang tertunda.
_bgDebounce?.cancel();
}
if (state == AppLifecycleState.resumed) {
// Hanya refresh jika benar2 sempat background (bukan cuma slide config)
if (_trulyBackgrounded) {
_onRefresh();
}
// reset state
_trulyBackgrounded = false;
_backgroundSince = null;
}
_lastLifecycleState = state;
}
@override
void dispose() {
_disposeBloc();
notifSub.cancel();
_bgDebounce?.cancel();
super.dispose();
}
void _disposeBloc() {
WidgetsBinding.instance.removeObserver(this);
_scrollController.dispose();
orderBloc.close();
shipmentBloc.close();
}
void initBloc() {
orderBloc.add(
OrdersFetch(
statusMode: statusMode,
currentPage: 1,
perPage: PER_PAGE_DO,
filter: filterQuery,
searchQuery: searchQuery,
sort: sortQuery,
),
);
_commonBloc.add(CommonSlidersFetch(statusMode: PROVIDER_DRIVER));
shipmentBloc.add(
ShipmentOrderFetch(
statusMode: statusMode,
currentPage: 1,
perPage: PER_PAGE_DO,
filter: filterQuery,
searchQuery: searchQuery,
sort: sortQuery,
),
);
statsBloc.add(StatsRefresh(
date: date,
materialId: materialId,
));
}
void getAccountInfo() async {
setState(() {
processShipment = widget.userDriver?.canProcessShipment ?? true;
processShipmentTransporter =
widget.userDriver?.transporter?.canProcessShipment ?? true;
});
}
Future<void> getNotificationPermission() async {
try {
print("getNotificationPermission".toUpperCase());
// get user location
await api.checkPermissionNotification();
} catch (err, st) {
print(err);
print(st);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
elevation: APPBAR_ELEVATION,
centerTitle: false,
title: Image.asset(
LOGO_ORANGE,
// height: 50,
width: 110,
),
backgroundColor: MyColors.toolBarApp,
),
body: SafeArea(
child: RefreshIndicator(
child: _buildBody(),
onRefresh: _onRefresh,
),
),
);
}
Widget _buildBody() {
return ListView(
controller: _scrollController,
physics: AlwaysScrollableScrollPhysics(),
children: [
SizedBox(height: 8),
_buildImageSlider(),
_buildStats(),
SizedBox(height: 8),
_buildSearch(),
SizedBox(height: 8),
_buildOrderList(),
// _buildAbilitiesBloc()
],
);
}
Widget _buildStats() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(left: 10, top: 8),
child: Text(
'STATS_TODAY'.tr,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 10, top: 8, bottom: 8),
child: Container(
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(16.0),
color: Colors.blue.withValues(alpha: 0.1)),
child: BlocBuilder<StatsBloc, StatsState>(
bloc: statsBloc,
builder: (context, state) {
if (state is StatsFailure) {
if (state.dataError?.code == 401) {
// print(state.dataError.message);
_forceLogout();
}
return Center(
child: LoadDataError(
subtitle: state.dataError?.message,
onTap: _onRefresh,
),
);
}
if (state is StatsLoaded) {
return Padding(
padding: const EdgeInsets.all(5.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
// SummaryDataWidget('RESERVATION_ASSIGNED'.tr,
// '${state.data?.orderAssignsCount}', null, Icons.directions_car),
// SummaryDataWidget(
// 'CONFIRMATION'.tr,
// '${state.data?.confirmedCount}',
// '(${state.data?.confirmedPercent?.toStringAsFixed(0)}%)', Icons.check_box),
SummaryDataWidget(
'UNCONFIRMED'.tr,
'${state.data?.unconfirmedCount}',
'(${state.data?.unconfirmedPercent?.toStringAsFixed(0)}%)',
// Icons.close_outlined,
null,
color: Colors.red,
),
// SummaryDataWidget(
// 'COMPLETED'.tr,
// '${state.data?.completedCount}',
// '(${state.data?.completedPercent?.toStringAsFixed(0)}%)', Icons.flag),
// SummaryDataWidget(
// 'SERVED_PASSENGERS'.tr,
// '${state.data?.passengersCount}',
// null, Icons.people),
],
),
);
}
return Loader();
},
),
),
),
),
Container(
width: 100,
height: 50,
padding: EdgeInsets.all(5),
margin: const EdgeInsets.only(right: 5),
child: MaterialButton(
color: MyColors.menuBluebird,
// padding: const EdgeInsets.all(10),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
onPressed: () {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (_) => TabPageDriver(
tabIndex: 2,
userDriver: widget.userDriver,
)));
},
child: Text(
'Detail',
style: TextStyle(
fontWeight: FontWeight.bold,
color: MyColors.white,
),
),
),
),
],
),
],
);
}
Widget _buildSearch() {
return Padding(
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 16),
child: SearchBoxAndFilterComponent(
searchController: _searchController,
onSearching: _onSearching,
onClear: _onClear,
sortQuery: sortQuery,
updateFilterQuery: updateFilterQuery,
),
);
}
Widget _buildOrderList() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildTextDO(),
_buildDO(),
Visibility(
visible: processShipment && processShipmentTransporter,
child: _buildTextShipment(),
),
Visibility(
visible: processShipment && processShipmentTransporter,
child: _buildShipment(),
)
],
);
}
Widget _buildShipment() {
return BlocBuilder<ShipmentBloc, ShipmentState>(
bloc: shipmentBloc,
builder: (context, state) {
if (state is ShipmentFailure) {
if (state.dataError?.code == 401) {
// print(state.dataError.message);
_forceLogout();
}
return Center(
child: LoadDataError(
subtitle: state.dataError?.message,
onTap: _onRefresh,
),
);
}
if (state is NoConnection) {
return Center(
child: NoConnectionWidget(
title: 'Masalah Koneksi',
subtitle: state.message,
),
);
}
if (state is ShipmentOrderLoaded) {
if (state.dataList?.isEmpty == true) {
return NoDataWidget(
processShipment: processShipment,
processShipmentTransporter: processShipmentTransporter,
);
}
return SizedBox(
child: ListView.builder(
shrinkWrap: true,
itemCount:
state.dataList!.length > 3 ? 3 : state.dataList!.length,
itemBuilder: (context, index) {
return OrderHomeShipment(
data: state.dataList?[index],
isBottomCard: true,
isProgress: true,
isFinished: false,
isNeedAssigment: false,
refreshParent: _refreshParent,
status: STATUS_ONTRIP,
userMode: PROVIDER_DRIVER,
);
},
),
);
}
return Loader();
},
);
}
Widget _buildDO() {
return BlocBuilder<OrderBloc, OrderState>(
bloc: orderBloc,
builder: (BuildContext context, state) {
if (state is OrderFailure) {
if (state.dataError?.code == 401) {
// print(state.dataError.message);
_forceLogout();
}
return Center(
child: LoadDataError(
subtitle: state.dataError?.message,
onTap: _onRefresh,
),
);
}
if (state is OrderLoaded) {
if (state.dataList?.isEmpty == true) {
return NoDataWidget();
}
return SizedBox(
child: ListView.builder(
shrinkWrap: true,
physics: BouncingScrollPhysics(),
itemCount:
state.dataList!.length > 3 ? 3 : state.dataList!.length,
itemBuilder: (context, index) {
return OrderDOWidget(
data: state.dataList?[index],
isBottomCard: true,
isProgress: true,
isFinished: false,
isNeedAssigment: false,
refreshParent: _refreshParent,
status: STATUS_ONTRIP,
userMode: PROVIDER_DRIVER,
userDriver: widget.userDriver,
networkInfo: networkInfo,
);
},
),
);
}
return Loader();
},
);
}
Widget _buildTextDO() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: EdgeInsets.only(left: 10),
child: Text(
widget.userDriver?.reservationOnly == true
? "DO_RESERVATION_LIST".tr
: "DO_LIST".tr,
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
),
),
Padding(
padding: EdgeInsets.only(right: 10),
child: GestureDetector(
onTap: () {
_goInProgressOrder();
},
child: Text(
widget.userDriver?.reservationOnly == true
? "ALL_DO_RESERVATION".tr
: "ALL_DO".tr,
style: TextStyle(color: Colors.blue),
),
),
),
],
);
}
Widget _buildTextShipment() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.only(left: 10),
child: Text(
"SHIPMENT_LIST".tr,
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
),
),
Padding(
padding: const EdgeInsets.only(right: 10),
child: GestureDetector(
onTap: () {
_goShipment();
},
child: Text(
"ALL_SHIPMENT".tr,
style: TextStyle(color: Colors.blue),
),
),
),
],
);
}
Widget _buildImageSlider() {
return BlocBuilder(
bloc: _commonBloc,
builder: (context, state) {
if (state is CommonFailure) {
if (state.dataError?.code == 401) {
// print(state.dataError.message);
_forceLogout();
}
return Center(
child: LoadDataError(
subtitle: state.dataError?.message,
onTap: _onRefresh,
),
);
}
if (state is ConnectionFailure) {
return Center(
child: CarouselNoConnection(
onTap: (int index) {
setState(() {
_current = index;
});
},
message: state.dataError,
),
);
}
if (state is CommonSlidersLoaded) {
if (state.dataList.isEmpty) {
return Container();
}
imageSliders = state.dataList
.map((slider) => SliderImageWidget(
slider: slider,
))
.toList();
return Column(
children: [
CarouselSlider(
items: imageSliders,
options: CarouselOptions(
height: SLIDERS_HEIGHT,
autoPlay: true,
enlargeCenterPage: true,
aspectRatio: 2.0,
onPageChanged: (index, reason) {
setState(() {
_current = index;
});
}),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: state.dataList.map((slider) {
int index = state.dataList.indexOf(slider);
return Container(
width: 8.0,
height: 8.0,
margin:
EdgeInsets.symmetric(vertical: 10.0, horizontal: 2.0),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: _current == index
// ? Color.fromRGBO(0, 0, 0, 0.9)
? MyColors.appPrimaryColor
: Color.fromRGBO(0, 0, 0, 0.2),
),
);
}).toList(),
),
),
],
);
}
// return SlidersShimmer();
// return Container();
return Padding(
padding: const EdgeInsets.all(8.0),
child: Loader(),
);
},
);
}
Future<void> _refreshParent() async {
print("[HomePage] VoidCallback: _refreshParent");
widget.onGetAccountInfo?.call();
widget.getOpenTrip?.call();
orderBloc.add(
OrdersRefresh(
statusMode: statusMode,
currentPage: 1,
perPage: PER_PAGE_DO,
filter: filterQuery,
searchQuery: searchQuery,
sort: sortQuery,
),
);
shipmentBloc.add(ShipmentOrderRefresh(
statusMode: statusMode,
currentPage: 1,
perPage: PER_PAGE_DO,
filter: filterQuery,
searchQuery: searchQuery,
sort: sortQuery,
));
}
Future<void> _forceLogout() async {
try {
await auth.forceSignOut();
print("LOGOUT");
widget.onSignedOut?.call();
} catch (err) {
print(err);
}
}
Future<void> _onSearching(String? text) async {
setState(() {
searchQuery = text!;
});
orderBloc.add(
OrdersRefresh(
statusMode: statusMode,
currentPage: 1,
perPage: PER_PAGE_DO,
filter: filterQuery,
searchQuery: text!,
sort: sortQuery,
),
);
shipmentBloc.add(ShipmentOrderRefresh(
statusMode: statusMode,
currentPage: 1,
perPage: PER_PAGE_DO,
filter: filterQuery,
searchQuery: text,
sort: sortQuery,
));
}
void _onClear() {
setState(() {
_searchController.clear();
searchQuery = "";
});
orderBloc.add(
OrdersRefresh(
statusMode: statusMode,
currentPage: 1,
perPage: PER_PAGE_DO,
filter: filterQuery,
searchQuery: searchQuery,
sort: sortQuery,
),
);
shipmentBloc.add(ShipmentOrderRefresh(
statusMode: statusMode,
currentPage: 1,
perPage: PER_PAGE_DO,
filter: filterQuery,
searchQuery: searchQuery,
sort: sortQuery,
));
}
Future<void> _onRefresh() async {
debugPrint('[HomePage] Trigger _onRefresh() karena type=order_assigned');
if (!mounted) return;
setState(() {
widget.onGetAccountInfo?.call();
widget.getOpenTrip?.call();
_commonBloc.add(CommonSlidersRefresh(statusMode: PROVIDER_DRIVER));
orderBloc.add(
OrdersRefresh(
statusMode: statusMode,
currentPage: 1,
perPage: PER_PAGE_DO,
filter: filterQuery,
searchQuery: searchQuery,
sort: sortQuery,
),
);
shipmentBloc.add(ShipmentOrderRefresh(
statusMode: statusMode,
currentPage: 1,
perPage: PER_PAGE_DO,
filter: filterQuery,
searchQuery: searchQuery,
sort: sortQuery,
));
statsBloc.add(StatsRefresh(
date: date,
materialId: materialId,
));
});
}
_goInProgressOrder() {
print('_goInProgressOrder');
Get.to(() => InProgressOrderPage(
onSignedOut: widget.onSignedOut, userDriver: widget.userDriver));
}
_goShipment() {
print('_goShipment');
Get.to(() => ShipmentPage(onSignedOut: widget.onSignedOut));
}
void updateFilterQuery(String? _filterQuery, String? _sortQuery) {
setState(() {
filterQuery = _filterQuery!;
sortQuery = _sortQuery!;
});
print("filterQuery: $filterQuery");
_onRefresh();
}
}
Editor is loading...
Leave a Comment