fist commit ftc staff app clone

This commit is contained in:
2024-08-01 13:46:46 +05:30
commit bf9064a9c9
515 changed files with 42796 additions and 0 deletions

View 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),
),
),
),
],
),
],
),
);
}
}

View File

@@ -0,0 +1 @@
export 'custom_forget_password_dialog.dart';

View 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)
],
),
),
);
}
}

View File

@@ -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;
}
},
),
),
],
),
);
});
}
}

View 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)
],
),
),
);
}
}

View File

@@ -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),
// ),
// ],
// ),
// ],
// );
// }
// }

View 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,
),
),
);
}
}

View 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);
}

View 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,
),
);
}

View 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);
// }

View 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,
),
),
),
),
],
),
),
);
}
}

View 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,
),
),
),
],
);
}
}

View 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,
),
);
}
}

View 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,
),
);
}
}

View 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,
),
),
],
),
),
);
}
}

View 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,
)
],
),
);
}
}

View 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,
);
},
),
),
);
}
}

View 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!,
],
),
);
}
}

View 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,
),
);
}
}

View 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,
),
)),
);
}
}

View 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';

View 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),
),
),
],
),
);
}
}

View File

@@ -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),
),
),
],
),
);
}
}

View File

@@ -0,0 +1,2 @@
export 'custom_privacy_policy_dialog.dart';
export 'custom_message_dialog.dart';

View 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),
),
),
),
);
}
}

View 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()
],
),
);
}
}

View 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,
),
),
],
);
}

View 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();
}
}

View 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,
),
),
);
}
}

View 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);
},
);
}
}

View File

@@ -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,),
],
),
);
}
}

View File

@@ -0,0 +1,2 @@
export 'custom_dialog_notification.dart';
export 'holiday_request_accept_dialog.dart';

View File

@@ -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,
),
],
),
);
}
}

View 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());
}
}
}

View 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';

View 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),
),
),
);
}
}