fist commit ftc staff app clone
This commit is contained in:
165
lib/view/custom_widgets/auth/custom_forget_password_dialog.dart
Normal file
165
lib/view/custom_widgets/auth/custom_forget_password_dialog.dart
Normal file
@@ -0,0 +1,165 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ftc_mobile_app/ftc_mobile_app.dart';
|
||||
import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class CustomForgetPasswordDialog extends StatelessWidget {
|
||||
const CustomForgetPasswordDialog({
|
||||
Key? key,
|
||||
required this.dialogMessageText,
|
||||
required this.headingText,
|
||||
required this.dialogMessageTextBold,
|
||||
required this.dialogButtonAcceptText,
|
||||
required this.dialogButtonCloseText,
|
||||
// this.acceptFunction,
|
||||
}) : super(key: key);
|
||||
|
||||
final String headingText;
|
||||
final String dialogMessageText;
|
||||
final String dialogMessageTextBold;
|
||||
final String dialogButtonAcceptText;
|
||||
|
||||
// final Function? acceptFunction;
|
||||
final String dialogButtonCloseText;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
TextEditingController emailController = TextEditingController();
|
||||
RxString emailErrorMsg = "".obs;
|
||||
|
||||
bool validateEmail() {
|
||||
if (emailController.text.isEmpty) {
|
||||
emailErrorMsg.value = ConstantText.kEmailIsRequired;
|
||||
} else if (!GetUtils.isEmail(emailController.text)) {
|
||||
emailErrorMsg.value = ConstantText.kInvalidEmail;
|
||||
} else {
|
||||
emailErrorMsg.value = "";
|
||||
}
|
||||
return emailErrorMsg.isEmpty;
|
||||
}
|
||||
|
||||
return AlertDialog(
|
||||
surfaceTintColor: CustomAppColors.kPrimaryColor,
|
||||
shape: LinearBorder.top(side: BorderSide.none),
|
||||
backgroundColor: CustomAppColors.kWhiteColor,
|
||||
shadowColor: CustomAppColors.kWhiteColor,
|
||||
titlePadding: EdgeInsets.only(top: 20.h),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CustomTextWidget(
|
||||
text: headingText,
|
||||
isExpanded: false,
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.all(10.sp),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CustomTextWidget(
|
||||
text: dialogMessageText,
|
||||
isExpanded: false,
|
||||
fontSize: 10.sp,
|
||||
fontWeight: FontWeight.w400,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(
|
||||
height: 3.h,
|
||||
),
|
||||
dialogMessageTextBold != ""
|
||||
? CustomTextWidget(
|
||||
text: dialogMessageTextBold,
|
||||
isExpanded: false,
|
||||
fontSize: 10.sp,
|
||||
fontWeight: FontWeight.bold,
|
||||
textAlign: TextAlign.left,
|
||||
)
|
||||
: Container(),
|
||||
],
|
||||
),
|
||||
),
|
||||
// SizedBox(height: 10.h,),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 5.0.h),
|
||||
child: CustomTextFieldWidget(
|
||||
controller: emailController,
|
||||
hintText: ConstantText.kPleaseInputEmail,
|
||||
heading: ConstantText.kEmailHeading,
|
||||
onChange: (_) {
|
||||
validateEmail();
|
||||
},
|
||||
),
|
||||
),
|
||||
Obx(() {
|
||||
return CustomErrorMsg(
|
||||
message: emailErrorMsg.value,
|
||||
);
|
||||
}),
|
||||
16.verticalSpace,
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: 32.h,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: Size(double.infinity, 30.h),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
backgroundColor: CustomAppColors.kSecondaryColor),
|
||||
onPressed: () async {
|
||||
if (validateEmail()) {
|
||||
Navigator.of(context).pop();
|
||||
var response = await AuthService()
|
||||
.forgetPassword(email: emailController.text)
|
||||
.showLoader();
|
||||
if (response == true) {
|
||||
FrequentFunctions.showToast(
|
||||
message:
|
||||
"A password reset link has been sent to your registered email.",
|
||||
);
|
||||
}
|
||||
// Add functionality for the "Agree" button.
|
||||
}
|
||||
},
|
||||
child: CustomTextWidget(
|
||||
text: dialogButtonAcceptText,
|
||||
fontColor: CustomAppColors.kPrimaryColor,
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w400),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 10.h),
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: 32.h,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: Size(double.infinity, 30.h),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
backgroundColor: CustomAppColors.kLightGreyColor),
|
||||
onPressed: () {
|
||||
// Add functionality for the "pop" button.
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: CustomTextWidget(
|
||||
text: dialogButtonCloseText,
|
||||
fontColor: CustomAppColors.kPrimaryColor,
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w400),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
1
lib/view/custom_widgets/auth/export_auth_widgets.dart
Normal file
1
lib/view/custom_widgets/auth/export_auth_widgets.dart
Normal file
@@ -0,0 +1 @@
|
||||
export 'custom_forget_password_dialog.dart';
|
||||
46
lib/view/custom_widgets/clients/CareNoteOptionCard.dart
Normal file
46
lib/view/custom_widgets/clients/CareNoteOptionCard.dart
Normal file
@@ -0,0 +1,46 @@
|
||||
import 'package:ftc_mobile_app/ftc_mobile_app.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CareNoteOptionCard extends StatelessWidget {
|
||||
final String icon;
|
||||
final String name;
|
||||
|
||||
const CareNoteOptionCard({
|
||||
super.key,
|
||||
required this.icon,
|
||||
required this.name,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
elevation: 2,
|
||||
shadowColor: CustomAppColors.kLightGreyColor,
|
||||
surfaceTintColor: Colors.white,
|
||||
color: Colors.white,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4).r),
|
||||
child: Padding(
|
||||
padding: REdgeInsets.all(8),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
CustomImageWidget(
|
||||
imagePath: icon,
|
||||
height: 24.r,
|
||||
width: 24.r,
|
||||
),
|
||||
12.verticalSpace,
|
||||
CustomTextWidget(
|
||||
text: name,
|
||||
alignment: Alignment.center,
|
||||
isExpanded: false,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontColor: CustomAppColors.kDarkBlueTextColor,
|
||||
fontSize: 14.sp)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
import 'package:ftc_mobile_app/controllers/clients/careNoteFormControllers/category_subcategory_widget_controller.dart';
|
||||
import 'package:ftc_mobile_app/ftc_mobile_app.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ftc_mobile_app/models/clients/body_points_category.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class CategorySubcategoryDropdownsWidget extends StatelessWidget {
|
||||
final CategorySubcategoryWidgetController controller;
|
||||
|
||||
const CategorySubcategoryDropdownsWidget(
|
||||
{super.key, required this.controller});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_bodyPartsDropdown,
|
||||
_subcategoryDropdown,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget get _bodyPartsDropdown {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10.r),
|
||||
border: Border.all(
|
||||
color: CustomAppColors.kLightGreyColor,
|
||||
width: 1.sp,
|
||||
),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: 5.h,
|
||||
horizontal: 15.w,
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CustomTextWidget(
|
||||
text: "Select Category",
|
||||
fontSize: 10.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontColor: CustomAppColors.kLightTextColor,
|
||||
alignment: Alignment.centerLeft,
|
||||
),
|
||||
Obx(() {
|
||||
return DropdownButtonHideUnderline(
|
||||
child: DropdownButtonFormField<BodyPointsCategory>(
|
||||
onTap: () {
|
||||
FocusScopeNode().unfocus();
|
||||
},
|
||||
dropdownColor: Colors.white,
|
||||
decoration: const InputDecoration(
|
||||
border: InputBorder.none,
|
||||
),
|
||||
hint: Text(
|
||||
"Select...",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14.sp,
|
||||
color: CustomAppColors.kLightTextColor,
|
||||
),
|
||||
),
|
||||
items: controller.bodyPointsCategoryList
|
||||
.map(
|
||||
(e) => DropdownMenuItem<BodyPointsCategory>(
|
||||
value: e,
|
||||
child: Text(e.name),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
isExpanded: true,
|
||||
iconSize: 20.h,
|
||||
icon: Padding(
|
||||
padding: REdgeInsets.only(right: 4.0),
|
||||
child: const Icon(Icons.arrow_drop_down_sharp,
|
||||
color: Colors.grey),
|
||||
),
|
||||
onChanged: (category) {
|
||||
controller.selectedBodyPart.value = category;
|
||||
|
||||
if ((category?.subCategory ?? []).isEmpty) {
|
||||
controller.selectedSubcategory.value = null;
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget get _subcategoryDropdown {
|
||||
return Obx(() {
|
||||
return (controller.selectedBodyPart.value?.subCategory ?? []).isEmpty
|
||||
? const SizedBox.shrink()
|
||||
: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10.r),
|
||||
border: Border.all(
|
||||
color: CustomAppColors.kLightGreyColor,
|
||||
width: 1.sp,
|
||||
),
|
||||
),
|
||||
margin: REdgeInsets.only(top: 20.r),
|
||||
padding: EdgeInsets.symmetric(vertical: 5.h, horizontal: 15.w),
|
||||
alignment: Alignment.center,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CustomTextWidget(
|
||||
text: "Select Subcategory",
|
||||
fontSize: 10.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontColor: CustomAppColors.kLightTextColor,
|
||||
alignment: Alignment.centerLeft,
|
||||
),
|
||||
DropdownButtonHideUnderline(
|
||||
child: DropdownButtonFormField<SubCat>(
|
||||
onTap: () {
|
||||
FocusScopeNode().unfocus();
|
||||
},
|
||||
dropdownColor: Colors.white,
|
||||
decoration: const InputDecoration(
|
||||
border: InputBorder.none,
|
||||
),
|
||||
hint: Text(
|
||||
"Select...",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14.sp,
|
||||
color: CustomAppColors.kLightTextColor,
|
||||
),
|
||||
),
|
||||
items: controller.selectedBodyPart.value!.subCategory
|
||||
.map(
|
||||
(e) => DropdownMenuItem<SubCat>(
|
||||
value: e,
|
||||
child: Text(e.name),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
isExpanded: true,
|
||||
iconSize: 20.h,
|
||||
icon: Padding(
|
||||
padding: REdgeInsets.only(right: 4.0),
|
||||
child: const Icon(Icons.arrow_drop_down_sharp,
|
||||
color: Colors.grey),
|
||||
),
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
controller.selectedSubcategory.value = value;
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
56
lib/view/custom_widgets/clients/custom_icon_tile.dart
Normal file
56
lib/view/custom_widgets/clients/custom_icon_tile.dart
Normal file
@@ -0,0 +1,56 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ftc_mobile_app/ftc_mobile_app.dart';
|
||||
|
||||
class CustomIconTile extends StatelessWidget {
|
||||
const CustomIconTile({
|
||||
super.key,
|
||||
required this.iconPath,
|
||||
required this.text,
|
||||
required this.route,
|
||||
this.arguments,
|
||||
});
|
||||
|
||||
final String iconPath;
|
||||
final String text;
|
||||
final String route;
|
||||
final Object? arguments;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
if (route.isNotEmpty) {
|
||||
Navigator.pushNamed(context, route, arguments: arguments);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
padding: REdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(2.r),
|
||||
border: Border.all(color: CustomAppColors.kSecondaryColor)),
|
||||
child: Row(
|
||||
children: [
|
||||
CustomImageWidget(
|
||||
imagePath: iconPath,
|
||||
height: 16.r,
|
||||
width: 16.r,
|
||||
),
|
||||
8.horizontalSpace,
|
||||
Expanded(
|
||||
child: CustomTextWidget(
|
||||
text: text,
|
||||
alignment: Alignment.centerLeft,
|
||||
isExpanded: false,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontColor: CustomAppColors.kDarkBlueTextColor,
|
||||
fontSize: 14.sp),
|
||||
),
|
||||
Icon(Icons.arrow_forward_ios_rounded,
|
||||
size: 12.r, color: CustomAppColors.kSecondaryColor)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
//
|
||||
//
|
||||
// import 'package:flutter/material.dart';
|
||||
// import 'package:ftc_mobile_app/ftc_mobile_app.dart';
|
||||
// import 'package:ftc_mobile_app/models/clients/service_users_model.dart';
|
||||
// import 'package:get/get.dart';
|
||||
// import '../../../models/clients/documents_list_model.dart';
|
||||
//
|
||||
// class ShowDocumentsOptionDialog extends StatelessWidget {
|
||||
// final DocumentModel documentModel;
|
||||
//
|
||||
// const ShowDocumentsOptionDialog({
|
||||
// super.key,required this.documentModel,
|
||||
// });
|
||||
//
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// final DocumentsListScreenController controller = Get.find();
|
||||
// return AlertDialog(
|
||||
// surfaceTintColor: CustomAppColors.kPrimaryColor,
|
||||
// shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(2.r)),
|
||||
// backgroundColor: CustomAppColors.kPrimaryColor,
|
||||
// title: Center(
|
||||
// child: CustomTextWidget(
|
||||
// text: 'Please Select what you wanna do',
|
||||
// fontWeight: FontWeight.bold,
|
||||
// isExpanded: false,
|
||||
// alignment: Alignment.center,
|
||||
// fontSize: 16.sp,
|
||||
// ),
|
||||
// ),
|
||||
// content: Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
// children: [
|
||||
// InkWell(
|
||||
// onTap: () async{
|
||||
// var response = await Navigator.pushNamed(controller.screenKey.currentContext!,
|
||||
// CustomRouteNames.kAddNewDocumentScreenRoute,
|
||||
// arguments: [documentModel,controller.serviceUser.value]);
|
||||
// if(response is List){
|
||||
// int index = controller.documentsList.value.documentList.indexWhere((item) => item == documentModel);
|
||||
// controller.documentsList.value.documentList[index].title = response[0];
|
||||
// controller.documentsList.value.documentList[index].details = response[1];
|
||||
// controller.documentsList.refresh();
|
||||
// }
|
||||
// },
|
||||
// child: Container(
|
||||
// padding: EdgeInsets.all(1.sp),
|
||||
// decoration: BoxDecoration(
|
||||
// color: CustomAppColors.kYellowColor.withOpacity(0.5),
|
||||
// borderRadius: BorderRadius.circular(50.r),
|
||||
// ),
|
||||
// child: const Icon(Icons.edit,color: CustomAppColors.kDarkYellowColor,size: 40,)),
|
||||
// ),
|
||||
// SizedBox(width: 3.w,),
|
||||
// InkWell(
|
||||
// onTap: () async {
|
||||
// dynamic response = await ClientService().deleteDocumentService(docId: documentModel.id);
|
||||
// if (response is bool) {
|
||||
// controller.removeDocumentItem(documentModel);
|
||||
// Navigator.pop(controller.screenKey.currentState!.context);
|
||||
// }
|
||||
// },
|
||||
// child: Container(
|
||||
// padding: EdgeInsets.all(1.sp),
|
||||
// decoration: BoxDecoration(
|
||||
// color: CustomAppColors.kRedColor.withOpacity(0.5),
|
||||
// borderRadius: BorderRadius.circular(50.r),
|
||||
// ),
|
||||
// child: const Icon(Icons.delete,color: CustomAppColors.kRedColor,size: 40,)),
|
||||
// ),
|
||||
// SizedBox(width: 3.w,),
|
||||
// InkWell(
|
||||
// onTap: () async{
|
||||
// dynamic response = await Navigator.pushNamed(
|
||||
// controller.screenKey.currentContext!,
|
||||
// CustomRouteNames.kAddNewDocumentScreenRoute,
|
||||
// arguments: controller.serviceUser.value
|
||||
// );
|
||||
// if(response is DocumentModel){
|
||||
// controller.documentsList.value.documentList.insert(0,response);
|
||||
// controller.documentsList.refresh();
|
||||
// }
|
||||
// },
|
||||
// child: Container(
|
||||
// padding: EdgeInsets.all(1.sp),
|
||||
// decoration: BoxDecoration(
|
||||
// color: CustomAppColors.kSecondaryColor.withOpacity(0.5),
|
||||
// borderRadius: BorderRadius.circular(50.r),
|
||||
// ),
|
||||
// child: const Icon(Icons.add,color: CustomAppColors.kSecondaryColor,size: 40,)),
|
||||
// ),
|
||||
// SizedBox(width: 3.w,),
|
||||
// InkWell(
|
||||
// onTap: () {
|
||||
// Navigator.pushNamed(
|
||||
// controller.screenKey.currentContext!,
|
||||
// CustomRouteNames.kAddNewDocumentScreenRoute,
|
||||
// arguments: [documentModel,true]
|
||||
// );
|
||||
// },
|
||||
// child: Container(
|
||||
// padding: EdgeInsets.all(1.sp),
|
||||
// decoration: BoxDecoration(
|
||||
// color: CustomAppColors.kGreenColor.withOpacity(0.5),
|
||||
// borderRadius: BorderRadius.circular(50.r),
|
||||
// ),
|
||||
// child: const Icon(Icons.visibility,color: CustomAppColors.kGreenColor,size: 40,)),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// actions: [
|
||||
// Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// mainAxisAlignment: MainAxisAlignment.start,
|
||||
// children: [
|
||||
// Visibility(
|
||||
// visible: false,
|
||||
// child: ElevatedButton(
|
||||
// style: ElevatedButton.styleFrom(
|
||||
// minimumSize: Size(double.infinity, 30.h),
|
||||
// shape: RoundedRectangleBorder(
|
||||
// borderRadius: BorderRadius.circular(5.r),
|
||||
// ),
|
||||
// backgroundColor: CustomAppColors.kSecondaryColor
|
||||
// ),
|
||||
//
|
||||
// onPressed: () {
|
||||
// // Add functionality for the "Cancel Shift" button.
|
||||
// Navigator.of(context).pop();
|
||||
// },
|
||||
// child: const CustomTextWidget(text: 'Cancel Shift',fontColor: CustomAppColors.kPrimaryColor),
|
||||
// ),
|
||||
// ),
|
||||
// ElevatedButton(
|
||||
// style: ElevatedButton.styleFrom(
|
||||
// minimumSize: Size(double.infinity, 30.h),
|
||||
// shape: RoundedRectangleBorder(
|
||||
// borderRadius: BorderRadius.circular(5.r),
|
||||
// ),
|
||||
// backgroundColor: CustomAppColors.kPrimaryColor
|
||||
// ),
|
||||
//
|
||||
// onPressed: () {
|
||||
// // Add functionality for the "Close" button.
|
||||
// Navigator.of(context).pop();
|
||||
// },
|
||||
// child: CustomTextWidget(text: 'Close',fontColor: CustomAppColors.kBlackColor,fontSize: 15.sp),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
29
lib/view/custom_widgets/common_cancel_button.dart
Normal file
29
lib/view/custom_widgets/common_cancel_button.dart
Normal file
@@ -0,0 +1,29 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ftc_mobile_app/ftc_mobile_app.dart';
|
||||
|
||||
class CommonCloseTextButton extends StatelessWidget {
|
||||
final VoidCallback? onTap;
|
||||
|
||||
const CommonCloseTextButton({super.key, this.onTap});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: onTap ?? () => Navigator.of(context).pop(),
|
||||
child: Container(
|
||||
height: 30.h,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: CustomAppColors.kLightGreyColor),
|
||||
borderRadius: BorderRadius.circular(5.r),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: CustomTextWidget(
|
||||
text: 'Close',
|
||||
fontColor: CustomAppColors.kBlackColor,
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
143
lib/view/custom_widgets/custom_app_bar.dart
Normal file
143
lib/view/custom_widgets/custom_app_bar.dart
Normal file
@@ -0,0 +1,143 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:ftc_mobile_app/ftc_mobile_app.dart';
|
||||
|
||||
class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
final VoidCallback? onBackButtonPressed;
|
||||
final Widget? action;
|
||||
final Widget? leadingButton;
|
||||
final Widget? titleWidget;
|
||||
final String titleText;
|
||||
final bool showBoxShadow;
|
||||
|
||||
const CustomAppBar({
|
||||
Key? key,
|
||||
this.onBackButtonPressed,
|
||||
this.showBoxShadow = true,
|
||||
this.leadingButton,
|
||||
this.action,
|
||||
this.titleWidget,
|
||||
this.titleText = "",
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ModalRoute<dynamic>? parentRoute = ModalRoute.of(context);
|
||||
final bool canPop = parentRoute?.canPop ?? false;
|
||||
return PreferredSize(
|
||||
preferredSize: preferredSize,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: CustomAppColors.kPrimaryColor,
|
||||
boxShadow: showBoxShadow
|
||||
? [
|
||||
BoxShadow(
|
||||
color: CustomAppColors.kLightGreyColor,
|
||||
spreadRadius: 1.2,
|
||||
blurRadius: 4,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
]
|
||||
: null,
|
||||
),
|
||||
width: MediaQuery.of(context).size.width,
|
||||
alignment: Alignment.center,
|
||||
padding: EdgeInsets.only(
|
||||
top: GetPlatform.isIOS ? 40.0 : 20.0,
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
padding: EdgeInsets.only(top: 4.sp, left: 20.0.sp, right: 20.sp),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Builder(builder: (context) {
|
||||
if (titleWidget != null) {
|
||||
return titleWidget!;
|
||||
}
|
||||
return FittedBox(
|
||||
child: CustomTextWidget(
|
||||
text: titleText,
|
||||
fontColor: CustomAppColors.kIconColor,
|
||||
fontSize: 22.0.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Visibility(
|
||||
visible: canPop || leadingButton != null,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 10.0.sp,
|
||||
top: 32.0.sp,
|
||||
),
|
||||
child: leadingButton != null
|
||||
? leadingButton!
|
||||
: GestureDetector(
|
||||
onTap: () {
|
||||
if (onBackButtonPressed != null) {
|
||||
onBackButtonPressed!();
|
||||
} else {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: SizedBox(
|
||||
width: 30.0,
|
||||
height: 30.0,
|
||||
child: Container(
|
||||
padding: Platform.isIOS
|
||||
? const EdgeInsets.symmetric(
|
||||
horizontal: 10.0,
|
||||
vertical: 5.0,
|
||||
)
|
||||
: null,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(4.sp),
|
||||
color: CustomAppColors.kLightGreyColor,
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Icon(
|
||||
Icons.adaptive.arrow_back,
|
||||
color: CustomAppColors.kIconColor,
|
||||
size: 20.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
Visibility(
|
||||
visible: action != null,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
right: 15.0,
|
||||
top: 25.0.sp,
|
||||
),
|
||||
child: action,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Size get preferredSize => const Size.fromHeight(56.0);
|
||||
}
|
||||
33
lib/view/custom_widgets/custom_app_bar_title_only.dart
Normal file
33
lib/view/custom_widgets/custom_app_bar_title_only.dart
Normal file
@@ -0,0 +1,33 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ftc_mobile_app/ftc_mobile_app.dart';
|
||||
|
||||
class CustomAppBarTitleOnly extends AppBar {
|
||||
CustomAppBarTitleOnly(BuildContext context,
|
||||
{super.key, required String titleText})
|
||||
: super(
|
||||
toolbarHeight: 56.r,
|
||||
leading: IconButton(
|
||||
icon: CustomImageWidget(
|
||||
imagePath: AssetsManager.kBackIcon,
|
||||
height: 11.53.h,
|
||||
width: 8.66.w,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
centerTitle: false,
|
||||
titleSpacing: 0,
|
||||
leadingWidth: 50.r,
|
||||
surfaceTintColor: Colors.white,
|
||||
title: CustomTextWidget(
|
||||
text: titleText,
|
||||
isExpanded: false,
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w700,
|
||||
maxLines: 1,
|
||||
textOverflow: TextOverflow.ellipsis,
|
||||
fontColor: CustomAppColors.kDarkBlueTextColor,
|
||||
),
|
||||
);
|
||||
}
|
||||
187
lib/view/custom_widgets/custom_app_bar_with_action.dart
Normal file
187
lib/view/custom_widgets/custom_app_bar_with_action.dart
Normal file
@@ -0,0 +1,187 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ftc_mobile_app/ftc_mobile_app.dart';
|
||||
|
||||
class CustomAppBarWithAction extends AppBar {
|
||||
CustomAppBarWithAction(
|
||||
BuildContext context, {
|
||||
super.key,
|
||||
required String titleText,
|
||||
required String actionText,
|
||||
Color? actionTextColor,
|
||||
required Function() onActionTap,
|
||||
}) : super(
|
||||
toolbarHeight: 56.r,
|
||||
leading: IconButton(
|
||||
icon: CustomImageWidget(
|
||||
imagePath: AssetsManager.kBackIcon,
|
||||
height: 11.53.h,
|
||||
width: 8.66.w,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
centerTitle: false,
|
||||
titleSpacing: 0,
|
||||
leadingWidth: 50.r,
|
||||
surfaceTintColor: Colors.white,
|
||||
title: CustomTextWidget(
|
||||
text: titleText,
|
||||
isExpanded: false,
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w700,
|
||||
maxLines: 1,
|
||||
textOverflow: TextOverflow.ellipsis,
|
||||
fontColor: CustomAppColors.kDarkBlueTextColor,
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: onActionTap,
|
||||
child: Text(
|
||||
actionText,
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: actionTextColor ?? CustomAppColors.kLightTextColor,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
// final VoidCallback? onBackButtonPressed;
|
||||
// final String titleText;
|
||||
// final bool showBoxShadow;
|
||||
//
|
||||
// const CustomAppBar({
|
||||
// Key? key,
|
||||
// this.onBackButtonPressed,
|
||||
// this.showBoxShadow = true,
|
||||
// this.titleText = "",
|
||||
// }) : super(key: key);
|
||||
//
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// final ModalRoute<dynamic>? parentRoute = ModalRoute.of(context);
|
||||
// final bool canPop = parentRoute?.canPop ?? false;
|
||||
// return PreferredSize(
|
||||
// preferredSize: preferredSize,
|
||||
// child: Container(
|
||||
// decoration: BoxDecoration(
|
||||
// color: CustomAppColors.kPrimaryColor,
|
||||
// boxShadow: showBoxShadow
|
||||
// ? [
|
||||
// BoxShadow(
|
||||
// color: CustomAppColors.kLightGreyColor,
|
||||
// spreadRadius: 1.2,
|
||||
// blurRadius: 4,
|
||||
// offset: const Offset(0, 2),
|
||||
// ),
|
||||
// ]
|
||||
// : null,
|
||||
// ),
|
||||
// width: MediaQuery.of(context).size.width,
|
||||
// alignment: Alignment.center,
|
||||
// padding: EdgeInsets.only(
|
||||
// top: GetPlatform.isIOS ? 40.0 : 20.0,
|
||||
// ),
|
||||
// child: Stack(
|
||||
// children: [
|
||||
// Container(
|
||||
// width: MediaQuery.of(context).size.width,
|
||||
// padding: EdgeInsets.only(top: 4.sp, left: 20.0.sp, right: 20.sp),
|
||||
// child: Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.center,
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
// children: [
|
||||
// Builder(builder: (context) {
|
||||
// CustomTextWidget(
|
||||
// text: titleText,
|
||||
// isExpanded: false,
|
||||
// fontSize: 16.sp,
|
||||
// fontWeight: FontWeight.w700,
|
||||
// fontColor: CustomAppColors.kDarkBlueTextColor,
|
||||
// );
|
||||
// return FittedBox(
|
||||
// child: CustomTextWidget(
|
||||
// text: titleText,
|
||||
// fontColor: CustomAppColors.kIconColor,
|
||||
// fontSize: 22.0.sp,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// ),
|
||||
// );
|
||||
// }),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
// crossAxisAlignment: CrossAxisAlignment.center,
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// Visibility(
|
||||
// visible: canPop || leadingButton != null,
|
||||
// child: Padding(
|
||||
// padding: EdgeInsets.only(
|
||||
// left: 10.0.sp,
|
||||
// top: 32.0.sp,
|
||||
// ),
|
||||
// child: leadingButton != null
|
||||
// ? leadingButton!
|
||||
// : GestureDetector(
|
||||
// onTap: () {
|
||||
// if (onBackButtonPressed != null) {
|
||||
// onBackButtonPressed!();
|
||||
// } else {
|
||||
// Navigator.pop(context);
|
||||
// }
|
||||
// },
|
||||
// child: SizedBox(
|
||||
// width: 30.0,
|
||||
// height: 30.0,
|
||||
// child: Container(
|
||||
// padding: Platform.isIOS
|
||||
// ? const EdgeInsets.symmetric(
|
||||
// horizontal: 10.0,
|
||||
// vertical: 5.0,
|
||||
// )
|
||||
// : null,
|
||||
// decoration: BoxDecoration(
|
||||
// borderRadius: BorderRadius.circular(4.sp),
|
||||
// color: CustomAppColors.kLightGreyColor,
|
||||
// ),
|
||||
// alignment: Alignment.center,
|
||||
// child: Icon(
|
||||
// Icons.adaptive.arrow_back,
|
||||
// color: CustomAppColors.kIconColor,
|
||||
// size: 20.0,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// const Spacer(),
|
||||
// Visibility(
|
||||
// visible: action != null,
|
||||
// child: Padding(
|
||||
// padding: EdgeInsets.only(
|
||||
// right: 15.0,
|
||||
// top: 25.0.sp,
|
||||
// ),
|
||||
// child: action,
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// @override
|
||||
// Size get preferredSize => const Size.fromHeight(56.0);
|
||||
// }
|
||||
76
lib/view/custom_widgets/custom_app_button.dart
Normal file
76
lib/view/custom_widgets/custom_app_button.dart
Normal file
@@ -0,0 +1,76 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ftc_mobile_app/ftc_mobile_app.dart';
|
||||
|
||||
class CustomAppButton extends StatelessWidget {
|
||||
final String buttonText;
|
||||
final VoidCallback? onTap;
|
||||
final Color? buttonColor;
|
||||
final Color? textColor;
|
||||
final Color? borderColor;
|
||||
final bool isLoading;
|
||||
|
||||
const CustomAppButton({
|
||||
super.key,
|
||||
this.buttonColor,
|
||||
this.textColor,
|
||||
required this.buttonText,
|
||||
this.onTap,
|
||||
this.isLoading = false,
|
||||
this.borderColor,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
if(isLoading) {
|
||||
return;
|
||||
}
|
||||
FocusScope.of(context).unfocus();
|
||||
if(onTap!=null) {
|
||||
onTap!();
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
height: 48.h,
|
||||
decoration: BoxDecoration(
|
||||
color: buttonColor ?? CustomAppColors.kSecondaryColor,
|
||||
borderRadius: BorderRadius.circular(3.r),
|
||||
border: Border.all(
|
||||
color: borderColor ?? CustomAppColors.kSecondaryColor,
|
||||
),
|
||||
),
|
||||
// padding: EdgeInsets.symmetric(vertical: 15.h,),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
CustomTextWidget(
|
||||
text: buttonText,
|
||||
fontWeight: FontWeight.w600,
|
||||
isExpanded: false,
|
||||
fontSize: 16.sp,
|
||||
fontColor: textColor ?? CustomAppColors.kPrimaryColor,
|
||||
),
|
||||
|
||||
Visibility(
|
||||
visible: isLoading,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 8.0.w),
|
||||
child: SizedBox(
|
||||
height: 15.h,
|
||||
width: 15.w,
|
||||
child: CircularProgressIndicator(
|
||||
color: textColor ?? CustomAppColors.kPrimaryColor,
|
||||
strokeWidth: 3.0.w,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
51
lib/view/custom_widgets/custom_check_box.dart
Normal file
51
lib/view/custom_widgets/custom_check_box.dart
Normal file
@@ -0,0 +1,51 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ftc_mobile_app/ftc_mobile_app.dart';
|
||||
|
||||
class CustomCheckBox extends StatelessWidget {
|
||||
final bool checkBoxValue;
|
||||
final String titleText;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
const CustomCheckBox({
|
||||
super.key,
|
||||
required this.checkBoxValue,
|
||||
required this.titleText,
|
||||
this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 25.h,
|
||||
width: 25.w,
|
||||
child: Checkbox(
|
||||
value: checkBoxValue,
|
||||
activeColor: CustomAppColors.kSecondaryColor,
|
||||
onChanged: (_) {
|
||||
if(onTap!=null) {
|
||||
onTap!();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 8.0.w),
|
||||
child: GestureDetector(
|
||||
onTap: onTap,
|
||||
child: CustomTextWidget(
|
||||
text: titleText,
|
||||
isExpanded: false,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 12.sp,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
26
lib/view/custom_widgets/custom_error_msg.dart
Normal file
26
lib/view/custom_widgets/custom_error_msg.dart
Normal file
@@ -0,0 +1,26 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ftc_mobile_app/ftc_mobile_app.dart';
|
||||
|
||||
class CustomErrorMsg extends StatelessWidget {
|
||||
final String message;
|
||||
final Alignment? alignment;
|
||||
|
||||
const CustomErrorMsg({
|
||||
super.key,
|
||||
required this.message,
|
||||
this.alignment,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Visibility(
|
||||
visible: message.isNotEmpty,
|
||||
child: CustomTextWidget(
|
||||
text: message,
|
||||
fontColor: CustomAppColors.kRedColor,
|
||||
fontWeight: FontWeight.w500,
|
||||
alignment: alignment ?? Alignment.centerRight,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
104
lib/view/custom_widgets/custom_image_widgets.dart
Normal file
104
lib/view/custom_widgets/custom_image_widgets.dart
Normal file
@@ -0,0 +1,104 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:ftc_mobile_app/ftc_mobile_app.dart';
|
||||
|
||||
class CustomImageWidget extends StatelessWidget {
|
||||
final String imagePath;
|
||||
final BoxFit? fit;
|
||||
final double? width;
|
||||
final double? height;
|
||||
final Color? imageColor;
|
||||
|
||||
const CustomImageWidget({
|
||||
Key? key,
|
||||
this.imagePath = "",
|
||||
this.fit,
|
||||
this.width,
|
||||
this.height,
|
||||
this.imageColor,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (imagePath.startsWith("http")) {
|
||||
// handel network image here.
|
||||
return Image.network(
|
||||
imagePath,
|
||||
fit: fit,
|
||||
height: height,
|
||||
width: width,
|
||||
color: imageColor,
|
||||
errorBuilder: (ctx, obj, stc) {
|
||||
return _ErrorWidget(
|
||||
size: height ?? 50.0,
|
||||
color: imageColor,
|
||||
);
|
||||
},
|
||||
);
|
||||
} else if (imagePath.trim().isEmpty) {
|
||||
return _ErrorWidget(
|
||||
size: height ?? 50.0,
|
||||
color: imageColor,
|
||||
);
|
||||
} else if (imagePath.startsWith("assets/") && imagePath.endsWith(".png")) {
|
||||
return Image.asset(
|
||||
imagePath,
|
||||
fit: fit,
|
||||
height: height,
|
||||
width: width,
|
||||
color: imageColor,
|
||||
errorBuilder: (ctx, obj, stc) {
|
||||
return _ErrorWidget(
|
||||
size: height ?? 50.0,
|
||||
color: imageColor,
|
||||
);
|
||||
},
|
||||
);
|
||||
} else if(imagePath.startsWith("assets/") && imagePath.endsWith(".svg")) {
|
||||
return SvgPicture.asset(
|
||||
imagePath,
|
||||
height: height,
|
||||
width: width,
|
||||
colorFilter: imageColor!=null? ColorFilter.mode(imageColor!, BlendMode.srcIn):null,
|
||||
// color: imageColor,
|
||||
);
|
||||
}
|
||||
return Image.file(
|
||||
File(imagePath),
|
||||
fit: fit,
|
||||
height: height,
|
||||
width: width,
|
||||
errorBuilder: (ctx, obj, stc) {
|
||||
return _ErrorWidget(
|
||||
size: height ?? 50.0,
|
||||
color: imageColor,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ErrorWidget extends StatelessWidget {
|
||||
final double size;
|
||||
final Color? color;
|
||||
|
||||
const _ErrorWidget({
|
||||
Key? key,
|
||||
this.size = 50.0,
|
||||
this.color,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Align(
|
||||
alignment: Alignment.center,
|
||||
child: Icon(
|
||||
Icons.error_outline,
|
||||
size: size,
|
||||
color: color ?? CustomAppColors.kIconColor,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
268
lib/view/custom_widgets/custom_navigation_drawer.dart
Normal file
268
lib/view/custom_widgets/custom_navigation_drawer.dart
Normal file
@@ -0,0 +1,268 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ftc_mobile_app/ftc_mobile_app.dart';
|
||||
import 'package:ftc_mobile_app/view/screens/webview/webview_screen.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'my_circle_image.dart';
|
||||
|
||||
class CustomDrawer extends StatefulWidget {
|
||||
const CustomDrawer({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<CustomDrawer> createState() => _CustomDrawerState();
|
||||
}
|
||||
|
||||
class _CustomDrawerState extends State<CustomDrawer> {
|
||||
DashboardScreenController dashboardController =
|
||||
Get.put(DashboardScreenController());
|
||||
CustomNavigationDrawerController drawerController =
|
||||
Get.put(CustomNavigationDrawerController());
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Drawer(
|
||||
elevation: 5,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(1.r)),
|
||||
backgroundColor: CustomAppColors.kPrimaryColor,
|
||||
surfaceTintColor: CustomAppColors.kPrimaryColor,
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
40.verticalSpace,
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Center(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
drawerController.selectedIndex.value = -1;
|
||||
Navigator.pop(context);
|
||||
Navigator.pushNamed(
|
||||
context, CustomRouteNames.kViewProfileScreenRoute);
|
||||
},
|
||||
child: Obx(() {
|
||||
return MyCircleImage(
|
||||
imageSize: 80.r,
|
||||
url:
|
||||
"${WebUrls.baseUrl}${DashboardScreenController.instance.myProfileData()?.user?.profilePictureUrl ?? ""}",
|
||||
errorWidget: CustomImageWidget(
|
||||
imagePath: AssetsManager.kPersonMainIcon,
|
||||
imageColor: CustomAppColors.kDarkBlueTextColor,
|
||||
height: 80.r,
|
||||
width: 80.r,
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
10.verticalSpace,
|
||||
Obx(
|
||||
() => CustomTextWidget(
|
||||
text: DashboardScreenController.instance
|
||||
.myProfileData()
|
||||
?.staffMemberName ??
|
||||
"",
|
||||
textAlign: TextAlign.center,
|
||||
fontColor: CustomAppColors.kBlackColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14.sp,
|
||||
),
|
||||
),
|
||||
Obx(
|
||||
() => CustomTextWidget(
|
||||
text: DashboardScreenController.instance
|
||||
.myProfileData()
|
||||
?.staffDesignation ??
|
||||
"",
|
||||
fontColor: CustomAppColors.kLightGreyColor,
|
||||
fontSize: 10.sp,
|
||||
fontWeight: FontWeight.w500),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.only(top: 20.h, bottom: 20.h),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
_getDrawerNavItem(
|
||||
title: "Home",
|
||||
iconPath: AssetsManager.kHomeIcon,
|
||||
color: CustomAppColors.kLightGreyColor,
|
||||
selected: drawerController.selectedIndex.value == 1,
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
dashboardController.selectedIndex.value = 1;
|
||||
drawerController.selectedIndex.value = 1;
|
||||
// Navigator.pushNamed(
|
||||
// context,
|
||||
// CustomRouteNames.kDashboardScreenRoute,
|
||||
// );
|
||||
}),
|
||||
6.verticalSpace,
|
||||
_getDrawerNavItem(
|
||||
title: "Rota",
|
||||
iconPath: AssetsManager.kPersonMainIcon,
|
||||
color: CustomAppColors.kLightGreyColor,
|
||||
selected: drawerController.selectedIndex.value == 0,
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
// dashboardController.selectedIndex.value = 0;
|
||||
// drawerController.selectedIndex.value = 0;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
CustomRouteNames.kRotaDashboardScreenRoute,
|
||||
);
|
||||
}),
|
||||
6.verticalSpace,
|
||||
_getDrawerNavItem(
|
||||
title: "Clients",
|
||||
iconPath: AssetsManager.kPeopleIcon,
|
||||
color: CustomAppColors.kLightGreyColor,
|
||||
selected: drawerController.selectedIndex.value == 3,
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
dashboardController.selectedIndex.value = 3;
|
||||
drawerController.selectedIndex.value = 3;
|
||||
// Navigator.pushNamed(
|
||||
// context,
|
||||
// CustomRouteNames.kClientsListScreenRoute,
|
||||
// );
|
||||
}),
|
||||
6.verticalSpace,
|
||||
_getDrawerNavItem(
|
||||
title: "Inbox",
|
||||
iconPath: AssetsManager.kMessageIcon,
|
||||
color: CustomAppColors.kLightGreyColor,
|
||||
selected: drawerController.selectedIndex.value == 2,
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
dashboardController.selectedIndex.value = 2;
|
||||
drawerController.selectedIndex.value = 2;
|
||||
// Navigator.pushNamed(
|
||||
// context,
|
||||
// CustomRouteNames.kInboxScreenRoute,
|
||||
// );
|
||||
}),
|
||||
6.verticalSpace,
|
||||
_getDrawerNavItem(
|
||||
title: "Policies and Procedures",
|
||||
iconPath: AssetsManager.kPoliciesIcon,
|
||||
color: CustomAppColors.kLightGreyColor,
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
CustomRouteNames.kWebviewScreen,
|
||||
arguments: WebviewScreenArgument(
|
||||
title: 'Policies and Procedures',
|
||||
url: ConstantText.privacyUrl),
|
||||
);
|
||||
}),
|
||||
6.verticalSpace,
|
||||
_getDrawerNavItem(
|
||||
title: "Settings",
|
||||
iconPath: AssetsManager.kSettingsIcon,
|
||||
color: CustomAppColors.kLightGreyColor,
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
//Todo: uncomment when start working
|
||||
// Navigator.pushNamed(
|
||||
// context,
|
||||
// CustomRouteNames.kSettingsScreen,
|
||||
// );
|
||||
}),
|
||||
6.verticalSpace,
|
||||
_getDrawerNavItem(
|
||||
title: "Notifications",
|
||||
iconPath: AssetsManager.kBellIcon,
|
||||
color: CustomAppColors.kLightGreyColor,
|
||||
selected: drawerController.selectedIndex.value == 6,
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
//Todo: uncomment
|
||||
// Navigator.pushNamed(
|
||||
// context,
|
||||
// CustomRouteNames.kNotificationListScreenRoute,
|
||||
// );
|
||||
}),
|
||||
const Expanded(child: FrequentFunctions.noWidget),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
CustomTextWidget(
|
||||
text: "Shift end at ",
|
||||
fontColor: CustomAppColors.kLightGreyColor,
|
||||
isExpanded: false,
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
CustomTextWidget(
|
||||
text: "07Hrs 50Sec",
|
||||
fontColor: CustomAppColors.kSecondaryColor,
|
||||
isExpanded: false,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 16.sp,
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _getDrawerNavItem(
|
||||
{required String title,
|
||||
required String iconPath,
|
||||
Color color = CustomAppColors.kSecondaryColor,
|
||||
bool selected = false,
|
||||
required Function() onTap}) {
|
||||
return Container(
|
||||
color: selected
|
||||
? CustomAppColors.kSecondaryColor
|
||||
: CustomAppColors.kPrimaryColor,
|
||||
padding: REdgeInsets.symmetric(horizontal: 26, vertical: 12),
|
||||
child: GestureDetector(
|
||||
onTap: onTap,
|
||||
// onTap: ()=> controller.onTap(title),
|
||||
child: Row(
|
||||
children: [
|
||||
// Icon(
|
||||
// icon,
|
||||
// size: 27,
|
||||
// color: selected ? CustomAppColors.kWhiteColor : color,
|
||||
// ),
|
||||
CustomImageWidget(
|
||||
imagePath: iconPath,
|
||||
width: title == "Clients" ? 20.w : 22.w,
|
||||
height: title == "Clients" ? 20.h : 26.h,
|
||||
// height: 26.h,
|
||||
imageColor: selected ? CustomAppColors.kWhiteColor : color,
|
||||
),
|
||||
10.horizontalSpace,
|
||||
Expanded(
|
||||
child: CustomTextWidget(
|
||||
text: title,
|
||||
textAlign: TextAlign.left,
|
||||
fontColor: selected
|
||||
? CustomAppColors.kWhiteColor
|
||||
: CustomAppColors.kIconColor,
|
||||
fontSize: 18.sp,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
59
lib/view/custom_widgets/custom_radio_button.dart
Normal file
59
lib/view/custom_widgets/custom_radio_button.dart
Normal file
@@ -0,0 +1,59 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:ftc_mobile_app/utilities/custom_app_colors.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'custom_text_widget.dart';
|
||||
|
||||
class RadioButton extends StatelessWidget {
|
||||
final String value;
|
||||
final Rx<String?> selectedOption;
|
||||
final bool isEnabled;
|
||||
|
||||
const RadioButton({
|
||||
super.key,
|
||||
required this.value,
|
||||
required this.selectedOption,
|
||||
this.isEnabled = true,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
selectedOption(value);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Ink(
|
||||
width: 32.r,
|
||||
height: 32.r,
|
||||
child: Obx(() {
|
||||
return Radio(
|
||||
value: value,
|
||||
groupValue: selectedOption(),
|
||||
onChanged: selectedOption,
|
||||
fillColor: MaterialStateProperty.resolveWith((states) {
|
||||
if (!isEnabled) return CustomAppColors.kLightGreyColor;
|
||||
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
return CustomAppColors.kSecondaryColor;
|
||||
}
|
||||
return Colors.black;
|
||||
}),
|
||||
);
|
||||
}),
|
||||
),
|
||||
CustomTextWidget(
|
||||
text: value,
|
||||
isExpanded: false,
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w400,
|
||||
fontColor:
|
||||
isEnabled ? Colors.black : CustomAppColors.kLightGreyColor,
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
166
lib/view/custom_widgets/custom_scaffold.dart
Normal file
166
lib/view/custom_widgets/custom_scaffold.dart
Normal file
@@ -0,0 +1,166 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:ftc_mobile_app/ftc_mobile_app.dart';
|
||||
|
||||
class CustomScaffold extends StatelessWidget {
|
||||
final VoidCallback? onBackButton;
|
||||
final VoidCallback? onScreenTap;
|
||||
final String? titleText;
|
||||
final bool avoidBottomInsets;
|
||||
final bool? showShadow;
|
||||
final bool showAppBar;
|
||||
final bool enableLayoutBuilder;
|
||||
final GlobalKey<ScaffoldState> screenKey;
|
||||
final PreferredSizeWidget? appBar;
|
||||
final Widget? body;
|
||||
final Widget? sideDrawer;
|
||||
final Widget? customTabViewWidget;
|
||||
final Widget? bottomMenu;
|
||||
final Widget? floatingActionButton;
|
||||
final Color? backgroundColor;
|
||||
|
||||
const CustomScaffold({
|
||||
Key? key,
|
||||
this.onBackButton,
|
||||
this.customTabViewWidget,
|
||||
this.titleText,
|
||||
this.appBar,
|
||||
this.body,
|
||||
this.showShadow,
|
||||
this.bottomMenu,
|
||||
this.floatingActionButton,
|
||||
this.onScreenTap,
|
||||
this.avoidBottomInsets = true,
|
||||
this.showAppBar = false,
|
||||
this.enableLayoutBuilder = true,
|
||||
required this.screenKey,
|
||||
this.backgroundColor,
|
||||
this.sideDrawer,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ModalRoute<dynamic>? parentRoute = ModalRoute.of(context);
|
||||
final bool canPop = parentRoute?.canPop ?? false;
|
||||
return GestureDetector(
|
||||
onHorizontalDragEnd: GetPlatform.isIOS
|
||||
? (details) {
|
||||
if (details.velocity.pixelsPerSecond.dx > 0) {
|
||||
if (onBackButton != null) {
|
||||
onBackButton!();
|
||||
} else {
|
||||
if (canPop) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
: null,
|
||||
onTap: () {
|
||||
if (onScreenTap != null) onScreenTap!();
|
||||
},
|
||||
child: WillPopScope(
|
||||
onWillPop: () async {
|
||||
if (onBackButton != null) {
|
||||
onBackButton!();
|
||||
} else {
|
||||
if (canPop) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraint) {
|
||||
if (constraint.maxWidth > 600 && enableLayoutBuilder) {
|
||||
return Scaffold(
|
||||
drawer: sideDrawer,
|
||||
key: screenKey,
|
||||
resizeToAvoidBottomInset: avoidBottomInsets,
|
||||
drawerEnableOpenDragGesture: false,
|
||||
endDrawerEnableOpenDragGesture: false,
|
||||
backgroundColor: backgroundColor ?? CustomAppColors.kPrimaryColor,
|
||||
body: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const Spacer(),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: customTabViewWidget ??
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(
|
||||
2.0.sp,
|
||||
),
|
||||
color: backgroundColor ??
|
||||
CustomAppColors.kPrimaryColor,
|
||||
),
|
||||
padding: EdgeInsets.all(
|
||||
10.0.sp,
|
||||
),
|
||||
child: body,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
],
|
||||
),
|
||||
appBar: showAppBar
|
||||
? appBar ??
|
||||
CustomAppBar(
|
||||
titleText: titleText ?? "titleText",
|
||||
onBackButtonPressed: () {
|
||||
if (onBackButton != null) {
|
||||
onBackButton!();
|
||||
return;
|
||||
}
|
||||
if (canPop) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
)
|
||||
: null,
|
||||
floatingActionButton: floatingActionButton,
|
||||
);
|
||||
}
|
||||
return Scaffold(
|
||||
drawer: sideDrawer,
|
||||
resizeToAvoidBottomInset: avoidBottomInsets,
|
||||
key: screenKey,
|
||||
floatingActionButton: floatingActionButton,
|
||||
appBar: showAppBar
|
||||
? appBar ??
|
||||
CustomAppBar(
|
||||
showBoxShadow: showShadow ?? false,
|
||||
titleText: titleText ?? "titleText",
|
||||
onBackButtonPressed: () {
|
||||
if (onBackButton != null) {
|
||||
onBackButton!();
|
||||
return;
|
||||
}
|
||||
if (canPop) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
)
|
||||
: null,
|
||||
body: body ?? Container(),
|
||||
backgroundColor:
|
||||
backgroundColor ?? CustomAppColors.kPrimaryColor,
|
||||
bottomNavigationBar: bottomMenu,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
130
lib/view/custom_widgets/custom_text_field_widget.dart
Normal file
130
lib/view/custom_widgets/custom_text_field_widget.dart
Normal file
@@ -0,0 +1,130 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:ftc_mobile_app/ftc_mobile_app.dart';
|
||||
|
||||
class CustomTextFieldWidget extends StatelessWidget {
|
||||
final TextEditingController? controller;
|
||||
final int? minLines;
|
||||
final int? maxLines;
|
||||
final int? maxLength;
|
||||
final String hintText;
|
||||
final String heading;
|
||||
final ValueChanged<String>? onChange;
|
||||
final bool isEnabled;
|
||||
final bool isObscure;
|
||||
final BorderRadius? borderRadius;
|
||||
final Color? borderColor;
|
||||
final double? borderWidth;
|
||||
final Widget? bottomChild;
|
||||
final TextInputType? inputType;
|
||||
final TextCapitalization? textCapitalization;
|
||||
final List<TextInputFormatter>? inputFormatters;
|
||||
|
||||
const CustomTextFieldWidget({
|
||||
super.key,
|
||||
this.controller,
|
||||
required this.heading,
|
||||
this.minLines = 1,
|
||||
this.maxLines = 1,
|
||||
this.maxLength,
|
||||
this.hintText = "",
|
||||
this.onChange,
|
||||
this.isEnabled = true,
|
||||
this.isObscure = false,
|
||||
this.borderRadius,
|
||||
this.borderColor,
|
||||
this.borderWidth = 1.0,
|
||||
this.bottomChild,
|
||||
this.textCapitalization,
|
||||
this.inputType,
|
||||
this.inputFormatters,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: borderRadius,
|
||||
border: Border.all(
|
||||
color: borderColor ?? CustomAppColors.kSmokeColor,
|
||||
width: borderWidth ?? 1.5.sp,
|
||||
),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: 10.h,
|
||||
horizontal: 15.w,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// CustomTextWidget(
|
||||
// text: heading,
|
||||
// fontSize: 10.sp,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// fontColor: CustomAppColors.kLightTextColor,
|
||||
// alignment: Alignment.centerLeft,
|
||||
// textAlign: TextAlign.left,
|
||||
// ),
|
||||
Text(
|
||||
heading,
|
||||
textAlign: TextAlign.left,
|
||||
style: TextStyle(
|
||||
fontSize: 10.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: CustomAppColors.kLightTextColor,
|
||||
),
|
||||
),
|
||||
4.verticalSpace,
|
||||
TextField(
|
||||
controller: controller,
|
||||
enabled: isEnabled,
|
||||
obscureText: isObscure,
|
||||
minLines: minLines,
|
||||
maxLines: maxLines,
|
||||
maxLength: maxLength,
|
||||
inputFormatters: inputFormatters,
|
||||
onChanged: (_) {
|
||||
if (onChange != null) {
|
||||
onChange!(_);
|
||||
}
|
||||
},
|
||||
keyboardType: inputType,
|
||||
textCapitalization: textCapitalization ?? TextCapitalization.none,
|
||||
cursorColor: CustomAppColors.kSecondaryColor,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14.sp,
|
||||
color: CustomAppColors.kIconColor,
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
hintText: hintText,
|
||||
hintStyle: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14.sp,
|
||||
color: CustomAppColors.kLightTextColor,
|
||||
),
|
||||
counterText: "",
|
||||
contentPadding: EdgeInsets.zero,
|
||||
border: const OutlineInputBorder(
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
disabledBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (bottomChild != null) bottomChild!,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
53
lib/view/custom_widgets/custom_text_widget.dart
Normal file
53
lib/view/custom_widgets/custom_text_widget.dart
Normal file
@@ -0,0 +1,53 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ftc_mobile_app/ftc_mobile_app.dart';
|
||||
|
||||
class CustomTextWidget extends StatelessWidget {
|
||||
final String text;
|
||||
final double? fontSize;
|
||||
final bool isExpanded;
|
||||
final FontWeight? fontWeight;
|
||||
final Color? fontColor;
|
||||
final String? fontFamily;
|
||||
final Alignment? alignment;
|
||||
final TextOverflow? textOverflow;
|
||||
final TextAlign textAlign;
|
||||
final TextDecoration textDecoration;
|
||||
final int? maxLines;
|
||||
|
||||
const CustomTextWidget({
|
||||
Key? key,
|
||||
required this.text,
|
||||
this.fontSize,
|
||||
this.fontWeight,
|
||||
this.fontColor,
|
||||
this.alignment,
|
||||
this.isExpanded = true,
|
||||
this.fontFamily,
|
||||
this.textOverflow,
|
||||
this.textAlign = TextAlign.center,
|
||||
this.textDecoration = TextDecoration.none,
|
||||
this.maxLines,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
alignment: alignment,
|
||||
width: isExpanded ? MediaQuery.of(context).size.width : null,
|
||||
child: Text(
|
||||
text,
|
||||
textAlign: textAlign,
|
||||
maxLines: maxLines,
|
||||
style: TextStyle(
|
||||
fontSize: fontSize,
|
||||
fontWeight: fontWeight,
|
||||
color: fontColor ?? CustomAppColors.kIconColor,
|
||||
fontFamily: fontFamily,
|
||||
decoration: textDecoration,
|
||||
decorationColor: fontColor ?? CustomAppColors.kIconColor,
|
||||
),
|
||||
overflow: textOverflow,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
31
lib/view/custom_widgets/edit_icon.dart
Normal file
31
lib/view/custom_widgets/edit_icon.dart
Normal file
@@ -0,0 +1,31 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:ftc_mobile_app/utilities/custom_app_colors.dart';
|
||||
|
||||
class EditIcon extends StatelessWidget {
|
||||
final VoidCallback onTap;
|
||||
|
||||
const EditIcon({super.key, required this.onTap});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
width: 24.r,
|
||||
height: 24.r,
|
||||
// padding: REdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: CustomAppColors.kYellowColor.withOpacity(0.5),
|
||||
borderRadius: BorderRadius.circular(50.r),
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(
|
||||
Icons.edit,
|
||||
color: CustomAppColors.kDarkYellowColor,
|
||||
size: 16.r,
|
||||
),
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
16
lib/view/custom_widgets/export_custom_widgets.dart
Normal file
16
lib/view/custom_widgets/export_custom_widgets.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
export 'custom_text_widget.dart';
|
||||
export 'custom_app_bar.dart';
|
||||
export 'custom_scaffold.dart';
|
||||
export 'custom_image_widgets.dart';
|
||||
export 'custom_text_field_widget.dart';
|
||||
export 'custom_error_msg.dart';
|
||||
export 'custom_check_box.dart';
|
||||
export 'custom_app_button.dart';
|
||||
export 'rota/export_rota.dart';
|
||||
export 'home/export_home_widgets.dart';
|
||||
export 'notifications/export_notification_widgets.dart';
|
||||
export 'custom_navigation_drawer.dart';
|
||||
export 'auth/export_auth_widgets.dart';
|
||||
export 'custom_radio_button.dart';
|
||||
export 'loading_widget.dart';
|
||||
export 'custom_app_bar_title_only.dart';
|
||||
84
lib/view/custom_widgets/home/custom_message_dialog.dart
Normal file
84
lib/view/custom_widgets/home/custom_message_dialog.dart
Normal file
@@ -0,0 +1,84 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ftc_mobile_app/ftc_mobile_app.dart';
|
||||
|
||||
class CustomMessageDialog extends StatelessWidget {
|
||||
const CustomMessageDialog({Key? key, required this.dialogMessageText, required this.headingText, required this.dialogMessageTextBold, required this.dialogButtonText}) : super(key: key);
|
||||
|
||||
final String headingText;
|
||||
final String dialogMessageText;
|
||||
final String dialogMessageTextBold;
|
||||
final String dialogButtonText;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
surfaceTintColor: CustomAppColors.kPrimaryColor,
|
||||
insetPadding: REdgeInsets.all(18),
|
||||
contentPadding: REdgeInsets.all(15),
|
||||
shape: LinearBorder.top(side: BorderSide.none),
|
||||
backgroundColor: CustomAppColors.kWhiteColor,
|
||||
shadowColor: CustomAppColors.kWhiteColor,
|
||||
// titlePadding: EdgeInsets.only(top: 20.h),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CustomTextWidget(
|
||||
text: headingText,
|
||||
isExpanded: false,
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
8.verticalSpace,
|
||||
Container(
|
||||
padding: EdgeInsets.all(10.sp),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: CustomAppColors.kLightGreyColor)),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CustomTextWidget(
|
||||
text: dialogMessageText,
|
||||
isExpanded: false,
|
||||
fontSize: 10.sp,
|
||||
fontWeight: FontWeight.w400,
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
12.verticalSpace,
|
||||
dialogMessageTextBold != ""
|
||||
? CustomTextWidget(
|
||||
text: dialogMessageTextBold,
|
||||
isExpanded: false,
|
||||
fontSize: 10.sp,
|
||||
fontWeight: FontWeight.bold,
|
||||
textAlign: TextAlign.left,
|
||||
)
|
||||
: Container(),
|
||||
],
|
||||
),
|
||||
),
|
||||
12.verticalSpace,
|
||||
SizedBox(
|
||||
height: 32.h,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: Size(double.infinity, 30.h),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
backgroundColor: CustomAppColors.kSecondaryColor),
|
||||
onPressed: () {
|
||||
// Add functionality for the "Agree" button.
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: CustomTextWidget(
|
||||
text: dialogButtonText,
|
||||
fontColor: CustomAppColors.kPrimaryColor,
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w400),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ftc_mobile_app/ftc_mobile_app.dart';
|
||||
|
||||
class PrivacyPolicyDialog extends StatelessWidget {
|
||||
const PrivacyPolicyDialog({Key? key, required this.privacyPolicy, required this.checkBoxOnChange}) : super(key: key);
|
||||
|
||||
final String privacyPolicy;
|
||||
final ValueChanged<bool>? checkBoxOnChange;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
surfaceTintColor: CustomAppColors.kPrimaryColor,
|
||||
insetPadding: EdgeInsets.only(right: 18.w,left: 18.w, top:40.h ,bottom: MediaQuery.of(context).size.height/3.h),
|
||||
contentPadding: EdgeInsets.only(left: 15.w,right: 15.w,top: 11.h,bottom: 0.h),
|
||||
shape: LinearBorder.top(side: BorderSide.none),
|
||||
backgroundColor: CustomAppColors.kWhiteColor,
|
||||
shadowColor: CustomAppColors.kWhiteColor,
|
||||
titlePadding: EdgeInsets.only(top: 20.h),
|
||||
title: CustomTextWidget(
|
||||
text: "New Policy",
|
||||
isExpanded: false,
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
content: Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: EdgeInsets.all(10.sp),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: CustomAppColors.kLightGreyColor)),
|
||||
child: CustomTextWidget(
|
||||
text: privacyPolicy,
|
||||
isExpanded: false,
|
||||
fontSize: 10.sp,
|
||||
fontWeight: FontWeight.w400,
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
SizedBox(
|
||||
height: 30.h,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Checkbox(
|
||||
activeColor: CustomAppColors.kSecondaryColor,
|
||||
value: true,
|
||||
onChanged: (value) {
|
||||
if (checkBoxOnChange != null && value!= null) {
|
||||
checkBoxOnChange!(value);
|
||||
}
|
||||
}),
|
||||
CustomTextWidget(
|
||||
text: "Agree to the new policy",
|
||||
isExpanded: false,
|
||||
fontSize: 10.sp,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
// padding: EdgeInsets.only(bottom: 50.h),
|
||||
// height: 25.h,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: Size(double.infinity, 30.h),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
backgroundColor: CustomAppColors.kSecondaryColor),
|
||||
onPressed: () {
|
||||
// Add functionality for the "Agree" button.
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: CustomTextWidget(
|
||||
text: 'Agree',
|
||||
fontColor: CustomAppColors.kPrimaryColor,
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w400),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
2
lib/view/custom_widgets/home/export_home_widgets.dart
Normal file
2
lib/view/custom_widgets/home/export_home_widgets.dart
Normal file
@@ -0,0 +1,2 @@
|
||||
export 'custom_privacy_policy_dialog.dart';
|
||||
export 'custom_message_dialog.dart';
|
||||
117
lib/view/custom_widgets/human_body_mapper_widget.dart
Normal file
117
lib/view/custom_widgets/human_body_mapper_widget.dart
Normal file
@@ -0,0 +1,117 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ftc_mobile_app/ftc_mobile_app.dart';
|
||||
import '../../utilities/enums/body_parts.dart';
|
||||
|
||||
class HumanBodyWidget extends StatefulWidget {
|
||||
final double width;
|
||||
final Map<BodyPart, Color?> visibleBodyPoints;
|
||||
final Function(BodyPart, Offset position)? onPointTap;
|
||||
|
||||
const HumanBodyWidget({
|
||||
super.key,
|
||||
required this.visibleBodyPoints,
|
||||
required this.width,
|
||||
this.onPointTap,
|
||||
});
|
||||
|
||||
@override
|
||||
State<HumanBodyWidget> createState() => _HumanBodyWidgetState();
|
||||
}
|
||||
|
||||
class _HumanBodyWidgetState extends State<HumanBodyWidget> {
|
||||
final GlobalKey _imageKey = GlobalKey();
|
||||
|
||||
//Note: change the size if changing [AssetsManager.pngHumanBodyFrontBack] image
|
||||
final _actualImageSize = const Size(500, 681);
|
||||
|
||||
late double height;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
height = widget.width / _actualImageSize.aspectRatio;
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
Size? imageSize = _imageKey.currentContext?.size;
|
||||
// Now you can use imageSize.width and imageSize.height
|
||||
|
||||
print("image size: $imageSize");
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return UnconstrainedBox(
|
||||
child: SizedBox(
|
||||
width: widget.width,
|
||||
height: height,
|
||||
child: LayoutBuilder(builder: (_, constrains) {
|
||||
return Stack(
|
||||
fit: StackFit.loose,
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Image.asset(
|
||||
AssetsManager.pngHumanBodyFrontBack,
|
||||
key: _imageKey,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
...widget.visibleBodyPoints.entries
|
||||
.map((e) => _pointWidget(
|
||||
bodyPart: e.key,
|
||||
color: e.value,
|
||||
constrains: constrains,
|
||||
onPointTap: widget.onPointTap,
|
||||
))
|
||||
.toList()
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Positioned _pointWidget({
|
||||
required BodyPart bodyPart,
|
||||
required BoxConstraints constrains,
|
||||
Color? color,
|
||||
Function(BodyPart, Offset position)? onPointTap,
|
||||
}) {
|
||||
return Positioned(
|
||||
top: constrains.maxHeight * (bodyPart.topPercent / 100.0),
|
||||
left: constrains.maxWidth * (bodyPart.leftPercent / 100.0),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
// RenderBox? button = context.findRenderObject() as RenderBox?;
|
||||
// RenderBox? overlay =
|
||||
// Overlay.of(context).context.findRenderObject() as RenderBox?;
|
||||
// Rect? buttonRect = (button == null)
|
||||
// ? null
|
||||
// : Rect.fromPoints(
|
||||
// button.localToGlobal(Offset.zero, ancestor: overlay),
|
||||
// button.localToGlobal(button.size.bottomRight(Offset.zero),
|
||||
// ancestor: overlay),
|
||||
// );
|
||||
// double screenHeight = MediaQuery.of(context).size.height;
|
||||
// double popupHeight = screenHeight - (buttonRect?.bottom ?? 0.5.sh);
|
||||
},
|
||||
onTapUp: (d) {
|
||||
if (onPointTap != null) {
|
||||
onPointTap(bodyPart, d.globalPosition);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
width: 12.r,
|
||||
height: 12.r,
|
||||
decoration: BoxDecoration(
|
||||
color: color ?? CustomAppColors.kRedColor,
|
||||
border: Border.all(color: CustomAppColors.kWhiteColor, width: 2),
|
||||
borderRadius: BorderRadius.circular(6.r),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
57
lib/view/custom_widgets/label_value_box_widget.dart
Normal file
57
lib/view/custom_widgets/label_value_box_widget.dart
Normal file
@@ -0,0 +1,57 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ftc_mobile_app/ftc_mobile_app.dart';
|
||||
|
||||
class LabelValueBoxWidget extends StatelessWidget {
|
||||
const LabelValueBoxWidget({
|
||||
super.key,
|
||||
required this.label,
|
||||
required this.value,
|
||||
this.trailing,
|
||||
this.borderColor,
|
||||
});
|
||||
|
||||
final String label;
|
||||
final String value;
|
||||
final Color? borderColor;
|
||||
final Widget? trailing;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.all(10.h),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: borderColor ?? Colors.grey),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
CustomTextWidget(
|
||||
text: label,
|
||||
fontColor: CustomAppColors.kLightGreyColor,
|
||||
fontWeight: FontWeight.w500,
|
||||
isExpanded: false,
|
||||
textAlign: TextAlign.left,
|
||||
fontSize: 10.sp,
|
||||
),
|
||||
6.verticalSpace,
|
||||
CustomTextWidget(
|
||||
text: value,
|
||||
fontColor: CustomAppColors.kBlackColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14.0.sp,
|
||||
textAlign: TextAlign.left,
|
||||
isExpanded: false,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
trailing ?? const SizedBox.shrink()
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
48
lib/view/custom_widgets/loading_widget.dart
Normal file
48
lib/view/custom_widgets/loading_widget.dart
Normal file
@@ -0,0 +1,48 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ftc_mobile_app/ftc_mobile_app.dart';
|
||||
|
||||
class LoadingWidget extends StatelessWidget {
|
||||
const LoadingWidget({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: 150.w,
|
||||
height: 170.h,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white, borderRadius: BorderRadius.circular(8.r)),
|
||||
child: LoaderColumn(
|
||||
loadingText: "Loading",
|
||||
textColor: Colors.black,
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
class LoaderColumn extends Column {
|
||||
LoaderColumn({Key? key, String? loadingText, Color? color, Color? textColor})
|
||||
: super(
|
||||
key: key,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
CircularProgressIndicator(color: color),
|
||||
(loadingText == null || loadingText.isEmpty)
|
||||
? const SizedBox.shrink()
|
||||
: Padding(
|
||||
padding: REdgeInsets.only(top: 16.0),
|
||||
child: CustomTextWidget(
|
||||
text: loadingText!,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
237
lib/view/custom_widgets/multiline_text_field_sheet.dart
Normal file
237
lib/view/custom_widgets/multiline_text_field_sheet.dart
Normal file
@@ -0,0 +1,237 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class MultilineTextFieldSheet {
|
||||
final String appBarTitle;
|
||||
final String textFieldHint;
|
||||
final String buttonLabel;
|
||||
final bool wantLeadingIcon;
|
||||
final String prevValueForField;
|
||||
final int minLines;
|
||||
final int maxLines;
|
||||
|
||||
// final ContentSheetAction action;
|
||||
final Function(String text) onButtonClick;
|
||||
|
||||
MultilineTextFieldSheet({
|
||||
required this.buttonLabel,
|
||||
required this.appBarTitle,
|
||||
required this.textFieldHint,
|
||||
this.prevValueForField = "",
|
||||
this.wantLeadingIcon = true,
|
||||
required this.onButtonClick,
|
||||
this.minLines = 5,
|
||||
this.maxLines = 5,
|
||||
});
|
||||
|
||||
show() {
|
||||
Get.bottomSheet(
|
||||
_ContentWidget(
|
||||
key: const ValueKey(1),
|
||||
appBarTitle: appBarTitle,
|
||||
buttonLabel: buttonLabel,
|
||||
onButtonClick: onButtonClick,
|
||||
textFieldHint: textFieldHint,
|
||||
prevValueForField: prevValueForField,
|
||||
// action: action,
|
||||
wantLeadingIcon: wantLeadingIcon,
|
||||
minLines: minLines,
|
||||
maxLines: maxLines,
|
||||
),
|
||||
clipBehavior: Clip.none,
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 4,
|
||||
isScrollControlled: true,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(32)).r,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ContentWidget extends StatefulWidget {
|
||||
final String appBarTitle;
|
||||
final String textFieldHint;
|
||||
final String buttonLabel;
|
||||
final String prevValueForField;
|
||||
final bool wantLeadingIcon;
|
||||
final Function onButtonClick;
|
||||
final int minLines;
|
||||
final int maxLines;
|
||||
|
||||
const _ContentWidget({
|
||||
super.key,
|
||||
required this.appBarTitle,
|
||||
required this.buttonLabel,
|
||||
required this.textFieldHint,
|
||||
this.prevValueForField = "",
|
||||
required this.onButtonClick,
|
||||
required this.wantLeadingIcon,
|
||||
this.minLines = 5,
|
||||
this.maxLines = 5,
|
||||
});
|
||||
|
||||
@override
|
||||
_ContentWidgetState createState() => _ContentWidgetState();
|
||||
}
|
||||
|
||||
class _ContentWidgetState extends State<_ContentWidget> {
|
||||
final ContentController _controller = ContentController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_controller.initData(widget.prevValueForField, "");
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
color: Colors.white,
|
||||
padding: MediaQuery.of(context).viewPadding,
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
children: [
|
||||
bottomSheetAppBar(context),
|
||||
24.verticalSpace,
|
||||
_textField().addPaddingHorizontal(24),
|
||||
32.verticalSpace,
|
||||
_addButton(context).addPaddingHorizontal(24),
|
||||
24.verticalSpace,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
AppBar bottomSheetAppBar(BuildContext context) {
|
||||
return AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
title: Text(
|
||||
widget.appBarTitle,
|
||||
style: const TextStyle(color: Colors.black),
|
||||
),
|
||||
centerTitle: false,
|
||||
titleSpacing: 24,
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
actions: [
|
||||
InkWell(
|
||||
child: const Icon(
|
||||
Icons.close,
|
||||
color: Colors.black,
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
}),
|
||||
16.horizontalSpace,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _textField() {
|
||||
return DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: (8).toRadius(),
|
||||
border: Border.all(color: Colors.grey, width: 1)),
|
||||
child: Padding(
|
||||
padding: REdgeInsets.symmetric(horizontal: 16.0, vertical: 12),
|
||||
child: TextField(
|
||||
controller: _controller.textFieldTEC,
|
||||
textCapitalization: TextCapitalization.sentences,
|
||||
textInputAction: TextInputAction.next,
|
||||
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w400),
|
||||
onChanged: (text) {
|
||||
_controller.isButtonEnable(
|
||||
text.isNotNullOrEmpty() && text != widget.prevValueForField);
|
||||
},
|
||||
minLines: widget.minLines,
|
||||
maxLines: widget.maxLines,
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
hintText: widget.textFieldHint,
|
||||
hintStyle: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w400)
|
||||
.copyWith(color: Colors.grey),
|
||||
border: InputBorder.none,
|
||||
enabledBorder: InputBorder.none,
|
||||
focusedBorder: InputBorder.none,
|
||||
errorBorder: InputBorder.none,
|
||||
focusedErrorBorder: InputBorder.none,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _addButton(context) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
if (_controller.isButtonEnable()) {
|
||||
Get.back();
|
||||
widget.onButtonClick(_controller.textFieldTEC.text);
|
||||
}
|
||||
},
|
||||
child: Obx(() {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: _controller.isButtonEnable()
|
||||
? Get.theme.primaryColor
|
||||
: Colors.grey.withOpacity(0.5),
|
||||
borderRadius: BorderRadius.circular(4.r),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Get.theme.primaryColor.withOpacity(0.2),
|
||||
spreadRadius: _controller.isButtonEnable() ? 4 : 0,
|
||||
blurRadius: _controller.isButtonEnable() ? 8 : 0,
|
||||
offset: _controller.isButtonEnable()
|
||||
? const Offset(4, 8)
|
||||
: const Offset(0, 0))
|
||||
]),
|
||||
width: double.maxFinite,
|
||||
height: 48,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Visibility(
|
||||
visible: widget.wantLeadingIcon,
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.only(right: 8.0),
|
||||
child: Icon(
|
||||
Icons.add,
|
||||
color: Colors.white,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(widget.buttonLabel,
|
||||
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w700)
|
||||
.copyWith(color: Colors.white)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ContentController extends GetxController {
|
||||
final TextEditingController textFieldTEC = TextEditingController();
|
||||
final FocusNode textFieldFN = FocusNode();
|
||||
|
||||
final isButtonEnable = false.obs;
|
||||
|
||||
initData(String prevText, String prevColor) {
|
||||
textFieldTEC.text = (prevText.isNullOrEmptyNot()) ? prevText : "";
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
textFieldTEC.dispose();
|
||||
textFieldFN.unfocus();
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
35
lib/view/custom_widgets/my_circle_image.dart
Normal file
35
lib/view/custom_widgets/my_circle_image.dart
Normal file
@@ -0,0 +1,35 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'my_network_image.dart';
|
||||
|
||||
class MyCircleImage extends StatelessWidget {
|
||||
final double imageSize;
|
||||
final String url;
|
||||
final Widget? loadingWidget;
|
||||
final Widget? errorWidget;
|
||||
final BoxFit? fit;
|
||||
|
||||
const MyCircleImage(
|
||||
{Key? key,
|
||||
required this.imageSize,
|
||||
required this.url,
|
||||
this.loadingWidget,
|
||||
this.errorWidget,
|
||||
this.fit})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ClipOval(
|
||||
child: SizedBox(
|
||||
width: imageSize,
|
||||
height: imageSize,
|
||||
child: MyNetworkImage(
|
||||
url: url,
|
||||
errorWidget: errorWidget,
|
||||
loadingWidget: loadingWidget,
|
||||
fit: fit,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
77
lib/view/custom_widgets/my_network_image.dart
Normal file
77
lib/view/custom_widgets/my_network_image.dart
Normal file
@@ -0,0 +1,77 @@
|
||||
import 'dart:io';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:get/utils.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
class MyNetworkImage extends StatelessWidget {
|
||||
final String url;
|
||||
final Widget? loadingWidget;
|
||||
final Widget? errorWidget;
|
||||
final BoxFit? fit;
|
||||
final int? memCacheWidth;
|
||||
final int? memCacheHeight;
|
||||
|
||||
const MyNetworkImage({
|
||||
Key? key,
|
||||
required this.url,
|
||||
this.loadingWidget,
|
||||
this.errorWidget,
|
||||
this.fit,
|
||||
this.memCacheWidth,
|
||||
this.memCacheHeight,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final errWidget =
|
||||
(errorWidget ?? ColoredBox(color: Colors.grey.withOpacity(0.3)));
|
||||
return (url.isEmpty || !url.isImageFileName)
|
||||
? errWidget
|
||||
: _media;
|
||||
}
|
||||
|
||||
Widget get _media {
|
||||
return (path.extension(url).toLowerCase() == ".svg")
|
||||
? SvgPicture.network(
|
||||
url,
|
||||
fit: fit ?? BoxFit.cover,
|
||||
placeholderBuilder: (_) {
|
||||
return Center(
|
||||
child: SizedBox.square(
|
||||
dimension: 32,
|
||||
child: (Platform.isIOS)
|
||||
? const CupertinoActivityIndicator(radius: 20)
|
||||
: const CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
: CachedNetworkImage(
|
||||
imageUrl: url,
|
||||
fit: fit ?? BoxFit.cover,
|
||||
memCacheWidth: memCacheWidth,
|
||||
memCacheHeight: memCacheHeight,
|
||||
placeholder: (context, url) {
|
||||
return loadingWidget ??
|
||||
Center(
|
||||
child: SizedBox.square(
|
||||
dimension: 32,
|
||||
child: (Platform.isIOS)
|
||||
? const CupertinoActivityIndicator(radius: 20)
|
||||
: const CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
errorWidget: (context, url, error) {
|
||||
return errorWidget ?? Icon(Icons.image_not_supported_outlined);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ftc_mobile_app/ftc_mobile_app.dart';
|
||||
|
||||
|
||||
class ShowDialogNotification extends StatelessWidget {
|
||||
const ShowDialogNotification({
|
||||
super.key,
|
||||
required this.rotaShift,
|
||||
});
|
||||
|
||||
final RotaShift rotaShift;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
surfaceTintColor: CustomAppColors.kPrimaryColor,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(2)),
|
||||
backgroundColor: CustomAppColors.kPrimaryColor,
|
||||
title: Center(
|
||||
child: CustomTextWidget(
|
||||
text: rotaShift.managerName,
|
||||
fontWeight: FontWeight.bold,
|
||||
isExpanded: false,
|
||||
alignment: Alignment.center,
|
||||
fontSize: 16.sp,
|
||||
),
|
||||
),
|
||||
content: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
NotificationInfoRow( label: 'Service User:', value: rotaShift.name,),
|
||||
const SizedBox(height: 5,),
|
||||
NotificationInfoRow(label: 'Worker Type Role:', value: rotaShift.workerType),
|
||||
const SizedBox(height: 5,),
|
||||
NotificationInfoRow(label: 'Location:',value: rotaShift.location),
|
||||
const SizedBox(height: 5,),
|
||||
NotificationInfoRow(label: 'Start Time:', value: rotaShift.startTime),
|
||||
const SizedBox(height: 5,),
|
||||
NotificationInfoRow(label: 'End Time:',value: rotaShift.endTime),
|
||||
const SizedBox(height: 5,),
|
||||
NotificationInfoRow(label: 'Break Time:',value: rotaShift.breakTime),
|
||||
const SizedBox(height: 5,),
|
||||
NotificationInfoRow(label: 'Notes:',value: rotaShift.notes),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Visibility(
|
||||
visible: false,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
maximumSize: Size(MediaQuery.of(context).size.width/3.5, 30.h),
|
||||
minimumSize: Size(MediaQuery.of(context).size.width/3.5, 30.h),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(5.r),
|
||||
),
|
||||
backgroundColor: CustomAppColors.kRedColor
|
||||
),
|
||||
onPressed: () {
|
||||
// Add functionality for the "Decline" button.
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: const CustomTextWidget(text: 'Decline',fontColor: CustomAppColors.kPrimaryColor),
|
||||
),
|
||||
),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
// maximumSize: Size(MediaQuery.of(context).size.width/3.5, 30.h),
|
||||
maximumSize: Size(MediaQuery.of(context).size.width/1.8, 30.h),
|
||||
// minimumSize: Size(MediaQuery.of(context).size.width/3.5, 30.h),
|
||||
minimumSize: Size(MediaQuery.of(context).size.width/1.8, 30.h),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(5.r),
|
||||
),
|
||||
backgroundColor: CustomAppColors.kPrimaryColor
|
||||
),
|
||||
onPressed: () {
|
||||
// Add functionality for the "Close" button.
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: CustomTextWidget(text: 'Close',fontColor: CustomAppColors.kBlackColor,fontSize: 15.sp),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NotificationInfoRow extends StatelessWidget {
|
||||
const NotificationInfoRow({
|
||||
super.key,required this.label, required this.value, this.half = false
|
||||
});
|
||||
|
||||
final String label, value;
|
||||
final bool half;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(width: 1.w, color: CustomAppColors.kLightGreyColor),
|
||||
borderRadius: BorderRadius.circular(5.r)),
|
||||
padding: EdgeInsets.only(left: 10.w,top: 1.h,bottom: 1.h),
|
||||
width: half ? null : MediaQuery.of(context).size.width,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CustomTextWidget(text: label,isExpanded: false,fontSize: 12.sp),
|
||||
CustomTextWidget(text: value,fontWeight: FontWeight.w600,isExpanded: false,fontSize: 14,),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export 'custom_dialog_notification.dart';
|
||||
export 'holiday_request_accept_dialog.dart';
|
||||
@@ -0,0 +1,105 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ftc_mobile_app/ftc_mobile_app.dart';
|
||||
|
||||
|
||||
class HolidayRequestAcceptDialog extends StatelessWidget {
|
||||
const HolidayRequestAcceptDialog({
|
||||
super.key, required this.startDate, required this.endDate, required this.noOfDays,
|
||||
});
|
||||
|
||||
final String startDate;
|
||||
final String endDate;
|
||||
final String noOfDays;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
insetPadding: EdgeInsets.only(right: 18.w,left: 18.w, top:40.h ,bottom: 230.h),
|
||||
contentPadding: EdgeInsets.only(left: 15.w,right: 15.w,top: 11.h,bottom: 0.h),
|
||||
surfaceTintColor: CustomAppColors.kPrimaryColor,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(2)),
|
||||
backgroundColor: CustomAppColors.kPrimaryColor,
|
||||
title: Center(
|
||||
child: CustomTextWidget(
|
||||
text: "Your Holiday Request has been sent",
|
||||
fontWeight: FontWeight.w700,
|
||||
isExpanded: false,
|
||||
alignment: Alignment.center,
|
||||
fontSize: 14.sp,
|
||||
),
|
||||
),
|
||||
content: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: DateInfo( label: 'Start Date', value: startDate,half: true)),
|
||||
SizedBox(width: 5.w,),
|
||||
Expanded(child: DateInfo(label: 'End Date', value: endDate,half: true)),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10.h,),
|
||||
DateInfo(label: 'Holiday Total Time',value: noOfDays),
|
||||
SizedBox(height: 10.h,),
|
||||
CustomTextWidget(text: "Kindly wait as we review your holiday request.",fontSize: 12.sp),
|
||||
SizedBox(height: 10.h,),
|
||||
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(5.r),
|
||||
),
|
||||
backgroundColor: CustomAppColors.kPrimaryColor,
|
||||
surfaceTintColor: CustomAppColors.kPrimaryColor,
|
||||
),
|
||||
onPressed: () {
|
||||
// Add functionality for the "Close" button.
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: CustomTextWidget(
|
||||
text: 'Close',
|
||||
fontColor: CustomAppColors.kBlackColor,
|
||||
fontSize: 15.sp),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DateInfo extends StatelessWidget {
|
||||
const DateInfo({
|
||||
super.key,required this.label, required this.value, this.half = false
|
||||
});
|
||||
|
||||
final String label, value;
|
||||
final bool half;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(width: 1.w, color: CustomAppColors.kLightGreyColor),
|
||||
borderRadius: BorderRadius.circular(5.r)),
|
||||
padding: EdgeInsets.only(left: 10.w,top: 1.h,bottom: 1.h),
|
||||
width: half ? null : MediaQuery.of(context).size.width,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CustomTextWidget(text: label, isExpanded: false, fontSize: 12.sp),
|
||||
CustomTextWidget(
|
||||
text: value,
|
||||
fontWeight: FontWeight.w600,
|
||||
isExpanded: false,
|
||||
fontSize: 14,
|
||||
fontColor: CustomAppColors.kDarkBlueTextColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
321
lib/view/custom_widgets/rota/custom_calendar_widget.dart
Normal file
321
lib/view/custom_widgets/rota/custom_calendar_widget.dart
Normal file
@@ -0,0 +1,321 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_calendar_carousel/classes/event.dart';
|
||||
import 'package:flutter_calendar_carousel/flutter_calendar_carousel.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import '../../../ftc_mobile_app.dart';
|
||||
|
||||
// class SetMonthAndYearWidget extends StatelessWidget {
|
||||
// const SetMonthAndYearWidget({
|
||||
// super.key,
|
||||
// required this.currentMonthName,
|
||||
// required this.previousMonthTap,
|
||||
// required this.nextMonthTap,
|
||||
// });
|
||||
//
|
||||
// final String currentMonthName;
|
||||
// final VoidCallback previousMonthTap;
|
||||
// final VoidCallback nextMonthTap;
|
||||
//
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return Container(
|
||||
// padding: const EdgeInsets.only(left: 20.0, right: 20.0),
|
||||
// child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// children: [
|
||||
// GestureDetector(
|
||||
// onTap: previousMonthTap,
|
||||
// child: Transform.rotate(
|
||||
// angle: 3.1415 * 2.5,
|
||||
// child: const Icon(Icons.arrow_drop_down),
|
||||
// ),
|
||||
// ),
|
||||
// CustomTextWidget(
|
||||
// isExpanded: false,
|
||||
// text: currentMonthName,
|
||||
// textAlign: TextAlign.center,
|
||||
// fontColor: CustomAppColors.kBlackColor,
|
||||
// fontSize: 13,
|
||||
// fontWeight: FontWeight.bold),
|
||||
// GestureDetector(
|
||||
// onTap: nextMonthTap,
|
||||
// child: Transform.rotate(
|
||||
// angle: 3.1415 * 3.5,
|
||||
// child: const Icon(Icons.arrow_drop_down),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
typedef OnRangeSelect = Function(
|
||||
DateTime rangeStart, DateTime? rangeEnd, List<String> selectedDates);
|
||||
|
||||
class CalendarWidget extends StatefulWidget {
|
||||
static final dateFormat = DateFormat("dd-MM-yyyy");
|
||||
|
||||
const CalendarWidget({
|
||||
super.key,
|
||||
this.minDate,
|
||||
this.maxDate,
|
||||
this.onEventTap,
|
||||
required this.targetDateTime,
|
||||
required this.markedDatesMap,
|
||||
this.onDayTap,
|
||||
this.rangeStart,
|
||||
this.rangeEnd,
|
||||
this.canSelectRange = false,
|
||||
this.onRangeSelect,
|
||||
});
|
||||
|
||||
final DateTime? minDate;
|
||||
final DateTime? maxDate;
|
||||
final DateTime targetDateTime;
|
||||
final EventList<Event> markedDatesMap;
|
||||
final ValueChanged<List<Event>>? onEventTap;
|
||||
final Function(DateTime, List<Event>)? onDayTap;
|
||||
final bool canSelectRange;
|
||||
|
||||
///[selectedDates] will be in format dd-MM-yyyy
|
||||
final OnRangeSelect? onRangeSelect;
|
||||
final DateTime? rangeStart;
|
||||
final DateTime? rangeEnd;
|
||||
|
||||
@override
|
||||
State<CalendarWidget> createState() => _CalendarWidgetState();
|
||||
|
||||
///This method checks if [start] and [end] dates are at proper position i.e.,
|
||||
///[start] date should be before [end] date. If not, this method corrects the two dates and
|
||||
///return proper [start] [end] dates
|
||||
static ({DateTime start, DateTime end}) rectifyStartEndDates(
|
||||
DateTime start, DateTime end) {
|
||||
final first = start.isBefore(end) ? start : end;
|
||||
final last = end.isAfter(start) ? end : start;
|
||||
return (start: first, end: last);
|
||||
}
|
||||
|
||||
static List<String> getDatesFromStartEndRange(DateTime start, DateTime end) {
|
||||
final List<String> dates = [];
|
||||
// final first = start.isBefore(end) ? start : end;
|
||||
// final last = end.isAfter(start) ? end : start;
|
||||
|
||||
final record = rectifyStartEndDates(start, end);
|
||||
for (int i = 0; i <= record.end.difference(record.start).inDays; i++) {
|
||||
dates.add(dateFormat.format(record.start.add(i.days)));
|
||||
}
|
||||
return dates;
|
||||
}
|
||||
|
||||
static Widget shiftIcon(String day) => CircleAvatar(
|
||||
backgroundColor: CustomAppColors.kSecondaryColor,
|
||||
child: Text(
|
||||
day,
|
||||
style:
|
||||
const TextStyle(color: CustomAppColors.kWhiteColor, fontSize: 13),
|
||||
),
|
||||
);
|
||||
|
||||
static Widget underlineIcon() => Container(
|
||||
alignment: Alignment.center,
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: CustomAppColors.kSecondaryColor,
|
||||
width: 3.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class _CalendarWidgetState extends State<CalendarWidget> {
|
||||
//Selected dates will be in format dd-MM-yyyy
|
||||
final selectedDates = RxList<String>();
|
||||
|
||||
DateTime? start, end;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
if (widget.canSelectRange &&
|
||||
widget.rangeStart != null &&
|
||||
widget.rangeEnd != null) {
|
||||
start = widget.rangeStart;
|
||||
end = widget.rangeEnd;
|
||||
|
||||
selectedDates.value =
|
||||
CalendarWidget.getDatesFromStartEndRange(start!, end!);
|
||||
} else {
|
||||
selectedDates.value = [
|
||||
CalendarWidget.dateFormat.format(widget.targetDateTime)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
double width = MediaQuery.of(context).size.width;
|
||||
double height = MediaQuery.of(context).size.height;
|
||||
double textScaleFactor = MediaQuery.of(context).textScaleFactor;
|
||||
double itemHeight = (height * textScaleFactor - kToolbarHeight - 24) / 4;
|
||||
double calHeight = (height * textScaleFactor) / 2.9;
|
||||
double itemWidth = width * 0.7;
|
||||
|
||||
return Container(
|
||||
height: calHeight,
|
||||
margin: const EdgeInsets.only(top: 10, bottom: 3),
|
||||
padding: const EdgeInsets.only(left: 10.0, right: 10.0),
|
||||
child: CalendarCarousel<Event>(
|
||||
targetDateTime: widget.targetDateTime,
|
||||
firstDayOfWeek: 0,
|
||||
dayPadding: 1.0,
|
||||
minSelectedDate: widget.minDate,
|
||||
maxSelectedDate: widget.maxDate,
|
||||
todayTextStyle: const TextStyle(
|
||||
color: CustomAppColors.kBlackColor,
|
||||
fontWeight: FontWeight.normal,
|
||||
fontSize: 13.0,
|
||||
),
|
||||
weekdayTextStyle: const TextStyle(
|
||||
color: CustomAppColors.kBlueColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 13.0,
|
||||
),
|
||||
weekendTextStyle: const TextStyle(
|
||||
color: CustomAppColors.kBlackColor,
|
||||
fontSize: 13.0,
|
||||
),
|
||||
customDayBuilder: (bool isSelectable,
|
||||
int index,
|
||||
bool isSelectedDay,
|
||||
bool isToday,
|
||||
bool isPrevMonthDay,
|
||||
TextStyle textStyle,
|
||||
bool isNextMonthDay,
|
||||
bool isThisMonthDay,
|
||||
DateTime day) {
|
||||
if (selectedDates.contains(CalendarWidget.dateFormat.format(day))) {
|
||||
return Container(
|
||||
width: double.maxFinite,
|
||||
height: double.maxFinite,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
day.day.toString(),
|
||||
style: textStyle.copyWith(color: Colors.white),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onDayPressed: (DateTime date, List<Event> events) {
|
||||
if (widget.onEventTap != null) {
|
||||
widget.onEventTap!(events);
|
||||
}
|
||||
if (widget.onDayTap != null) {
|
||||
widget.onDayTap!(date, events);
|
||||
}
|
||||
if (widget.canSelectRange) {
|
||||
rangeSelectionHandler(date);
|
||||
}
|
||||
},
|
||||
childAspectRatio: textScaleFactor > 1.5
|
||||
? ((itemWidth / itemHeight) * 1.1)
|
||||
: textScaleFactor > 1
|
||||
? ((itemWidth / itemHeight) * 1.05)
|
||||
: (itemWidth / itemHeight),
|
||||
todayButtonColor: Colors.transparent,
|
||||
todayBorderColor: Colors.transparent,
|
||||
daysHaveCircularBorder: true,
|
||||
isScrollable: true,
|
||||
shouldShowTransform: false,
|
||||
pageSnapping: true,
|
||||
// selectedDateTime: DateTime(2024, 5, 25),
|
||||
thisMonthDayBorderColor: Colors.transparent,
|
||||
daysTextStyle: const TextStyle(
|
||||
fontSize: 13.0,
|
||||
color: CustomAppColors.kBlackColor,
|
||||
),
|
||||
weekFormat: false,
|
||||
showOnlyCurrentMonthDate: true,
|
||||
width: width,
|
||||
onHeaderTitlePressed: () {},
|
||||
customGridViewPhysics: const NeverScrollableScrollPhysics(),
|
||||
markedDatesMap: widget.markedDatesMap,
|
||||
//marked up dates
|
||||
markedDateIconBuilder: (event) => event.icon,
|
||||
markedDateCustomTextStyle: TextStyle(
|
||||
fontSize: 13.sp,
|
||||
color: CustomAppColors.kWhiteColor,
|
||||
),
|
||||
markedDateShowIcon: true,
|
||||
markedDateIconMaxShown: 1,
|
||||
markedDateMoreShowTotal: null,
|
||||
showHeader: true,
|
||||
leftButtonIcon: const Icon(
|
||||
Icons.arrow_left,
|
||||
color: Colors.black,
|
||||
),
|
||||
rightButtonIcon: const Icon(
|
||||
Icons.arrow_right,
|
||||
color: Colors.black,
|
||||
),
|
||||
headerTextStyle: const TextStyle(
|
||||
color: CustomAppColors.kBlackColor,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold),
|
||||
onCalendarChanged: (DateTime date) {},
|
||||
onDayLongPressed: (DateTime date) {},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
rangeSelectionHandler(DateTime selectedDay) {
|
||||
final d = CalendarWidget.dateFormat.format(selectedDay);
|
||||
|
||||
if (selectedDates.contains(d)) {
|
||||
selectedDates.remove(d);
|
||||
} else {
|
||||
selectedDates.add(d);
|
||||
}
|
||||
|
||||
//meaning either no range selected or range already been selected
|
||||
if (start == null || (start != null && end != null)) {
|
||||
start = selectedDay;
|
||||
end = null;
|
||||
selectedDates
|
||||
..clear()
|
||||
..add(CalendarWidget.dateFormat.format(selectedDay));
|
||||
|
||||
if (widget.onRangeSelect != null) {
|
||||
widget.onRangeSelect!(start!, null, selectedDates());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//Already selected start date now selecting range end date
|
||||
if (end == null) {
|
||||
final record = CalendarWidget.rectifyStartEndDates(start!, selectedDay);
|
||||
|
||||
start = record.start;
|
||||
end = record.end;
|
||||
|
||||
selectedDates.clear();
|
||||
selectedDates.value =
|
||||
CalendarWidget.getDatesFromStartEndRange(start!, end!);
|
||||
}
|
||||
print("start: ${start.toString()}\nEnd:${end.toString()}");
|
||||
|
||||
if (widget.onRangeSelect != null) {
|
||||
widget.onRangeSelect!(start!, end, selectedDates());
|
||||
}
|
||||
}
|
||||
}
|
||||
3
lib/view/custom_widgets/rota/export_rota.dart
Normal file
3
lib/view/custom_widgets/rota/export_rota.dart
Normal file
@@ -0,0 +1,3 @@
|
||||
export 'custom_calendar_widget.dart';
|
||||
export "package:ftc_mobile_app/dialogs/widgets/shift_dialog.dart";
|
||||
export 'rota_list_item.dart';
|
||||
130
lib/view/custom_widgets/rota/rota_list_item.dart
Normal file
130
lib/view/custom_widgets/rota/rota_list_item.dart
Normal file
@@ -0,0 +1,130 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ftc_mobile_app/models/rota/WeekArrayData.dart';
|
||||
import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart';
|
||||
import '../../../ftc_mobile_app.dart';
|
||||
|
||||
class RotaWidget extends StatelessWidget {
|
||||
const RotaWidget(
|
||||
{super.key, required this.data, required this.onViewShiftTap});
|
||||
|
||||
final DaysArrayData data;
|
||||
final Function() onViewShiftTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
// height: 150,
|
||||
alignment: Alignment.center,
|
||||
padding: EdgeInsets.all(10.sp),
|
||||
width: MediaQuery.of(context).size.width,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
border: Border(
|
||||
left: BorderSide(
|
||||
width: 6.r,
|
||||
color: CustomAppColors.kSecondaryColor,
|
||||
),
|
||||
top: const BorderSide(
|
||||
color: CustomAppColors.kSecondaryColor,
|
||||
),
|
||||
right: const BorderSide(
|
||||
color: CustomAppColors.kSecondaryColor,
|
||||
),
|
||||
bottom: const BorderSide(
|
||||
color: CustomAppColors.kSecondaryColor,
|
||||
),
|
||||
),
|
||||
borderRadius: BorderRadius.all(Radius.circular(2.0.r)),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
text: data.locationId?.shiftLocationName ?? "",
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
children: [
|
||||
const TextSpan(text: ' ('),
|
||||
TextSpan(
|
||||
text: ((data.serviceUserId?.displayName ??
|
||||
"Unassigned"))
|
||||
.trim(),
|
||||
style: TextStyle(
|
||||
color: data.serviceUserId?.displayName
|
||||
.isNotNullOrEmpty() ==
|
||||
true
|
||||
? Colors.black
|
||||
: Colors.red)),
|
||||
const TextSpan(text: ')'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
CustomTextWidget(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 10.sp,
|
||||
text: DateFormatter().getRotaNewDate(
|
||||
shiftDate: data.shiftDate ?? 0,
|
||||
startShiftTime: data.shiftStartTime ?? '0',
|
||||
endShiftTime: data.shiftEndTime ?? '0',
|
||||
),
|
||||
isExpanded: false),
|
||||
6.verticalSpace,
|
||||
CustomTextWidget(
|
||||
text: '${int.parse(data.workHrs?.toString() ?? "0")}hrs',
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
isExpanded: false,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
10.verticalSpace,
|
||||
ShiftButtonWidget(onTap: onViewShiftTap)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ShiftButtonWidget extends StatelessWidget {
|
||||
final VoidCallback onTap;
|
||||
|
||||
const ShiftButtonWidget({
|
||||
super.key,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: 26.h,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: CustomAppColors.kSecondaryColor,
|
||||
borderRadius: BorderRadius.circular(2.r),
|
||||
),
|
||||
child: const Text(
|
||||
"View Shift",
|
||||
style: TextStyle(color: CustomAppColors.kPrimaryColor),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user