Merge branch 'faiz_development' of http://34.17.52.79/Haroon6138/car_customer_app into faiz_development

 Conflicts:
	lib/config/customer_routes.dart
	lib/repositories/provider_repo.dart
	lib/view_models/appointments_view_model.dart
	lib/view_models/providers_view_model.dart
	lib/views/dashboard/fragments/branches_fragment.dart
	lib/views/dashboard/fragments/providers_fragment.dart
	lib/views/provider/branch_detail_page.dart
	lib/views/provider/provider_profile_page.dart
	pubspec.yaml
pull/3/head
FaizHashmiCS22 3 years ago
commit 83287f3b37

@ -4,7 +4,7 @@ import 'package:mc_common_app/config/dependencies.dart';
class CustomerDependencies {
static void addDependencies() {
AppDependencies.addDependencies();
injector.registerSingleton<ProviderRepo>(() => ProviderRepoImp());
injector.registerSingleton<BranchRepo>(() => BranchRepoImp());
}
}

@ -26,6 +26,8 @@ import 'package:provider/provider.dart';
import 'package:provider/single_child_widget.dart';
import 'package:sizer/sizer.dart';
//test commit
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
CustomerDependencies.addDependencies();
@ -56,6 +58,7 @@ Future<void> main() async {
create: (_) => AppointmentsVM(
providerRepo: injector.get<ProviderRepo>(),
commonServices: injector.get<CommonAppServices>(),
branchRepo: injector.get<BranchRepo>(),
commonRepo: injector.get<CommonRepo>(),
),
),

@ -0,0 +1,254 @@
import 'package:car_customer_app/view_models/appointments_view_model.dart';
import 'package:car_customer_app/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/models/widgets_models.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/views/advertisement/custom_add_button.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/common_widgets/info_bottom_sheet.dart';
import 'package:mc_common_app/widgets/common_widgets/time_slots.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:provider/provider.dart';
class AppointmentServicesListView extends StatelessWidget {
AppointmentServicesListView({Key? key}) : super(key: key);
void openTheAddServiceBottomSheet(BuildContext context, AppointmentsVM appointmentsVM) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
enableDrag: true,
builder: (BuildContext context) {
return AppointmentServicePickBottomSheet();
},
);
}
final _dummySlots = [
TimeSlotModel(isSelected: false, slotId: 1, slot: "11:00"),
TimeSlotModel(isSelected: false, slotId: 2, slot: "12:00"),
TimeSlotModel(isSelected: true, slotId: 3, slot: "13:00"),
TimeSlotModel(isSelected: false, slotId: 4, slot: "14:00"),
TimeSlotModel(isSelected: false, slotId: 5, slot: "15:00"),
TimeSlotModel(isSelected: false, slotId: 6, slot: "16:00"),
TimeSlotModel(isSelected: false, slotId: 7, slot: "17:00"),
TimeSlotModel(isSelected: false, slotId: 8, slot: "18:00"),
TimeSlotModel(isSelected: false, slotId: 9, slot: "19:00"),
TimeSlotModel(isSelected: false, slotId: 10, slot: "20:00"),
TimeSlotModel(isSelected: false, slotId: 11, slot: "21:00"),
TimeSlotModel(isSelected: false, slotId: 12, slot: "22:00"),
TimeSlotModel(isSelected: false, slotId: 13, slot: "23:00"),
TimeSlotModel(isSelected: false, slotId: 14, slot: "24:00"),
TimeSlotModel(isSelected: false, slotId: 15, slot: "25:00"),
TimeSlotModel(isSelected: false, slotId: 16, slot: "26:00"),
TimeSlotModel(isSelected: false, slotId: 17, slot: "27:00"),
TimeSlotModel(isSelected: false, slotId: 18, slot: "28:00"),
TimeSlotModel(isSelected: false, slotId: 19, slot: "29:00"),
];
void priceBreakDownClicked(BuildContext context) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
enableDrag: true,
builder: (BuildContext context) {
return InfoBottomSheet(
title: "Charges Breakdown",
description: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Services".toText(fontSize: 16, isBold: true),
Column(
children: List.generate(
5,
(index) => Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Gear Kit".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
"200 SAR".toText(fontSize: 12, isBold: true),
],
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
"1149 SAR".toText(fontSize: 16, isBold: true),
],
),
20.height,
"Home Location".toText(fontSize: 16, isBold: true),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"10km ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
"5 x 10".toText(fontSize: 12, isBold: true),
],
),
8.height,
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
"50 SAR".toText(fontSize: 16, isBold: true),
],
),
30.height,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Total Amount ".toText(fontSize: 16, isBold: true),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
"120".toText(fontSize: 29, isBold: true),
2.width,
"SAR".toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5),
],
)
],
),
30.height,
],
));
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
title: "Book Appointment",
isRemoveBackButton: false,
isDrawerEnabled: false,
actions: [MyAssets.searchIcon.buildSvg().paddingOnly(right: 21)],
onBackButtonTapped: () => Navigator.pop(context),
),
body: Consumer(
builder: (BuildContext context, AppointmentsVM appointmentsVM, Widget? child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
21.height,
CustomAddButton(
onTap: () => openTheAddServiceBottomSheet(context, appointmentsVM),
text: "Add Services",
icon: Container(
height: 24,
width: 24,
decoration: const BoxDecoration(shape: BoxShape.circle, color: MyColors.darkTextColor),
child: const Icon(Icons.add, color: MyColors.white),
),
).horPaddingMain(),
10.height,
ListView.builder(
shrinkWrap: true,
itemCount: 20,
itemBuilder: (BuildContext context, int index) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: "Mechanic".toText(fontSize: 16, isBold: true),
),
Align(
alignment: Alignment.topRight,
child: MyAssets.icEdit.buildSvg(fit: BoxFit.fill, height: 19, width: 19),
).onPress(() {}),
],
),
Builder(builder: (context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (true) ...[
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Service Location: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
("Home").toText(fontSize: 12, isBold: true).expand(),
],
),
8.height,
Column(
children: List.generate(
5,
(index) => Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Gear Kit: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
("200 SAR").toText(fontSize: 12, isBold: true).expand(),
],
),
),
),
8.height,
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
"120".toText(fontSize: 29, isBold: true),
2.width,
"SAR".toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5),
Icon(
Icons.arrow_drop_down,
size: 30,
)
],
).onPress(() => priceBreakDownClicked(context)),
],
if (true) ...[
8.height,
("Available Slots").toText(fontSize: 14, isBold: true),
5.height,
SizedBox(
width: double.infinity,
child: BuildTimeSlots(
timeSlots: _dummySlots,
onPressed: (index) => null,
),
),
],
],
);
}),
],
).toWhiteContainer(width: double.infinity, allPading: 12, margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 10));
},
).expand(),
Row(
children: [
Expanded(
child: ShowFillButton(
txtColor: MyColors.black,
maxHeight: 55,
title: "Cancel",
onPressed: () {},
backgroundColor: MyColors.greyButtonColor,
),
),
12.width,
Expanded(
child: ShowFillButton(
maxHeight: 55,
title: "Review",
onPressed: () {
navigateWithName(context, AppRoutes.reviewAppointmentView);
},
backgroundColor: MyColors.darkPrimaryColor,
),
)
],
).paddingAll(21)
],
);
},
));
}
}

@ -1,325 +0,0 @@
import 'package:car_customer_app/view_models/appointments_view_model.dart';
import 'package:flutter/material.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/models/widgets_models.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/views/advertisement/custom_add_button.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/extensions/extensions_widget.dart';
import 'package:mc_common_app/widgets/txt_field.dart';
import 'package:provider/provider.dart';
class BookAppointmentServicesView extends StatelessWidget {
const BookAppointmentServicesView({Key? key}) : super(key: key);
void openTheAddServiceBottomSheet(BuildContext context) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
enableDrag: true,
builder: (BuildContext context) {
AppointmentsVM appointmentsVM = context.watch<AppointmentsVM>();
return SizedBox(
height: MediaQuery.of(context).size.height * 0.85,
child: Column(
children: [
Container(
margin: const EdgeInsets.all(8),
height: 8,
width: 60,
decoration: const BoxDecoration(color: MyColors.lightTextColor, borderRadius: BorderRadius.all(Radius.circular(20))),
),
12.height,
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
"Select Category".toText(fontSize: 24, isBold: true),
],
),
30.height,
Expanded(
child: ListView(
children: [
Builder(
builder: (context) {
List<DropValue> serviceCategories = [];
for (var element in appointmentsVM.providerCategories) {
if (!element.isSelected!) {
serviceCategories.add(DropValue(element.id?.toInt() ?? 0, element.categoryName ?? "", ""));
}
}
return DropdownField(
(DropValue value) => appointmentsVM.updateProviderCategoryId(SelectionModel(selectedId: value.id, selectedOption: value.value, itemPrice: value.subValue)),
list: serviceCategories,
hint: "Select Category",
dropdownValue: appointmentsVM.providerCategoryId.selectedId != -1
? DropValue(appointmentsVM.providerCategoryId.selectedId, appointmentsVM.providerCategoryId.selectedOption, "")
: null,
);
},
),
if (appointmentsVM.isFetchingServices) ...[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [const CircularProgressIndicator().paddingAll(10)],
),
] else if (appointmentsVM.providerServices.isNotEmpty) ...[
8.height,
Builder(
builder: (context) {
List<DropValue> serviceCategories = [];
for (var element in appointmentsVM.providerServices) {
if (!element.isSelected!) {
serviceCategories.add(DropValue(element.id?.toInt() ?? 0, element.id.toString(), ""));
}
}
return DropdownField(
(DropValue value) => appointmentsVM.updateProviderServiceId(SelectionModel(selectedId: value.id, selectedOption: value.value, itemPrice: value.subValue)),
list: serviceCategories,
hint: "Select Services",
dropdownValue: appointmentsVM.providerServiceId.selectedId != -1
? DropValue(appointmentsVM.providerServiceId.selectedId, appointmentsVM.providerServiceId.selectedOption, "")
: null,
);
},
),
],
if (appointmentsVM.providerServiceId.selectedId != -1 && !appointmentsVM.isFetchingServices) ...[
16.height,
Row(
children: [
"Select Service Location".toText(
fontSize: 16,
isBold: true,
color: MyColors.black,
),
],
),
8.height,
Row(
children: [
Expanded(
child: ShowFillButton(
isFilled: appointmentsVM.isHomeTapped,
maxHeight: 48,
title: "Home",
txtColor: appointmentsVM.isHomeTapped ? MyColors.white : MyColors.darkTextColor,
onPressed: () => appointmentsVM.updateIsHomeTapped(true),
),
),
12.width,
Expanded(
child: ShowFillButton(
isFilled: !appointmentsVM.isHomeTapped,
txtColor: !appointmentsVM.isHomeTapped ? MyColors.white : MyColors.darkTextColor,
maxHeight: 48,
title: "Workshop",
onPressed: () => appointmentsVM.updateIsHomeTapped(false),
),
),
],
),
if (appointmentsVM.isHomeTapped) ...[
8.height,
TxtField(
hint: 'Pick Home Location',
value: appointmentsVM.pickedHomeLocation,
isNeedClickAll: true,
postfixData: Icons.location_on,
postFixDataColor: MyColors.darkTextColor,
onTap: () {
//TODO: open the place picked to pick the location and save it in provider.
appointmentsVM.updatePickedHomeLocation("PM58+F97, Al Olaya, Riyadh 12333");
},
),
14.height,
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Icon(
Icons.warning,
color: MyColors.adPendingStatusColor,
size: 19,
).paddingOnly(bottom: 2),
3.width,
"Some services are not available on home location.".toText(
color: MyColors.adPendingStatusColor,
fontSize: 12,
isItalic: true,
),
],
),
]
],
],
),
),
SizedBox(
width: double.infinity,
child: Column(
children: [
if (appointmentsVM.isHomeTapped && !appointmentsVM.isFetchingServices) ...[
const Divider(thickness: 1, height: 1),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
// TODO: This Price will be decided according to the service selected
150.toString().toText(fontSize: 30, isBold: true),
"SAR".toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5),
],
),
"These charges are additional to the actual service charges. For heavy items the charges may vary.".toText(fontSize: 12, color: MyColors.lightTextColor),
22.height,
],
SizedBox(
width: double.infinity,
child: ShowFillButton(
maxHeight: 55,
title: "Next",
onPressed: () {},
),
),
],
),
).paddingOnly(bottom: 20)
],
)).horPaddingMain();
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
title: "Book Appointment",
isRemoveBackButton: false,
isDrawerEnabled: false,
actions: [MyAssets.searchIcon.buildSvg().paddingOnly(right: 21)],
onBackButtonTapped: () => Navigator.pop(context),
),
body: Consumer(
builder: (BuildContext context, AppointmentsVM appointmentsVM, Widget? child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
21.height,
CustomAddButton(
onTap: () => {openTheAddServiceBottomSheet(context)},
text: "Add Services",
icon: Container(
height: 24,
width: 24,
decoration: const BoxDecoration(shape: BoxShape.circle, color: MyColors.darkTextColor),
child: const Icon(Icons.add, color: MyColors.white),
),
),
// 20.height,
// ListView.builder(
// physics: const NeverScrollableScrollPhysics(),
// shrinkWrap: true,
// itemCount: adVM.specialServiceCards.length,
// itemBuilder: (BuildContext context, int index) {
// SpecialServiceCard specialServicesCard = adVM.specialServiceCards[index];
// return Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Column(
// children: [
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Expanded(
// child: specialServicesCard.serviceSelectedId!.selectedOption.toText(fontSize: 16, isBold: true),
// ),
// Align(
// alignment: Alignment.topRight,
// child: MyAssets.closeWithOrangeBg.buildSvg(
// fit: BoxFit.fill,
// height: 30,
// width: 30,
// ),
// ).onPress(() => adVM.removeSpecialServiceCard(index))
// ],
// ),
// Builder(builder: (context) {
// return Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// if (specialServicesCard.serviceSelectedId!.selectedId != 1 && specialServicesCard.serviceSelectedId!.selectedId != 3) ...[
// // Row(
// // crossAxisAlignment: CrossAxisAlignment.start,
// // children: [
// // "Duration: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
// // (specialServicesCard.duration ?? "").toText(fontSize: 12, isBold: true).expand(),
// // ],
// // ),
// 8.height,
// Row(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// "Description: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
// (specialServicesCard.description ?? "").toText(fontSize: 12, isBold: true).expand(),
// ],
// ),
// ],
// if (specialServicesCard.serviceSelectedId!.selectedId == 1 || specialServicesCard.serviceSelectedId!.selectedId == 3) ...[
// Row(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// const Icon(
// weight: 2,
// Icons.location_on_outlined,
// color: MyColors.primaryColor,
// size: 17,
// ),
// "${specialServicesCard.duration} km".toText(fontSize: 10, color: MyColors.primaryColor),
// ],
// ),
// 8.height,
// Row(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// "Branch Address: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
// (specialServicesCard.address ?? "").toText(fontSize: 12, isBold: true).expand(),
// ],
// ),
// Row(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// "Appointment Time: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
// "${specialServicesCard.serviceDate} - ${specialServicesCard.serviceTime}".toText(fontSize: 12, isBold: true).expand(),
// ],
// ),
// ],
// 6.height,
// Row(
// crossAxisAlignment: CrossAxisAlignment.end,
// children: [
// (specialServicesCard.serviceSelectedId!.itemPrice).toText(fontSize: 20, isBold: true),
// 2.width,
// "SAR".toText(color: MyColors.lightTextColor, fontSize: 14),
// ],
// ),
// ],
// );
// }),
// 3.height,
// const Divider(thickness: 1.5)
// ],
// ),
// 10.height,
// ],
// );
// },
// ),
],
);
},
).horPaddingMain());
}
}

@ -0,0 +1,113 @@
import 'package:car_customer_app/view_models/appointments_view_model.dart';
import 'package:car_customer_app/views/appointments/widgets/service_item_with_price_checkbox.dart';
import 'package:flutter/material.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/theme/colors.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/extensions/extensions_widget.dart';
import 'package:provider/provider.dart';
class BookAppointmentsItemView extends StatelessWidget {
const BookAppointmentsItemView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
title: "Select Services",
isRemoveBackButton: false,
isDrawerEnabled: false,
actions: [MyAssets.searchIcon.buildSvg().paddingOnly(right: 21)],
onBackButtonTapped: () => Navigator.pop(context),
),
body: Consumer(
builder: (BuildContext context, AppointmentsVM appointmentsVM, Widget? child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 40,
width: double.infinity,
color: MyColors.darkTextColor,
alignment: Alignment.centerLeft,
child: "3 Items Selected".toText(fontSize: 16, color: MyColors.white).horPaddingMain(),
),
16.height,
Column(
children: [
"Few services are not available on home location. Change the location to workshop to full access the services".toText(fontSize: 12, isItalic: true, color: MyColors.lightTextColor),
8.height,
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Change location or service: ".toText(fontSize: 14, isBold: true),
"Edit".toText(fontSize: 14, isBold: true, isUnderLine: true, color: MyColors.adPendingStatusColor),
5.width,
MyAssets.icEdit.buildSvg(width: 17),
],
).onPress(() {}),
16.height,
Divider(),
],
).horPaddingMain(),
ListView.separated(
separatorBuilder: (BuildContext context, int index) => Divider(),
itemCount: 20,
itemBuilder: (BuildContext context, int index) {
return ServiceItemWithPriceCheckBox(
description: "Some description about the sub-services",
title: "Engine Check",
isSelected: true,
onSelection: (bool value) {},
priceWidget: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
// TODO: This Price will be decided according to the service selected
150.toString().toText(fontSize: 30, isBold: true),
"SAR".toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5),
],
),
);
},
).expand(),
Column(
children: [
Divider(
height: 1,
thickness: 0.7,
),
16.height,
Row(
children: [
Expanded(
child: ShowFillButton(
txtColor: MyColors.black,
maxHeight: 55,
title: "Cancel",
onPressed: () {},
backgroundColor: MyColors.greyButtonColor,
),
),
12.width,
Expanded(
child: ShowFillButton(
maxHeight: 55,
title: "Next",
onPressed: () {},
backgroundColor: MyColors.darkPrimaryColor,
),
),
],
).horPaddingMain(),
16.height,
],
),
],
);
},
));
}
}

@ -0,0 +1,265 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.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/theme/colors.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/extensions/extensions_widget.dart';
class ReviewAppointment extends StatelessWidget {
const ReviewAppointment({Key? key}) : super(key: key);
Widget buildBranchInfoCard({
required String branchImageUrl,
required String branchName,
required String branchRatings,
required String branchLocation,
}) {
return Padding(
padding: const EdgeInsets.only(
bottom: 10,
left: 21,
right: 21,
),
child: Row(
children: [
Image.asset(
branchImageUrl,
width: 80,
height: 60,
fit: BoxFit.cover,
),
12.width,
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
branchName.toText(fontSize: 16, isBold: true),
Row(
children: [
LocaleKeys.location.tr().toText(color: MyColors.lightTextColor, fontSize: 12),
2.width,
":$branchLocation".toText(fontSize: 12),
],
),
],
),
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
branchRatings.toText(
isUnderLine: true,
isBold: true,
fontSize: 12,
),
2.width,
MyAssets.starIcon.buildSvg(width: 12),
],
),
],
),
],
),
),
],
).onPress(() {}).toWhiteContainer(width: double.infinity, allPading: 12));
}
Widget buildServicesSubSection({required String title, required List<String> itemList}) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
title.toText(fontSize: 16, isBold: true),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: List.generate(
itemList.length,
(index) => itemList[index].toText(
fontSize: 12,
color: MyColors.lightTextColor,
isBold: true,
)),
),
],
).paddingOnly(bottom: 14);
}
Widget buildServicesInfoCard() {
return SizedBox(
width: double.infinity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: List.generate(
2,
(index) => buildServicesSubSection(
title: "Mechanic",
itemList: [
"Engine Check : 450 SAR",
"Gear Check : 700 SAR",
"Gear Check : 700 SAR",
"Gear Check : 700 SAR",
],
),
),
).toWhiteContainer(width: double.infinity, allPading: 12, margin: EdgeInsets.symmetric(horizontal: 21)),
);
}
Widget buildTimeAndLocationInfoCard() {
return SizedBox(
width: double.infinity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Time & Location".toText(fontSize: 16, isBold: true),
3.height,
Row(children: [
"Date & Time: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
"2nd Feb, 2023 at 09:30 AM".toText(fontSize: 12, isBold: true),
]),
Row(children: [
"Location: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
"PM58+F97, Al Olaya, Riyadh 12333".toText(fontSize: 12, isBold: true),
]),
],
).toWhiteContainer(width: double.infinity, allPading: 12, margin: EdgeInsets.only(left: 21, right: 21, top: 10)),
);
}
Widget buildChargesBreakDown() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Services".toText(fontSize: 16, isBold: true),
Column(
children: List.generate(
5,
(index) => Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Gear Kit".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
"200 SAR".toText(fontSize: 12, isBold: true),
],
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
"1149 SAR".toText(fontSize: 16, isBold: true),
],
),
10.height,
Divider(
thickness: 0.7,
),
"Home Location".toText(fontSize: 16, isBold: true),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"10km ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
"5 x 10".toText(fontSize: 12, isBold: true),
],
),
8.height,
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
"50 SAR".toText(fontSize: 16, isBold: true),
],
),
10.height,
Divider(
thickness: 0.7,
),
10.height,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Total Amount ".toText(fontSize: 16, isBold: true),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
"120".toText(fontSize: 29, isBold: true),
2.width,
"SAR".toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5),
],
)
],
),
10.height,
],
).toWhiteContainer(width: double.infinity, allPading: 12, margin: EdgeInsets.only(left: 21, right: 21, top: 10, bottom: 21));
}
Widget buildNextButtonFooter() {
return Container(
height: 90,
color: MyColors.white,
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Divider(thickness: 0.7, height: 3),
10.height,
SizedBox(
width: double.infinity,
child: ShowFillButton(
maxHeight: 55,
backgroundColor: MyColors.primaryColor,
title: "Book Appointment",
onPressed: () {},
).paddingOnly(bottom: 12, left: 21, right: 21),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
title: "Review Appointment",
isRemoveBackButton: false,
isDrawerEnabled: false,
actions: [MyAssets.searchIcon.buildSvg().paddingOnly(right: 21)],
onBackButtonTapped: () => Navigator.pop(context),
),
body: Stack(
alignment: Alignment.bottomCenter,
children: [
SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildBranchInfoCard(
branchImageUrl: MyAssets.bnCar,
branchLocation: " 3km",
branchName: "Al Ahmed Maintenance",
branchRatings: "4.9",
),
buildServicesInfoCard(),
buildTimeAndLocationInfoCard(),
buildChargesBreakDown(),
],
),
),
buildNextButtonFooter(),
],
),
);
}
}

@ -0,0 +1,194 @@
import 'package:car_customer_app/view_models/appointments_view_model.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/models/widgets_models.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/dropdown/dropdow_field.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';
class AppointmentServicePickBottomSheet extends StatelessWidget {
const AppointmentServicePickBottomSheet({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
AppointmentsVM appointmentsVM = context.watch<AppointmentsVM>();
return SizedBox(
height: MediaQuery.of(context).size.height * 0.85,
child: Column(
children: [
Container(
margin: const EdgeInsets.all(8),
height: 8,
width: 60,
decoration: const BoxDecoration(color: MyColors.lightTextColor, borderRadius: BorderRadius.all(Radius.circular(20))),
),
12.height,
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
"Select Category".toText(fontSize: 24, isBold: true),
],
),
30.height,
Expanded(
child: ListView(
children: [
Builder(
builder: (context) {
List<DropValue> serviceCategories = [];
for (var element in appointmentsVM.branchCategories) {
if (!element.isSelected!) {
serviceCategories.add(DropValue(element.id?.toInt() ?? 0, element.categoryName ?? "", ""));
}
}
return DropdownField(
(DropValue value) => appointmentsVM.updateProviderCategoryId(SelectionModel(selectedId: value.id, selectedOption: value.value, itemPrice: value.subValue)),
list: serviceCategories,
hint: "Select Category",
dropdownValue: appointmentsVM.providerCategoryId.selectedId != -1
? DropValue(appointmentsVM.providerCategoryId.selectedId, appointmentsVM.providerCategoryId.selectedOption, "")
: null,
);
},
),
if (appointmentsVM.isFetchingServices) ...[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [const CircularProgressIndicator().paddingAll(10)],
),
] else if (appointmentsVM.branchServices.isNotEmpty) ...[
8.height,
Builder(
builder: (context) {
List<DropValue> serviceCategories = [];
for (var element in appointmentsVM.branchServices) {
if (!element.isSelected!) {
serviceCategories.add(DropValue(element.providerBranchServiceID ?? 0, element.providerBranchServiceID.toString(), ""));
}
}
return DropdownField(
(DropValue value) => appointmentsVM.updateProviderServiceId(SelectionModel(selectedId: value.id, selectedOption: value.value, itemPrice: value.subValue)),
list: serviceCategories,
hint: "Select Services",
dropdownValue: appointmentsVM.branchServiceId.selectedId != -1
? DropValue(appointmentsVM.branchServiceId.selectedId, appointmentsVM.branchServiceId.selectedOption, "")
: null,
);
},
),
],
if (appointmentsVM.branchServiceId.selectedId != -1 && !appointmentsVM.isFetchingServices) ...[
16.height,
Row(
children: [
"Select Service Location".toText(
fontSize: 16,
isBold: true,
color: MyColors.black,
),
],
),
8.height,
Row(
children: [
Expanded(
child: ShowFillButton(
isFilled: appointmentsVM.isHomeTapped,
maxHeight: 48,
title: "Home",
txtColor: appointmentsVM.isHomeTapped ? MyColors.white : MyColors.darkTextColor,
onPressed: () => appointmentsVM.updateIsHomeTapped(true),
),
),
12.width,
Expanded(
child: ShowFillButton(
isFilled: !appointmentsVM.isHomeTapped,
txtColor: !appointmentsVM.isHomeTapped ? MyColors.white : MyColors.darkTextColor,
maxHeight: 48,
title: "Workshop",
onPressed: () => appointmentsVM.updateIsHomeTapped(false),
),
),
],
),
if (appointmentsVM.isHomeTapped) ...[
8.height,
TxtField(
hint: 'Pick Home Location',
errorValue: appointmentsVM.pickHomeLocationError,
value: appointmentsVM.pickedHomeLocation,
isNeedClickAll: true,
postfixData: Icons.location_on,
postFixDataColor: MyColors.darkTextColor,
onTap: () {
//TODO: open the place picked to pick the location and save it in provider.
appointmentsVM.updatePickedHomeLocation("PM58+F97, Al Olaya, Riyadh 12333");
},
),
14.height,
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Icon(
Icons.warning,
color: MyColors.adPendingStatusColor,
size: 19,
).paddingOnly(bottom: 2),
3.width,
"Some services are not available on home location.".toText(
color: MyColors.adPendingStatusColor,
fontSize: 12,
isItalic: true,
),
],
),
]
],
],
),
),
SizedBox(
width: double.infinity,
child: Column(
children: [
if (appointmentsVM.isHomeTapped && !appointmentsVM.isFetchingServices) ...[
const Divider(thickness: 1, height: 1),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
// TODO: This Price will be decided according to the service selected
150.toString().toText(fontSize: 30, isBold: true),
"SAR".toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5),
],
),
"These charges are additional to the actual service charges. For heavy items the charges may vary.".toText(fontSize: 12, color: MyColors.lightTextColor),
22.height,
],
SizedBox(
width: double.infinity,
child: ShowFillButton(
maxHeight: 55,
backgroundColor: !appointmentsVM.isServiceSelectionValidated() ? MyColors.lightTextColor.withOpacity(0.6) : MyColors.primaryColor,
title: "Next",
onPressed: () {
bool isValidated = appointmentsVM.isServiceSelectionValidated();
if (isValidated) {
navigateWithName(context, AppRoutes.bookAppointmentsItemView);
}
},
),
),
],
),
).paddingOnly(bottom: 20)
],
)).horPaddingMain();
}
}

@ -0,0 +1,51 @@
import 'package:flutter/material.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
class ServiceItemWithPriceCheckBox extends StatelessWidget {
final bool isSelected;
final String title, description;
final Widget priceWidget;
final Function(bool) onSelection;
const ServiceItemWithPriceCheckBox({
required this.isSelected,
required this.title,
required this.description,
this.priceWidget = const SizedBox(),
required this.onSelection,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return SizedBox(
width: double.infinity,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Checkbox(
value: isSelected,
onChanged: (bool? v) {
onSelection(v ?? false);
},
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
title.toText(fontSize: 16, isBold: true).paddingOnly(top: 10),
description.toText(fontSize: 12, color: MyColors.lightTextColor),
priceWidget,
],
),
),
],
),
);
}
}

@ -67,7 +67,7 @@ class _DashboardPageState extends State<DashboardPage> {
String getPageTitle(int index) {
if (index == 0) {
return "Branchs";
return "Branches";
}
if (index == 1) {
return "Appointments";

@ -103,7 +103,9 @@ class AdsFragment extends StatelessWidget {
),
floatingActionButton: FloatingActionButton(
onPressed: () {
context.read<AdVM>().getVehicleTypes();
if (context.read<AdVM>().vehicleTypes.isEmpty) {
context.read<AdVM>().getVehicleTypes();
}
navigateWithName(context, AppRoutes.createAdView);
},

@ -41,7 +41,11 @@ class AppointmentsFragment extends StatelessWidget {
itemCount: appointmentsVM.myAppointments.length,
itemBuilder: (BuildContext context, int index) {
return BuildAppointmentContainerForCustomer(
onTapped: () => navigateWithName(context, AppRoutes.appointmentDetailView, arguments: appointmentsVM.myAppointments[index]),
onTapped: () => navigateWithName(
context,
AppRoutes.appointmentDetailView,
arguments: appointmentsVM.myAppointments[index],
),
appointmentListModel: appointmentsVM.myAppointments[index],
);
},

Loading…
Cancel
Save