fixed select item and add more than one services in appointment

fatima_dev
Fatimah.Alshammari 1 week ago
parent dce3638761
commit 1e22570a1c

@ -1,57 +1,53 @@
import 'dart:async';
import 'package:car_provider_app/views/dashboard/widget/general_appointment_widget.dart';
import 'package:car_provider_app/views/branch_management/schedule/widgets/chips_picker_item.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
import 'package:mc_common_app/view_models/service_view_model.dart';
import 'package:car_provider_app/views/appoinments/widget/select_items_sheet.dart';
import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:provider/provider.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/appointments_models/appointment_list_model.dart';
import 'package:mc_common_app/models/services_models/item_model.dart';
import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/appointments_view_model.dart';
import 'package:mc_common_app/widgets/bottom_sheet.dart';
import 'package:mc_common_app/view_models/service_view_model.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart';
import 'package:mc_common_app/widgets/empty_widget.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:mc_common_app/widgets/txt_field.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:mc_common_app/models/appointments_models/selected_appointment_service_model.dart';
import 'package:car_provider_app/views/appoinments/widget/add_appointment_service_bottom_sheet.dart';
class AddNewServiceAppointmentPage extends StatefulWidget {
final AppointmentListModel appointmentListModel;
const AddNewServiceAppointmentPage(this.appointmentListModel, {Key? key}) : super(key: key);
@override
State<AddNewServiceAppointmentPage> createState() => _AddNewServiceAppointmentPageState();
}
class _AddNewServiceAppointmentPageState extends State<AddNewServiceAppointmentPage> {
DropValue? category;
DropValue? service;
class AddNewServiceAppointmentPage extends StatefulWidget {
final AppointmentListModel appointmentListModel;
List<ItemData> selectedList = [];
const AddNewServiceAppointmentPage(this.appointmentListModel, {Key? key})
: super(key: key);
double totalPrice = 0.0;
@override
State<AddNewServiceAppointmentPage> createState() =>
_AddNewServiceAppointmentPageState();
}
List<PickerItem>? pickedItems;
class _AddNewServiceAppointmentPageState
extends State<AddNewServiceAppointmentPage> {
final List<SelectedAppointmentServiceModel> appointmentSelectedServices = [];
ServiceVM? serviceVM;
bool isSubmitting = false;
// Currency dropdown
// Keep the existing Custom Service currency dropdown unchanged.
DropValue? selectedCurrency;
List<DropValue> currencies = [
final List<DropValue> currencies = [
DropValue(1, 'SAR', 'SAR'),
DropValue(2, 'AED', 'AED'),
DropValue(3, 'USD', 'USD'),
@ -59,44 +55,121 @@ class _AddNewServiceAppointmentPageState extends State<AddNewServiceAppointmentP
@override
void initState() {
// TODO: implement initState
super.initState();
scheduleMicrotask(() {
if (!mounted) return;
serviceVM = context.read<ServiceVM>();
serviceVM!.fetchBranchCategory(EasyLocalization.of(context)?.currentLocale?.countryCode ?? "SA");
serviceVM!.fetchBranchCategory(
EasyLocalization.of(context)?.currentLocale?.countryCode ?? 'SA',
);
});
}
openItemsSelectionBottomSheet() async {
return showMyBottomSheet(
context,
child: SelectItemsSheet(
serviceId: service?.id ?? 0,
list: pickedItems,
onSelectItems: (List<ItemData> list) {
selectedList.clear();
selectedList.addAll(list);
pickedItems = [];
totalPrice = 0.0;
for (var element in list) {
totalPrice = totalPrice + double.parse(element.price ?? "0");
pickedItems!.add(
PickerItem(id: element.id ?? 0, title: element.name ?? ""),
);
}
setState(() {});
},
Future<void> _openAddServiceSheet({int? editIndex}) async {
if (isSubmitting) return;
final existing = editIndex == null
? null
: appointmentSelectedServices[editIndex];
final result = await showModalBottomSheet<SelectedAppointmentServiceModel>(
context: context,
isScrollControlled: true,
enableDrag: true,
builder: (_) => AddAppointmentServiceBottomSheet(
branchId: widget.appointmentListModel.branchId ?? 0,
initialService: existing,
),
);
if (!mounted || result == null) return;
setState(() {
if (editIndex == null) {
appointmentSelectedServices.add(result);
} else {
appointmentSelectedServices[editIndex] = result;
}
});
}
double get _totalItemsPrice => appointmentSelectedServices.fold<double>(
0,
(sum, selected) => sum + selected.totalPrice,
);
List<int> get _allSelectedItemIds => appointmentSelectedServices
.expand((selected) => selected.itemIds)
.where((id) => id > 0)
.toSet()
.toList();
Future<void> _submit() async {
if (isSubmitting) return;
final vm = serviceVM ?? context.read<ServiceVM>();
Map<String, dynamic> postParams;
if (vm.customServiceEnabledStatus) {
// Original Custom Service behavior, intentionally unchanged.
if (!vm.isCustomServiceDetailValidated()) return;
postParams = {
'providerBranchID': widget.appointmentListModel.branchId,
'appointmentID': widget.appointmentListModel.id,
'serviceItemsFreeText': [
{
'name': vm.customServiceTitle,
'price': vm.customServicePrice,
'tax': vm.customerServiceTax,
}
],
};
} else {
final ids = _allSelectedItemIds;
if (ids.isEmpty) {
Utils.showToast(LocaleKeys.pleaseSelectItems.tr());
return;
}
postParams = {
'providerBranchID': widget.appointmentListModel.branchId,
'appointmentID': widget.appointmentListModel.id,
'serviceItemID': ids,
};
}
setState(() => isSubmitting = true);
bool loadingVisible = false;
try {
Utils.showLoading(context);
loadingVisible = true;
final GenericRespModel res =
await vm.addNewServiceInAppointment(postParams);
if (!mounted) return;
Utils.hideLoading(context);
loadingVisible = false;
if (res.messageStatus == 1) {
vm.resetCustomServiceForm();
await _updateAppointment(context, widget.appointmentListModel.branchId ?? 0);
if (!mounted) return;
Utils.showToast(LocaleKeys.itemsAddedSuccessfully.tr());
// Return to the previous page after the successful API call.
// Original code popped twice; confirm whether your navigation stack
// still needs a second pop before adding it here.
pop(context);
} else {
Utils.showToast(res.message.toString());
}
} catch (e) {
if (mounted) Utils.showToast(e.toString());
} finally {
if (mounted && loadingVisible) Utils.hideLoading(context);
if (mounted) setState(() => isSubmitting = false);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: MyColors.white,
appBar: CustomAppBar(
title: LocaleKeys.addService.tr(),
),
appBar: CustomAppBar(title: LocaleKeys.addService.tr()),
body: Padding(
padding: const EdgeInsets.all(21.0),
child: Stack(
@ -127,11 +200,15 @@ class _AddNewServiceAppointmentPageState extends State<AddNewServiceAppointmentP
child: Checkbox(
value: serviceVm.customServiceEnabledStatus,
onChanged: (value) {
serviceVm.updateCustomServiceEnabledStatus(value ?? false); // update the status
serviceVm.updateCustomServiceEnabledStatus(
value ?? false,
);
},
activeColor: MyColors.darkPrimaryColor,
checkColor: MyColors.white,
fillColor: serviceVm.customServiceEnabledStatus ? WidgetStateProperty.all(MyColors.darkPrimaryColor) : WidgetStateProperty.all(MyColors.white),
fillColor: serviceVm.customServiceEnabledStatus
? WidgetStateProperty.all(MyColors.darkPrimaryColor)
: WidgetStateProperty.all(MyColors.white),
),
),
],
@ -167,10 +244,8 @@ class _AddNewServiceAppointmentPageState extends State<AddNewServiceAppointmentP
Expanded(
flex: 1,
child: DropdownField(
(DropValue value) {
setState(() {
selectedCurrency = value;
});
(DropValue value) {
setState(() => selectedCurrency = value);
},
dropdownValue: selectedCurrency,
list: currencies,
@ -184,7 +259,12 @@ class _AddNewServiceAppointmentPageState extends State<AddNewServiceAppointmentP
postfixWidget: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
"%".toText(fontWeight: MyFonts.Medium, fontSize: 15, color: borderColor, textAlign: TextAlign.center),
'%'.toText(
fontWeight: MyFonts.Medium,
fontSize: 15,
color: borderColor,
textAlign: TextAlign.center,
),
],
),
isNeedClickAll: true,
@ -200,95 +280,114 @@ class _AddNewServiceAppointmentPageState extends State<AddNewServiceAppointmentP
],
),
] else ...[
// READY-MADE SERVICES: select via Bottom Sheet, display cards.
LocaleKeys.selectServiceDetails.tr().toText(fontSize: 16),
12.height,
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
serviceVm.categoryDropList.isNotEmpty
? DropdownField(
(DropValue value) async {
category = value;
service = null;
serviceVm.fetchProviderServices(widget.appointmentListModel.branchId.toString(), value.id.toString());
},
dropdownValue: category,
list: serviceVm.categoryDropList,
hint: LocaleKeys.selectServiceCategory.tr(),
)
: const Center(
child: CircularProgressIndicator(),
),
12.height,
serviceVm.servicesDropList.isNotEmpty
? DropdownField(
(DropValue value) {
service = value;
pickedItems = null;
serviceVm.setState(ViewState.idle);
openItemsSelectionBottomSheet();
},
dropdownValue: service,
list: serviceVm.servicesDropList,
hint: LocaleKeys.defineServices.tr(),
)
: category == null
? Container()
: serviceVm.services != null && serviceVm.servicesDropList.isEmpty
? EmptyWidget(text: LocaleKeys.noServicesAvailable.tr())
: const CircularProgressIndicator(),
12.height,
(service != null && pickedItems != null && pickedItems!.isNotEmpty)
? ChipsPickerItem(
isNeedLabelOnTop: true,
hint: LocaleKeys.selectItems.tr(),
itemsList: [...pickedItems ?? []],
onClick: () {
openItemsSelectionBottomSheet();
},
)
: service != null
? EmptyWidget(text: LocaleKeys.noItemSelectedYet.tr())
: const SizedBox(),
if ((service != null && pickedItems != null && pickedItems!.isNotEmpty))
Column(
SizedBox(
width: double.infinity,
child: OutlinedButton.icon(
onPressed: isSubmitting ? null : () => _openAddServiceSheet(),
icon: const Icon(Icons.add_circle_outline),
label: Text(LocaleKeys.addServices.tr()),
style: OutlinedButton.styleFrom(
minimumSize: const Size.fromHeight(52),
foregroundColor: MyColors.darkPrimaryColor,
side: const BorderSide(color: MyColors.darkPrimaryColor),
),
),
),
12.height,
...appointmentSelectedServices.asMap().entries.map((entry) {
final index = entry.key;
final selected = entry.value;
return Card(
margin: const EdgeInsets.only(bottom: 12),
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
16.height,
LocaleKeys.totalAdditionalAmount.tr().toText(
fontSize: 14,
isBold: true,
color: MyColors.lightTextColor,
),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
totalPrice.toString().toText(
fontSize: 29,
isBold: true,
),
2.width,
LocaleKeys.sar
.tr()
.toText(
Expanded(
child: Text(
selected.service.value,
style: const TextStyle(
fontSize: 16,
isBold: true,
color: MyColors.lightTextColor,
)
.paddingOnly(bottom: 5),
fontWeight: FontWeight.bold,
),
),
),
// Edit
MyAssets.icEdit.buildSvg(
height: 17,
width: 17,
).onPress(() {
_openAddServiceSheet(editIndex: index);
}),
const SizedBox(width: 10),
// Delete
MyAssets.closeWithOrangeBg.buildSvg(
height: 30,
width: 30,
).onPress(() {
setState(() {
appointmentSelectedServices.removeAt(index);
});
}),
],
),
10.height,
Text(
selected.category.value,
style: const TextStyle(color: Colors.grey),
),
8.height,
...selected.items.map(
(item) => Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Row(
children: [
Expanded(child: Text(item.name ?? '')),
Text('${item.price ?? '0'} SAR'),
],
),
),
),
const Divider(),
Text(
'${selected.totalPrice.toStringAsFixed(2)} SAR',
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
],
),
],
),
),
);
}),
if (appointmentSelectedServices.isNotEmpty) ...[
12.height,
LocaleKeys.totalAdditionalAmount.tr().toText(
fontSize: 14,
isBold: true,
color: MyColors.lightTextColor,
),
Text(
'${_totalItemsPrice.toStringAsFixed(2)} SAR',
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
),
),
],
],
],
).toWhiteContainer(width: double.infinity, allPading: 12);
},
),
55.height,
85.height,
],
),
),
@ -306,10 +405,8 @@ class _AddNewServiceAppointmentPageState extends State<AddNewServiceAppointmentP
txtColor: MyColors.redColor,
borderColor: MyColors.redColor,
onPressed: () {
if (pickedItems != null && pickedItems!.isNotEmpty) {
pickedItems!.clear();
}
serviceVM!.resetCustomServiceForm();
(serviceVM ?? context.read<ServiceVM>())
.resetCustomServiceForm();
pop(context);
},
),
@ -319,58 +416,7 @@ class _AddNewServiceAppointmentPageState extends State<AddNewServiceAppointmentP
child: ShowFillButton(
title: LocaleKeys.add.tr(),
maxWidth: double.infinity,
onPressed: () async {
Map<String, dynamic> postParams = {};
if (serviceVM!.customServiceEnabledStatus) {
if (serviceVM!.isCustomServiceDetailValidated()) {
postParams = {
"providerBranchID": widget.appointmentListModel.branchId,
"appointmentID": widget.appointmentListModel.id,
"serviceItemsFreeText": [
{
"name": serviceVM!.customServiceTitle,
"price": serviceVM!.customServicePrice,
"tax": serviceVM!.customerServiceTax,
}
],
};
} else {
return;
}
} else {
if (pickedItems != null && pickedItems!.isNotEmpty) {
List<int> items = [];
pickedItems?.forEach((element) {
items.add(element.id);
});
postParams = {
"providerBranchID": widget.appointmentListModel.branchId,
"appointmentID": widget.appointmentListModel.id,
"serviceItemID": items,
};
} else {
Utils.showToast(LocaleKeys.pleaseSelectItems.tr());
return;
}
}
try {
Utils.showLoading(context);
GenericRespModel res = await serviceVM!.addNewServiceInAppointment(postParams);
Utils.hideLoading(context);
if (res.messageStatus == 1) {
serviceVM!.resetCustomServiceForm();
_updateAppointment(context, widget.appointmentListModel.branchId ?? 0);
Utils.showToast(LocaleKeys.itemsAddedSuccessfully.tr());
pop(context);
pop(context);
} else {
Utils.showToast(res.message.toString());
}
} catch (e) {
Utils.showToast(e.toString());
Utils.hideLoading(context);
}
},
onPressed: _submit,
),
),
],
@ -384,9 +430,9 @@ class _AddNewServiceAppointmentPageState extends State<AddNewServiceAppointmentP
Future<void> _updateAppointment(BuildContext context, int branchId) async {
await context.read<AppointmentsVM>().applyFilterOnAppointmentsVMForProviders(
branchID: branchId,
appointmentStatusEnum: AppointmentStatusEnum.allAppointments,
isNeedCustomerFilter: true,
);
branchID: branchId,
appointmentStatusEnum: AppointmentStatusEnum.allAppointments,
isNeedCustomerFilter: true,
);
}
}

@ -0,0 +1,224 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/services_models/item_model.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/view_models/service_view_model.dart';
import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart';
import 'package:mc_common_app/widgets/bottom_sheet.dart';
import 'package:car_provider_app/views/appoinments/widget/select_items_sheet.dart';
import 'package:car_provider_app/views/branch_management/schedule/widgets/chips_picker_item.dart';
import 'package:mc_common_app/models/appointments_models/selected_appointment_service_model.dart';
import 'package:car_provider_app/views/branch_management/schedule/widgets/chips_picker_item.dart';
class AddAppointmentServiceBottomSheet extends StatefulWidget {
final int branchId;
final SelectedAppointmentServiceModel? initialService;
const AddAppointmentServiceBottomSheet({
super.key,
required this.branchId,
this.initialService,
});
@override
State<AddAppointmentServiceBottomSheet> createState() =>
_AddAppointmentServiceBottomSheetState();
}
class _AddAppointmentServiceBottomSheetState
extends State<AddAppointmentServiceBottomSheet> {
DropValue? category;
DropValue? service;
List<ItemData> selectedItems = [];
bool isLoadingServices = false;
String? loadError;
@override
void initState() {
super.initState();
category = widget.initialService?.category;
service = widget.initialService?.service;
selectedItems = List.of(widget.initialService?.items ?? []);
if (category != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) _loadServices(category!.id);
});
}
}
Future<void> _loadServices(int categoryId) async {
setState(() {
isLoadingServices = true;
loadError = null;
});
try {
await context.read<ServiceVM>().fetchProviderServices(
widget.branchId.toString(),
categoryId.toString(),
);
} catch (e) {
if (mounted) loadError = e.toString();
} finally {
if (mounted) setState(() => isLoadingServices = false);
}
}
Future<void> _openItemsSheet() async {
if (service == null) return;
final serviceId = service!.id;
final pickedItems = selectedItems
.map((item) => PickerItem(id: item.id ?? 0, title: item.name ?? ''))
.toList();
showMyBottomSheet(
context,
child: SelectItemsSheet(
serviceId: serviceId,
list: pickedItems,
onSelectItems: (items) {
if (!mounted) return;
setState(() => selectedItems = List.of(items));
},
),
);
}
void _confirm() {
if (category == null || service == null || selectedItems.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(LocaleKeys.pleaseSelectItems.tr())),
);
return;
}
Navigator.of(context).pop(
SelectedAppointmentServiceModel(
category: category!,
service: service!,
items: List.of(selectedItems),
),
);
}
@override
Widget build(BuildContext context) {
return SizedBox(
height: MediaQuery.of(context).size.height * 0.85,
child: SafeArea(
top: false,
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(
child: Container(
width: 60,
height: 6,
decoration: BoxDecoration(
color: Colors.grey.shade300,
borderRadius: BorderRadius.circular(20),
),
),
),
const SizedBox(height: 20),
Text(
LocaleKeys.selectServiceDetails.tr(),
style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
),
const SizedBox(height: 20),
Expanded(
child: SingleChildScrollView(
child: Consumer<ServiceVM>(
builder: (context, serviceVm, _) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
DropdownField(
(value) {
setState(() {
category = value;
service = null;
selectedItems.clear();
});
_loadServices(value.id);
},
dropdownValue: category,
list: serviceVm.categoryDropList,
hint: LocaleKeys.selectServiceCategory.tr(),
isSelectAble: !isLoadingServices,
),
if (category != null) ...[
const SizedBox(height: 16),
if (isLoadingServices)
const Center(child: CircularProgressIndicator())
else if (loadError != null)
TextButton(
onPressed: () => _loadServices(category!.id),
child: const Text('Retry loading services'),
)
else
DropdownField(
(value) {
setState(() {
service = value;
selectedItems.clear();
});
},
dropdownValue: service,
list: serviceVm.servicesDropList,
hint: LocaleKeys.defineServices.tr(),
),
],
if (service != null) ...[
const SizedBox(height: 16),
ChipsPickerItem(
isNeedLabelOnTop: true,
hint: LocaleKeys.selectItems.tr(),
itemsList: selectedItems.map((item) {
return PickerItem(
id: item.id ?? 0,
title: item.name ?? "",
);
}).toList(),
onClick: () {
_openItemsSheet();
},
),
...selectedItems.map(
(item) => ListTile(
contentPadding: EdgeInsets.zero,
title: Text(item.name ?? ''),
trailing: Text('${item.price ?? '0'} SAR'),
),
),
],
],
);
},
),
),
),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: isLoadingServices ? null : _confirm,
style: ElevatedButton.styleFrom(
backgroundColor: MyColors.darkPrimaryColor,
foregroundColor: Colors.white,
minimumSize: const Size.fromHeight(50),
),
child: Text(LocaleKeys.next.tr()),
),
),
],
),
),
),
);
}
}

@ -14,16 +14,52 @@ import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
class SelectItemsSheet extends StatelessWidget {
class SelectItemsSheet extends StatefulWidget {
final int serviceId;
final Function(List<ItemData>) onSelectItems;
final List<PickerItem>? list;
const SelectItemsSheet({super.key, required this.serviceId, required this.onSelectItems, this.list});
const SelectItemsSheet({
super.key,
required this.serviceId,
required this.onSelectItems,
this.list,
});
@override
State<SelectItemsSheet> createState() => _SelectItemsSheetState();
}
class _SelectItemsSheetState extends State<SelectItemsSheet> {
bool _loading = true;
String? _error;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) => _fetchItems());
}
Future<void> _fetchItems() async {
if (!mounted) return;
setState(() {
_loading = true;
_error = null;
});
try {
await context.read<ItemsVM>().getServiceItems(
widget.serviceId,
list: widget.list,
);
} catch (e) {
_error = e.toString();
} finally {
if (mounted) setState(() => _loading = false);
}
}
@override
Widget build(BuildContext context) {
context.read<ItemsVM>().getServiceItems(serviceId, list: list);
return SizedBox(
width: double.infinity,
height: MediaQuery.of(context).size.height / 1.4,
@ -32,6 +68,11 @@ class SelectItemsSheet extends StatelessWidget {
Expanded(
child: Consumer<ItemsVM>(
builder: (_, itemsVM, child) {
final items = itemsVM.serviceItems?.data;
final selectedCount = items
?.where((element) => element.isUpdateOrSelected == true)
.length ??
0;
return Padding(
padding: const EdgeInsets.only(left: 20, right: 20, top: 6),
child: Column(
@ -39,10 +80,13 @@ class SelectItemsSheet extends StatelessWidget {
Row(
children: [
Expanded(
child: LocaleKeys.selectItems.tr().toText(fontSize: 24, isBold: true),
child: LocaleKeys.selectItems.tr().toText(
fontSize: 24,
isBold: true,
),
),
Center(
child: (itemsVM.serviceItems == null ? "0" : itemsVM.serviceItems!.data!.where((element) => element.isUpdateOrSelected == true).toList().length.toString()).toText(
child: selectedCount.toString().toText(
fontSize: 10,
isBold: true,
color: Colors.white,
@ -56,38 +100,46 @@ class SelectItemsSheet extends StatelessWidget {
),
],
),
// 12.height,
// TxtField(
// hint: LocaleKeys.searchItems.tr(),
// onChanged: (v) {},
// ),
12.height,
Expanded(
child: itemsVM.serviceItems?.data == null
child: _loading
? const Center(child: CircularProgressIndicator())
: itemsVM.serviceItems?.data!.length == 0
? Center(child: EmptyWidget(text: LocaleKeys.noItemsToShow.tr()))
: ListView.separated(
itemBuilder: (BuildContext context, int index) {
return CheckboxListTile(
contentPadding: EdgeInsets.zero,
controlAffinity: ListTileControlAffinity.leading,
title: itemsVM.serviceItems!.data![index].name!.toText(fontSize: 16),
subtitle: itemsVM.serviceItems!.data![index].price.toString().toText(fontSize: 14, color: MyColors.lightTextColor),
value: itemsVM.serviceItems!.data![index].isUpdateOrSelected,
onChanged: (bool? v) {
itemsVM.serviceItems!.data![index].isUpdateOrSelected = v;
log("itemsVM.serviceItems!.data![index].isUpdateOrSelected: ${itemsVM.serviceItems!.data![index].isUpdateOrSelected}");
itemsVM.notifyListeners();
},
);
},
separatorBuilder: (BuildContext context, int index) {
return const Divider(height: 1);
},
itemCount: itemsVM.serviceItems?.data?.length ?? 0,
),
: _error != null
? Center(
child: TextButton(
onPressed: _fetchItems,
child: Text('Failed to load items. Retry: $_error'),
),
)
: items == null || items.isEmpty
? Center(
child: EmptyWidget(
text: LocaleKeys.noItemsToShow.tr(),
),
)
: ListView.separated(
itemBuilder: (context, index) {
final item = items[index];
return CheckboxListTile(
contentPadding: EdgeInsets.zero,
controlAffinity: ListTileControlAffinity.leading,
title: (item.name ?? '').toText(fontSize: 16),
subtitle: item.price.toString().toText(
fontSize: 14,
color: MyColors.lightTextColor,
),
value: item.isUpdateOrSelected ?? false,
onChanged: (bool? value) {
item.isUpdateOrSelected = value;
log('Item selected: ${item.isUpdateOrSelected}');
itemsVM.notifyListeners();
},
);
},
separatorBuilder: (_, __) =>
const Divider(height: 1),
itemCount: items.length,
),
),
],
),
@ -100,20 +152,15 @@ class SelectItemsSheet extends StatelessWidget {
maxWidth: double.infinity,
margin: const EdgeInsets.all(20),
onPressed: () {
if (_loading || _error != null) return;
final itemsVM = context.read<ItemsVM>();
if (itemsVM.serviceItems != null) {
List<ItemData> list = [];
for (var element in itemsVM.serviceItems!.data!) {
if (element.isUpdateOrSelected ?? false) {
list.add(element);
}
}
onSelectItems(list);
}
final selected = (itemsVM.serviceItems?.data ?? <ItemData>[])
.where((element) => element.isUpdateOrSelected == true)
.toList();
widget.onSelectItems(selected);
Navigator.pop(context);
},
)
),
],
),
);

@ -274,7 +274,7 @@ class _BranchDetailPageState extends State<BranchDetailPage> {
children: [
showData("${LocaleKeys.location.tr()}:",
"${isArabic ? widget.branchData.cityNameN.toString() : widget.branchData.cityName.toString()}, ${isArabic ? widget.branchData.countryNameN.toString() : widget.branchData.countryName.toString()}"),
showData("${LocaleKeys.provider.tr()}:", widget.branchData.serviceProviderName.toString()),
showData("${LocaleKeys.companyName.tr()}:", widget.branchData.serviceProviderName.toString()),
showData("${LocaleKeys.time.tr()}:", "${widget.branchData.openTime.toString()} - ${widget.branchData.closeTime.toString()}"),
showData("${LocaleKeys.address.tr()}:", widget.branchData.address.toString()),
],

Loading…
Cancel
Save