feat: add image upload functionality to poultry science feature, including repository and data source updates for handling image uploads

This commit is contained in:
2025-12-10 16:42:53 +03:30
parent 455a5a5571
commit 3d73d9a17a
23 changed files with 3481 additions and 36 deletions

View File

@@ -3,3 +3,5 @@ abstract class PoultryFarmLocalDataSource {
}

View File

@@ -3,3 +3,5 @@ abstract class PoultryFarmRemoteDataSource {
}

View File

@@ -3,3 +3,5 @@ abstract class PoultryFarmRepository {
}

View File

@@ -89,4 +89,9 @@ abstract class PoultryScienceRemoteDataSource {
required String token,
required String orderId,
});
Future<List<String>?> uploadImages({
required String token,
required List<XFile> images,
});
}

View File

@@ -245,5 +245,21 @@ class PoultryScienceRemoteDataSourceImpl
);
}
@override
Future<List<String>?> uploadImages({
required String token,
required List<XFile> images,
}) async {
var res = await _httpClient.post<List<String>?>(
'/upload_image_to_server_for_poultry_science/',
headers: {'Authorization': 'Bearer $token'},
data: FormData.fromMap({
'file': images.map((e) => MultipartFile.fromFileSync(e.path)).toList(),
}),
fromJson: (json) => List<String>.from(json['urls'] as List),
);
return res.data;
}
//endregion
}

View File

@@ -89,4 +89,9 @@ abstract class PoultryScienceRepository {
required String token,
required String orderId,
});
Future<List<String>?> uploadImages({
required String token,
required List<XFile> images,
});
}

View File

@@ -180,4 +180,12 @@ class PoultryScienceRepositoryImpl implements PoultryScienceRepository {
}) async {
return await _dataSource.deletePoultryOder(token: token, orderId: orderId);
}
@override
Future<List<String>?> uploadImages({
required String token,
required List<XFile> images,
}) async {
return await _dataSource.uploadImages(token: token, images: images);
}
}

View File

@@ -1,3 +1,4 @@
import 'package:rasadyar_chicken/data/di/chicken_di.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/hatching/hatching_models.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/repositories/poultry_science_repository.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/root/logic.dart';
@@ -22,7 +23,7 @@ class ActiveHatchingLogic extends GetxController {
@override
void onInit() {
super.onInit();
poultryScienceRepository = Get.find<PoultryScienceRepository>();
poultryScienceRepository = diChicken.get<PoultryScienceRepository>();
}
@override

View File

@@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/hatching/hatching_models.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/active_hatching/logic.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/create_inspection_bottom_sheet.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/create_inspection_bottom_sheet_logic.dart';
import 'package:rasadyar_chicken/presentation/utils/nested_keys_utils.dart';
import 'package:rasadyar_chicken/presentation/widget/base_page/view.dart';
import 'package:rasadyar_core/core.dart';
@@ -50,7 +52,6 @@ class ActiveHatchingPage extends GetView<ActiveHatchingLogic> {
itemCount: data.value.data?.results?.length ?? 0,
separatorBuilder: (context, index) => SizedBox(height: 8.h),
onLoadMore: () async => controller.getHatchingList(true),
);
}, controller.activeHatchingList);
}
@@ -58,7 +59,10 @@ class ActiveHatchingPage extends GetView<ActiveHatchingLogic> {
Container itemListExpandedWidget(HatchingModel item) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(8)),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: Column(
spacing: 8,
children: [
@@ -111,7 +115,10 @@ class ActiveHatchingPage extends GetView<ActiveHatchingLogic> {
),
),
buildRow(title: 'شماره مجوز جوجه ریزی', value: item.licenceNumber ?? 'N/A'),
buildRow(
title: 'شماره مجوز جوجه ریزی',
value: item.licenceNumber ?? 'N/A',
),
buildUnitRow(
title: 'حجم جوجه ریزی',
value: item.quantity.separatedByCommaFa,
@@ -122,21 +129,52 @@ class ActiveHatchingPage extends GetView<ActiveHatchingLogic> {
value: item.leftOver.separatedByCommaFa,
unit: '(قطعه)',
),
buildUnitRow(title: 'تلفات', value: item.losses.separatedByCommaFa, unit: '(قطعه)'),
buildUnitRow(
title: 'تلفات',
value: item.losses.separatedByCommaFa,
unit: '(قطعه)',
),
buildRow(
title: 'دامپزشک فارم',
value: '${item.vetFarm?.vetFarmFullName}(${item.vetFarm?.vetFarmMobile})',
value:
'${item.vetFarm?.vetFarmFullName}(${item.vetFarm?.vetFarmMobile})',
),
buildRow(
title: 'شرح بازرسی',
value: item.reportInfo?.image == false ? 'ارسال تصویر جوجه ریزی فارم ' : 'تکمیل شده',
value: item.reportInfo?.image == false
? 'ارسال تصویر جوجه ریزی فارم '
: 'تکمیل شده',
titleStyle: AppFonts.yekan14.copyWith(
color: (item.reportInfo?.image ?? false) ? AppColor.greenNormal : AppColor.redDark,
color: (item.reportInfo?.image ?? false)
? AppColor.greenNormal
: AppColor.redDark,
),
valueStyle: AppFonts.yekan14.copyWith(
color: (item.reportInfo?.image ?? false) ? AppColor.greenNormal : AppColor.redDark,
color: (item.reportInfo?.image ?? false)
? AppColor.greenNormal
: AppColor.redDark,
),
),
RElevated(
height: 40.h,
isFullWidth: true,
onPressed: () {
Get.find<CreateInspectionBottomSheetLogic>().setHatchingModel(
item,
);
Get.bottomSheet(
CreateInspectionBottomSheet(),
isScrollControlled: true,
ignoreSafeArea: false,
).then((value) {
if (Get.isRegistered<CreateInspectionBottomSheetLogic>()) {
Get.find<CreateInspectionBottomSheetLogic>().clearForm();
}
});
},
child: Text('ثبت بازرسی'),
),
],
),
);

View File

@@ -1,7 +1,6 @@
import 'package:flutter/cupertino.dart' hide LinearGradient;
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/presentation/utils/nested_keys_utils.dart';
import 'package:rasadyar_chicken/presentation/widget/base_page/view.dart';
import 'package:rasadyar_core/core.dart';

View File

@@ -14,6 +14,7 @@ import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/pou
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/root/logic.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/root/view.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/create_inspection_bottom_sheet_logic.dart';
import 'package:rasadyar_chicken/presentation/routes/global_binding.dart';
import 'package:rasadyar_core/core.dart';
@@ -38,56 +39,76 @@ class PoultrySciencePages {
name: PoultryScienceRoutes.inspectionPoultryScience,
page: () => InspectionPoultrySciencePage(),
middlewares: [AuthMiddleware()],
binding: BindingsBuilder(() {
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => InspectionPoultryScienceLogic());
}),
],
),
GetPage(
name: PoultryScienceRoutes.actionPoultryScience,
page: () => PoultryActionPage(),
middlewares: [AuthMiddleware()],
binding: BindingsBuilder(() {
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => PoultryActionLogic());
}),
],
),
GetPage(
name: PoultryScienceRoutes.farmPoultryScience,
page: () => FarmPage(),
middlewares: [AuthMiddleware()],
binding: BindingsBuilder(() {
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => FarmLogic());
Get.lazyPut(() => PoultryScienceHomeLogic());
Get.lazyPut(() => PoultryScienceRootLogic());
}),
],
),
GetPage(
name: PoultryScienceRoutes.activeHatchingPoultryScience,
page: () => ActiveHatchingPage(),
middlewares: [AuthMiddleware()],
binding: BindingsBuilder(() {
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => ActiveHatchingLogic());
Get.lazyPut(() => CreateInspectionBottomSheetLogic(), fenix: true);
Get.lazyPut(() => PoultryScienceRootLogic());
}),
],
),
GetPage(
name: PoultryScienceRoutes.genocidePoultryScience,
page: () => GenocidePage(),
middlewares: [AuthMiddleware()],
binding: BindingsBuilder(() {
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => GenocideLogic());
Get.lazyPut(() => PoultryScienceRootLogic());
Get.lazyPut(() => KillingRegistrationLogic(), fenix: true);
}),
],
),
GetPage(
name: PoultryScienceRoutes.killingRegistrationPoultryScience,
page: () => KillingRegistrationPage(),
middlewares: [AuthMiddleware()],
binding: BindingsBuilder(() {
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => KillingRegistrationLogic());
Get.lazyPut(() => GenocideLogic());
Get.lazyPut(() => PoultryScienceRootLogic());
}),
],
),
];
}

View File

@@ -0,0 +1,60 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/create_inspection_bottom_sheet_logic.dart';
import 'package:rasadyar_core/core.dart';
Widget farmInfoWidget({
required CreateInspectionBottomSheetLogic controller,
required String title,
required Widget child,
EdgeInsets? padding,
}) {
return Stack(
clipBehavior: Clip.none,
children: [
Positioned.fill(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(width: 0.50, color: AppColor.mediumGrey),
),
padding:
padding ?? EdgeInsets.symmetric(horizontal: 12.w, vertical: 11.h),
child: child,
),
),
Positioned(
top: -17,
right: 7,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 5.h),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(width: 0.50, color: AppColor.mediumGrey),
),
child: Text(
title,
style: AppFonts.yekan14.copyWith(color: AppColor.iconColor),
),
),
),
],
);
}
Widget cardInfo({required Widget child, EdgeInsets? padding}) {
return Container(
width: Get.width,
padding: padding ?? EdgeInsets.symmetric(horizontal: 12.w, vertical: 14.h),
decoration: BoxDecoration(
color: AppColor.bgLight,
borderRadius: BorderRadius.circular(8),
border: Border.all(width: 1, color: AppColor.blackLight),
),
child: child,
);
}

View File

@@ -0,0 +1,233 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/create_inspection_bottom_sheet_logic.dart';
import 'package:rasadyar_core/core.dart';
import 'step1_page.dart';
import 'step2_page.dart';
import 'step3_page.dart';
import 'step4_page.dart';
import 'step5_page.dart';
class CreateInspectionBottomSheet
extends GetWidget<CreateInspectionBottomSheetLogic> {
CreateInspectionBottomSheet({super.key});
List<Widget> get pages => [
step1Page(controller),
step2Page(controller),
step3Page(controller),
step4Page(controller),
step5Page(controller),
];
@override
Widget build(BuildContext context) {
return BaseBottomSheet(
height: Get.height,
rootChild: Column(
children: [
ObxValue((data) {
return stepper(activeStep: data.value);
}, controller.activeStepperIndex),
Expanded(
child: PageView.builder(
physics: const NeverScrollableScrollPhysics(),
reverse: true,
controller: controller.pageController,
itemBuilder: (context, index) => pages[index],
),
),
ObxValue((data) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
spacing: 16,
children: [
Expanded(
child: ObxValue((data) {
return RElevated(
height: 40.h,
enabled: data.value,
backgroundColor: AppColor.greenNormal,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.arrow_back_ios, color: Colors.white),
Text('ادامه'),
],
),
onPressed: () {
if (controller.activeStepperIndex.value < 4) {
controller.activeStepperIndex.value++;
}
},
);
}, controller.nextStepButtonEnabled),
),
Expanded(
child: ROutlinedElevated(
borderColor: AppColor.error,
height: 40.h,
child: Text('برگشت'),
enabled: controller.activeStepperIndex.value > 0,
onPressed: () {
if (controller.activeStepperIndex.value > 0) {
controller.activeStepperIndex.value--;
}
},
),
),
],
),
);
}, controller.activeStepperIndex),
],
),
);
}
}
class stepper extends StatelessWidget {
const stepper({super.key, required this.activeStep});
final int activeStep;
@override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.ltr,
child: SizedBox(
height: 24,
width: Get.width,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: activeStep >= 0
? AppColor.greenNormalHover
: AppColor.whiteNormalActive,
shape: BoxShape.circle,
),
width: 24.w,
height: 24.h,
child: Text(
'1',
textAlign: TextAlign.center,
style: AppFonts.yekan16.copyWith(
color: activeStep >= 0 ? Colors.white : AppColor.iconColor,
),
),
),
Expanded(
child: Divider(
color: activeStep >= 1
? AppColor.greenNormalHover
: AppColor.whiteNormalActive,
thickness: 8,
),
),
Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: activeStep >= 1
? AppColor.greenNormalHover
: AppColor.whiteNormalActive,
shape: BoxShape.circle,
),
width: 24.w,
height: 24.h,
child: Text(
'2',
textAlign: TextAlign.center,
style: AppFonts.yekan16.copyWith(
color: activeStep >= 1 ? Colors.white : AppColor.iconColor,
),
),
),
Expanded(
child: Divider(
color: activeStep >= 2
? AppColor.greenNormalHover
: AppColor.whiteNormalActive,
thickness: 8,
),
),
Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: activeStep >= 2
? AppColor.greenNormalHover
: AppColor.whiteNormalActive,
shape: BoxShape.circle,
),
width: 24.w,
height: 24.h,
child: Text(
'3',
textAlign: TextAlign.center,
style: AppFonts.yekan16.copyWith(
color: activeStep >= 2 ? Colors.white : AppColor.iconColor,
),
),
),
Expanded(
child: Divider(
color: activeStep >= 3
? AppColor.greenNormalHover
: AppColor.whiteNormalActive,
thickness: 8,
),
),
Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: activeStep >= 3
? AppColor.greenNormalHover
: AppColor.whiteNormalActive,
shape: BoxShape.circle,
),
width: 24.w,
height: 24.h,
child: Text(
'4',
textAlign: TextAlign.center,
style: AppFonts.yekan16.copyWith(
color: activeStep >= 3 ? Colors.white : AppColor.iconColor,
),
),
),
Expanded(
child: Divider(
color: activeStep >= 4
? AppColor.greenNormalHover
: AppColor.whiteNormalActive,
thickness: 8,
),
),
Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: activeStep >= 4
? AppColor.greenNormalHover
: AppColor.whiteNormalActive,
shape: BoxShape.circle,
),
width: 24.w,
height: 24.h,
child: Text(
'5',
textAlign: TextAlign.center,
style: AppFonts.yekan16.copyWith(
color: activeStep >= 3 ? Colors.white : AppColor.iconColor,
),
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,545 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/data/di/chicken_di.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/hatching/hatching_models.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/repositories/poultry_science_repository.dart';
import 'package:rasadyar_core/core.dart';
class CreateInspectionBottomSheetLogic extends GetxController
with GetSingleTickerProviderStateMixin {
HatchingModel? hatchingModel;
PoultryScienceRepository repository = diChicken
.get<PoultryScienceRepository>();
TokenStorageService tokenStorageService = Get.find<TokenStorageService>();
RxInt selectedSegmentIndex = 0.obs;
RxInt expandedIndex = RxInt(-1);
late TabController tabController;
RxInt selectedTabIndex = 0.obs;
RxInt activeStepperIndex = 0.obs;
PageController pageController = PageController(initialPage: 0);
RxBool nextStepButtonEnabled = false.obs;
RxInt casualtiesInformationHeight = 300.obs;
//step1
TextEditingController unitNameController = TextEditingController();
TextEditingController breedingUniqueIdController = TextEditingController();
TextEditingController healthLicenseController = TextEditingController();
TextEditingController tenantStatusController = TextEditingController();
TextEditingController tenantNameController = TextEditingController();
TextEditingController tenantNationalIdController = TextEditingController();
TextEditingController tenantPhoneNumberController = TextEditingController();
TextEditingController ownerNameController = TextEditingController();
TextEditingController ownerNationalCodeController = TextEditingController();
TextEditingController ownerPhoneNumberController = TextEditingController();
TextEditingController totalCapacityController = TextEditingController();
//--------------------------------
TextEditingController hatchingDateController = TextEditingController();
TextEditingController visitDateController = TextEditingController();
TextEditingController hatchingCountController = TextEditingController();
TextEditingController hatchingAverageWeightController =
TextEditingController();
TextEditingController hatchingBreedController = TextEditingController();
//step2
Resource<List<String>> causeOfUnusualCasualtiesList = Resource.success([
'بیماری',
'قطعی برق',
'استرس گرمایی',
'مشکلات دان',
'کیفیت جوجه',
'سایر (شرح…)',
]);
RxnString causeOfUnusualCasualties = RxnString();
RxBool isOthercauseOfUnusualCasualties = false.obs;
TextEditingController otherCauseOfUnusualCasualtiesController =
TextEditingController();
Resource<List<String>> typeOfDiseaseList = Resource.success([
'آنفلوانزا',
'نیوکاسل',
'IB',
'عفونت‌های باکتریایی',
'مشکلات گوارشی',
'سایر (شرح)',
]);
RxnString typeOfDisease = RxnString();
RxBool isOtherTypeOfDiseaseSelected = false.obs;
TextEditingController otherTypeOfDiseaseController = TextEditingController();
RxList<String> pultryImagesUrls = RxList<String>();
RxMap<XFile, bool> pultryImages = RxMap<XFile, bool>();
TextEditingController hatchingTemperatureController = TextEditingController();
TextEditingController waterHardnessController = TextEditingController();
TextEditingController normalLossesController = TextEditingController();
TextEditingController abnormalLossesController = TextEditingController();
TextEditingController sourceOfHatchingController = TextEditingController();
RxBool samplingDone = false.obs;
TextEditingController technicalHealthOfficerNameController =
TextEditingController();
TextEditingController technicalEngineeringOfficerNameController =
TextEditingController();
RxInt sanitaryConditionOfTheHallIndex = (-1).obs;
RxInt ventilationStatusIndex = (-1).obs;
RxnString sanitaryConditionOfTheHall = RxnString();
RxnString ventilationStatus = RxnString();
RxInt beddingStatusIndex = (-1).obs;
RxnString beddingStatus = RxnString();
RxInt waterQualityIndex = (-1).obs;
RxnString waterQuality = RxnString();
RxInt fuelTypeIndex = (-1).obs;
RxInt sampleTypeIndex = (-1).obs;
RxnString sampleType = RxnString();
//step3
Resource<List<String>> inputStatusList = Resource.success([
'وابسته',
'مستقل',
]);
RxnString inputStatus = RxnString();
TextEditingController companyNameController = TextEditingController();
TextEditingController trackingCodeController = TextEditingController();
Resource<List<String>> typeOfGrainList = Resource.success([
'آردی',
'پلت',
'کرامبل و اکسترود ',
'مش ',
'پوره و وال',
]);
RxnString typeOfGrain = RxnString();
TextEditingController inputInventoryInWarehouseController =
TextEditingController();
TextEditingController inputInventoryUntilVisitController =
TextEditingController();
TextEditingController generatorTypeController = TextEditingController();
TextEditingController generatorModelController = TextEditingController();
TextEditingController generatorCountController = TextEditingController();
TextEditingController generatorCapacityController = TextEditingController();
TextEditingController emergencyFuelInventoryController =
TextEditingController();
TextEditingController powerCutDurationController = TextEditingController();
TextEditingController powerCutHourController = TextEditingController();
TextEditingController additionalNotesController = TextEditingController();
RxnString fuelType = RxnString();
RxBool powerCutHistory = false.obs;
RxInt grainQualityInputIndex = (-1).obs;
RxnString grainQualityInput = RxnString();
RxInt generatorOperatingStatusIndex = (-1).obs;
RxnString generatorOperatingStatus = RxnString();
RxInt workerContractStatusIndex = (-1).obs;
RxnString workerContractStatus = RxnString();
RxInt newBeneficiaryRequestIndex = (-1).obs;
RxBool trainingStatus = false.obs;
RxnString newBeneficiaryRequest = RxnString();
RxBool overdueStatus = false.obs;
TextEditingController paymentYearController = TextEditingController();
Rx<Jalali> selectedPaymentYear = Jalali.now().obs;
//step3 - worker fields
TextEditingController employedWorkersCountController =
TextEditingController();
TextEditingController nativeWorkersCountController = TextEditingController();
TextEditingController nonNativeWorkersCountController =
TextEditingController();
TextEditingController activeFacilityController = TextEditingController();
TextEditingController facilityTypeController = TextEditingController();
TextEditingController facilityAmountController = TextEditingController();
//step4
TextEditingController facilityYearController = TextEditingController();
Rx<Jalali> selectedFacilityYear = Jalali.now().obs;
RxInt inspectorConclusionIndex = (-1).obs;
RxnString inspectorConclusion = RxnString();
TextEditingController inspectorConclusionDescriptionController =
TextEditingController();
RxList<String> hallImagesUrls = RxList<String>();
RxMap<XFile, bool> hallImages = RxMap<XFile, bool>();
RxList<String> inputWarehouseImagesUrls = RxList<String>();
RxMap<XFile, bool> inputWarehouseImages = RxMap<XFile, bool>();
RxList<String> lossesImagesUrls = RxList<String>();
RxMap<XFile, bool> lossesImages = RxMap<XFile, bool>();
//location
Rxn<LatLng> currentLocation = Rxn<LatLng>();
RxBool isLoadingLocation = false.obs;
//images
ImagePicker imagePicker = ImagePicker();
@override
void onReady() {
super.onReady();
activeStepperIndex.listen((value) {
pageController.animateToPage(
value,
duration: Duration(milliseconds: 300),
curve: Curves.linear,
);
});
initData();
setUpNextButtonListeners();
}
@override
void onClose() {
// TODO: implement onClose
super.onClose();
}
void initData() {
unitNameController.text =
hatchingModel?.poultry?.unitName ?? 'واحد مرغداری نامشخص';
breedingUniqueIdController.text =
hatchingModel?.poultry?.breedingUniqueId ?? 'کد یکتا/شناسه واحد نامشخص';
healthLicenseController.text =
hatchingModel?.poultry?.healthCertificateNumber ??
'پروانه بهداشتی ندارد ';
tenantStatusController.text = (hatchingModel?.poultry?.hasTenant == true)
? 'دارد'
: 'ندارد';
if (hatchingModel?.poultry?.hasTenant == true) {
tenantNameController.text =
hatchingModel?.tenantFullname ?? 'نام مستاجر نامشخص';
tenantNationalIdController.text =
hatchingModel?.tenantNationalCode ?? 'کد ملی مستاجر نامشخص';
tenantPhoneNumberController.text =
hatchingModel?.tenantMobile ?? 'شماره تماس مستاجر نامشخص';
}
ownerNameController.text =
hatchingModel?.poultry?.user?.fullname ?? 'مالک واحد مرغداری نامشخص';
ownerNationalCodeController.text =
hatchingModel?.chainCompany?.user?.nationalId ??
'کد ملی مالک واحد مرغداری نامشخص';
ownerPhoneNumberController.text =
hatchingModel?.poultry?.user?.mobile ??
'شماره تماس مالک واحد مرغداری نامشخص';
totalCapacityController.text =
hatchingModel?.poultry?.totalCapacity?.toString() ??
'ظرفیت اسمی سالن‌ها نامشخص';
hatchingDateController.text =
hatchingModel?.date?.toJalali.formatCompactDate() ??
'تاریخ جوجه ریزی نامشخص';
hatchingCountController.text =
hatchingModel?.quantity?.separatedByComma ?? 'تعداد جوجه ریزی نامشخص';
hatchingBreedController.text =
hatchingModel?.chickenBreed ?? 'نوع نژاد نامشخص';
visitDateController.text = Jalali.now().formatCompactDate();
}
void toggleExpanded(int index) {
expandedIndex.value = expandedIndex.value == index ? -1 : index;
}
void setHatchingModel(HatchingModel hatchingModel) {
this.hatchingModel = hatchingModel;
}
void changeSegmentIndex(int index) {
if (index == selectedSegmentIndex.value) {
return;
}
expandedIndex.value = -1;
selectedSegmentIndex.value = index;
}
void changeTab(int index) {
if (index == selectedTabIndex.value) {
return;
}
selectedTabIndex.value = index;
}
void clearForm() {
unitNameController.clear();
activeStepperIndex.value = 0;
selectedTabIndex.value = 0;
}
void setSanitaryConditionOfTheHallIndex(int index, String item) {
sanitaryConditionOfTheHall.value = item;
sanitaryConditionOfTheHallIndex.value =
index == sanitaryConditionOfTheHallIndex.value ? -1 : index;
}
void setFuelTypeIndex(int index, String item) {
fuelType.value = item;
fuelTypeIndex.value = index == fuelTypeIndex.value ? -1 : index;
}
void setVentilationStatusIndex(int index, String item) {
ventilationStatus.value = item;
ventilationStatusIndex.value = index == ventilationStatusIndex.value
? -1
: index;
}
void setBeddingStatusIndex(int index, String item) {
beddingStatus.value = item;
beddingStatusIndex.value = index == beddingStatusIndex.value ? -1 : index;
}
void setWaterQualityIndex(int index, String item) {
waterQuality.value = item;
waterQualityIndex.value = index == waterQualityIndex.value ? -1 : index;
}
void setSampleTypeIndex(int index, String item) {
sampleType.value = item;
sampleTypeIndex.value = index == sampleTypeIndex.value ? -1 : index;
}
void setGrainQualityInput(int index, String item) {
grainQualityInput.value = item;
grainQualityInputIndex.value = index == grainQualityInputIndex.value
? -1
: index;
}
void setGeneratorOperatingStatusIndex(int index, String item) {
generatorOperatingStatus.value = item;
generatorOperatingStatusIndex.value =
index == generatorOperatingStatusIndex.value ? -1 : index;
}
void setWorkerContractStatusIndex(int index, String item) {
workerContractStatus.value = item;
workerContractStatusIndex.value = index == workerContractStatusIndex.value
? -1
: index;
}
void setNewBeneficiaryRequestIndex(int index) {
newBeneficiaryRequestIndex.value = index == newBeneficiaryRequestIndex.value
? -1
: index;
}
void setInspectorConclusionIndex(int index, String item) {
inspectorConclusion.value = item;
inspectorConclusionIndex.value = index == inspectorConclusionIndex.value
? -1
: index;
}
Future<void> getCurrentLocation() async {
isLoadingLocation.value = true;
try {
final hasPermission = await checkLocationPermission(request: true);
if (!hasPermission) {
throw Exception('دسترسی به موقعیت مکانی داده نشده است');
}
// Get current location
final latLng = await determineCurrentLatLng();
currentLocation.value = latLng;
isLoadingLocation.value = false;
setUpNextButtonListeners();
} catch (e) {
isLoadingLocation.value = false;
currentLocation.value = null;
nextStepButtonEnabled.value = false;
}
}
void setUpNextButtonListeners() {
if (activeStepperIndex.value == 0) {
nextStepButtonEnabled.value = currentLocation.value != null;
} else {
nextStepButtonEnabled.value = false;
}
}
Future<void> pickImageFromCamera() async {
try {
final XFile? image = await imagePicker.pickImage(
source: ImageSource.camera,
imageQuality: 50,
maxWidth: 1080,
maxHeight: 720,
);
if (image != null) {
pultryImages[image] = false;
//await uploadImage(image);
}
} catch (e) {
Get.snackbar(
'خطا',
'خطا در باز کردن دوربین',
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red,
colorText: Colors.white,
);
}
}
Future<void> uploadImage(XFile imageFile) async {
await safeCall(
call: () async {
final urls = await repository.uploadImages(
token: tokenStorageService.accessToken.value ?? '',
images: [imageFile],
);
return urls;
},
onSuccess: (urls) {
if (urls != null) {
pultryImagesUrls.addAll(urls);
}
pultryImages[imageFile] = true;
},
onError: (error, e) {
pultryImages[imageFile] = false;
},
);
}
void removeImage(XFile image) {
pultryImages.remove(image);
}
void setTypeOfDiseaseIndex(String item) {
if (isOtherTypeOfDiseaseSelected.value && item != 'سایر (شرح)') {
casualtiesInformationHeight.value =
casualtiesInformationHeight.value - 50;
}
typeOfDisease.value = item;
isOtherTypeOfDiseaseSelected.value = item == 'سایر (شرح)';
if (isOtherTypeOfDiseaseSelected.value && item == 'سایر (شرح)') {
casualtiesInformationHeight.value =
casualtiesInformationHeight.value + 50;
}
}
void setCauseOfUnusualCasualtiesIndex(String item) {
if (isOthercauseOfUnusualCasualties.value && item != 'سایر (شرح…)') {
casualtiesInformationHeight.value =
casualtiesInformationHeight.value - 50;
}
causeOfUnusualCasualties.value = item;
isOthercauseOfUnusualCasualties.value = item == 'سایر (شرح…)';
if (item == 'سایر (شرح…)') {
casualtiesInformationHeight.value =
casualtiesInformationHeight.value + 50;
}
}
void setSamplingDone(String value) {
if (samplingDone.value && value != 'انجام شد') {
casualtiesInformationHeight.value =
casualtiesInformationHeight.value -80;
}
samplingDone.value = value == 'انجام شد';
if (value == 'انجام شد') {
casualtiesInformationHeight.value =
casualtiesInformationHeight.value + 80;
}
}
void setInputStatus(String item) {
inputStatus.value = item;
}
void setTypeOfGrain(String item) {
typeOfGrain.value = item;
}
void setFuelType(String item) {
fuelType.value = item;
}
void setPowerCutHistory(String item) {
powerCutHistory.value = item == 'دارد';
}
void setTrainingStatus(String item) {
trainingStatus.value = item == 'بله';
}
void setNewBeneficiaryRequest(String item) {
newBeneficiaryRequest.value = item;
}
void setOverdueStatus(String item) {
overdueStatus.value = item == 'دارای معوقه';
}
void showPaymentYearDatePicker() {
Get.bottomSheet(
modalDatePicker(
initialDate: selectedPaymentYear.value,
onDateSelected: (value) {
selectedPaymentYear.value = value;
paymentYearController.text = value.formatCompactDate();
},
),
);
}
void showFacilityYearDatePicker() {
Get.bottomSheet(
modalDatePicker(
initialDate: selectedFacilityYear.value,
onDateSelected: (value) {
selectedFacilityYear.value = value;
facilityYearController.text = value.formatCompactDate();
Get.back();
},
),
);
}
void removeHallImage(XFile key) {
hallImages.remove(key);
}
void removeInputWarehouseImage(XFile key) {
inputWarehouseImages.remove(key);
}
void removeLossesImage(XFile key) {
lossesImages.remove(key);
}
}

View File

@@ -0,0 +1,232 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/card_info.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/create_inspection_bottom_sheet_logic.dart';
import 'package:rasadyar_core/core.dart';
Widget step1Page(CreateInspectionBottomSheetLogic controller) {
return SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: Column(
children: [
SizedBox(height: 25.h),
Container(
height: controller.tenantStatusController.text == 'دارد'
? 570.h
: 445.h,
clipBehavior: Clip.none,
width: Get.width,
child: farmInfoWidget(
controller: controller,
title: 'اطلاعات پایه واحد',
child: basicUnitInformation(controller),
),
),
SizedBox(height: 30.h),
Container(
height: 280.h,
clipBehavior: Clip.none,
width: Get.width,
child: farmInfoWidget(
controller: controller,
title: 'اطلاعات جوجه ریزی',
child: hatchingInformation(controller),
),
),
SizedBox(height: 16.h),
],
),
);
}
Column basicUnitInformation(CreateInspectionBottomSheetLogic controller) {
return Column(
spacing: 10,
children: [
SizedBox(height: 1.h),
RTextField(
controller: controller.unitNameController,
label: 'نام واحد مرغداری',
filled: true,
filledColor: AppColor.bgLight,
readonly: true,
),
RTextField(
controller: controller.breedingUniqueIdController,
label: 'کد یکتا / شناسه واحد',
filled: true,
readonly: true,
filledColor: AppColor.bgLight,
),
RTextField(
controller: controller.healthLicenseController,
label: 'پروانه بهداشتی',
filled: true,
filledColor: AppColor.bgLight,
),
RTextField(
controller: controller.tenantStatusController,
label: 'وضعیت مستاجر',
filled: true,
filledColor: AppColor.bgLight,
),
Visibility(
child: controller.tenantStatusController.text == 'دارد'
? Column(
spacing: 10,
children: [
RTextField(
controller: controller.tenantNameController,
label: 'نام مستاجر',
filled: true,
filledColor: AppColor.bgLight,
),
RTextField(
controller: controller.tenantNationalIdController,
label: 'کد ملی مستاجر',
filled: true,
filledColor: AppColor.bgLight,
),
RTextField(
controller: controller.tenantPhoneNumberController,
label: 'شماره تماس مستاجر',
filled: true,
filledColor: AppColor.bgLight,
),
],
)
: SizedBox.shrink(),
),
RTextField(
controller: controller.ownerNationalCodeController,
label: 'کد ملی بهره‌بردار',
filled: true,
readonly: true,
filledColor: AppColor.bgLight,
),
RTextField(
controller: controller.ownerPhoneNumberController,
label: 'شماره تماس بهره‌بردار',
filled: true,
readonly: true,
filledColor: AppColor.bgLight,
),
RTextField(
controller: controller.totalCapacityController,
label: 'ظرفیت اسمی سالن‌ها',
filled: true,
filledColor: AppColor.bgLight,
),
Obx(
() => RElevated(
isFullWidth: true,
height: 40.h,
enabled: !controller.isLoadingLocation.value,
backgroundColor: AppColor.greenNormal,
onPressed: controller.isLoadingLocation.value
? null
: () => controller.getCurrentLocation(),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 4,
children: [
if (controller.isLoadingLocation.value)
SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
)
else
Icon(Icons.my_location_rounded, color: Colors.white, size: 24),
Text(
controller.currentLocation.value == null
? 'دریافت موقعیت جغرافیایی'
: 'موقعیت جغرافیایی دریافت شد',
style: AppFonts.yekan14.copyWith(color: Colors.white),
),
],
),
),
),
],
);
}
Column hatchingInformation(CreateInspectionBottomSheetLogic controller) {
return Column(
spacing: 10,
children: [
SizedBox(height: 1.h),
RTextField(
controller: controller.hatchingDateController,
label: 'تاریخ جوجه ریزی',
filled: true,
filledColor: AppColor.bgLight,
readonly: true,
),
RTextField(
controller: controller.visitDateController,
label: 'تاریخ بازدید',
filled: true,
filledColor: AppColor.bgLight,
readonly: true,
),
RTextField(
controller: controller.hatchingCountController,
label: 'تعداد جوجه‌ریزی اولیه',
filled: true,
filledColor: AppColor.bgLight,
readonly: true,
),
/* ResourceOverlayDropdown(
items: Resource.success(['سرابی', 'پژدر', 'روتوایلر', 'دیگر']),
itemBuilder: (item) => Text(item),
labelBuilder: (selected) => Text(selected ?? 'نوع نژاد'),
), */
/* RTextField(
controller: controller.unitNameController,
label: 'گرید جوجه',
filled: true,
filledColor: AppColor.bgLight,
readonly: true,
), */
RTextField(
controller: controller.hatchingBreedController,
label: 'نوع نژاد',
filled: true,
filledColor: AppColor.bgLight,
),
RTextField(
controller: controller.hatchingAverageWeightController,
label: 'میانگین وزن جوجه',
filled: true,
filledColor: AppColor.bgLight,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
SeparatorInputFormatter(),
],
),
],
);
}

View File

@@ -0,0 +1,654 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/card_info.dart';
import 'package:rasadyar_core/core.dart';
import 'create_inspection_bottom_sheet_logic.dart';
Widget step2Page(CreateInspectionBottomSheetLogic controller) {
return SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: Column(
children: [
SizedBox(height: 35.h),
Container(
height: 580.h,
clipBehavior: Clip.none,
width: Get.width,
child: farmInfoWidget(
controller: controller,
title: 'وضعیت عمومی سالن',
child: generalConditionOfTheHall(controller),
),
),
SizedBox(height: 30.h),
ObxValue((data) {
return Container(
height: data.value.h,
clipBehavior: Clip.none,
width: Get.width,
child: farmInfoWidget(
controller: controller,
title: 'تلفات',
child: casualtiesInformation(controller),
),
);
}, controller.casualtiesInformationHeight),
SizedBox(height: 24.h),
Container(
height: 130.h,
clipBehavior: Clip.none,
width: Get.width,
child: farmInfoWidget(
controller: controller,
title: 'بیماری‌ها و وضعیت سلامت',
child: diseasesAndHealthInformation(controller),
),
),
],
),
);
}
Column generalConditionOfTheHall(CreateInspectionBottomSheetLogic controller) {
return Column(
spacing: 10,
children: [
SizedBox.shrink(),
cardInfo(
padding: EdgeInsets.fromLTRB(0, 12, 12, 6),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 9,
children: [
Obx(
() => SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
// Fix: Use spread .entries.map for map iteration, and correct image argument
spacing: 8,
children: [
...controller.pultryImages.entries
.map(
(entry) => Stack(
children: [
Container(
height: 80.h,
width: 80.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: Border.all(
width: 1,
color: AppColor.blackLightHover,
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.file(
File(entry.key.path),
fit: BoxFit.cover,
),
),
),
// Delete button
Positioned(
top: -4,
right: -4,
child: GestureDetector(
onTap: () =>
controller.removeImage(entry.key),
child: Container(
width: 24,
height: 24,
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: Icon(
Icons.close,
color: Colors.white,
size: 16,
),
),
),
),
// Upload indicator
if (entry.value == false)
Positioned.fill(
child: Container(
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.5),
borderRadius: BorderRadius.circular(8),
),
child: Center(
child: SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor:
AlwaysStoppedAnimation<Color>(
Colors.white,
),
),
),
),
),
),
],
),
)
.toList(),
// Add image button
GestureDetector(
onTap: () => controller.pickImageFromCamera(),
child: Container(
height: 80.h,
width: 80.w,
padding: EdgeInsets.all(22),
decoration: BoxDecoration(
color: Color(0xFFE9E9E9),
border: Border.all(
width: 1,
color: AppColor.blackLightHover,
),
borderRadius: BorderRadius.circular(8),
),
child: Assets.vec.galleryAddSvg.svg(
width: 36,
height: 36,
),
),
),
],
),
),
),
Text(
'تعداد موجود فعلی',
style: AppFonts.yekan14.copyWith(color: AppColor.textColorLight),
),
],
),
),
cardInfo(
padding: EdgeInsets.fromLTRB(12, 12, 12, 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 9,
children: [
Text(
'وضعیت بهداشتی سالن',
style: AppFonts.yekan14Bold.copyWith(color: AppColor.textColor2),
),
ObxValue((data) {
return Row(
spacing: 8,
children: [
Expanded(
child: containerChips(
selectedIndex: data.value,
index: 0,
label: 'عالی',
onTap: (index) => controller
.setSanitaryConditionOfTheHallIndex(index, 'عالی'),
),
),
Expanded(
child: containerChips(
selectedIndex: data.value,
index: 1,
label: 'خوب',
onTap: (index) => controller
.setSanitaryConditionOfTheHallIndex(index, 'خوب'),
),
),
Expanded(
child: containerChips(
selectedIndex: data.value,
index: 2,
label: 'متوسط',
onTap: (index) => controller
.setSanitaryConditionOfTheHallIndex(index, 'متوسط'),
),
),
Expanded(
child: containerChips(
selectedIndex: data.value,
index: 3,
label: 'ضعیف',
onTap: (index) => controller
.setSanitaryConditionOfTheHallIndex(index, 'ضعیف'),
),
),
],
);
}, controller.sanitaryConditionOfTheHallIndex),
],
),
),
cardInfo(
padding: EdgeInsets.fromLTRB(12, 12, 12, 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 9,
children: [
Text(
'وضعیت تهویه',
style: AppFonts.yekan14Bold.copyWith(color: AppColor.textColor2),
),
ObxValue((data) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 8,
children: [
Expanded(
child: containerChips(
onTap: (index) =>
controller.setVentilationStatusIndex(index, 'عالی'),
selectedIndex: data.value,
index: 0,
label: 'عالی',
),
),
Expanded(
child: containerChips(
onTap: (index) =>
controller.setVentilationStatusIndex(index, 'خوب'),
selectedIndex: data.value,
index: 1,
label: 'خوب',
),
),
Expanded(
child: containerChips(
onTap: (index) =>
controller.setVentilationStatusIndex(index, 'متوسط'),
selectedIndex: data.value,
index: 2,
label: 'متوسط',
),
),
Expanded(
child: containerChips(
onTap: (index) =>
controller.setVentilationStatusIndex(index, 'ضعیف'),
selectedIndex: data.value,
index: 3,
label: 'ضعیف',
),
),
],
);
}, controller.ventilationStatusIndex),
],
),
),
cardInfo(
padding: EdgeInsets.fromLTRB(12, 12, 12, 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 9,
children: [
Text(
'وضعیت بستر',
style: AppFonts.yekan14Bold.copyWith(color: AppColor.textColor2),
),
ObxValue((data) {
return Row(
spacing: 8,
children: [
Expanded(
child: containerChips(
onTap: (index) =>
controller.setBeddingStatusIndex(index, 'خشک'),
selectedIndex: data.value,
index: 0,
label: 'خشک',
),
),
Expanded(
child: containerChips(
onTap: (index) =>
controller.setBeddingStatusIndex(index, 'نیمه‌مرطوب'),
selectedIndex: data.value,
index: 1,
label: 'نیمه‌مرطوب',
),
),
Expanded(
child: containerChips(
onTap: (index) =>
controller.setBeddingStatusIndex(index, 'مرطوب'),
selectedIndex: data.value,
index: 2,
label: 'مرطوب',
),
),
],
);
}, controller.beddingStatusIndex),
],
),
),
RTextField(
controller: controller.hatchingTemperatureController,
label: 'دمای سالن',
filled: true,
filledColor: AppColor.bgLight,
maxLines: 1,
minLines: 1,
keyboardType: TextInputType.number,
),
cardInfo(
padding: EdgeInsets.fromLTRB(12, 12, 12, 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 9,
children: [
Text(
' آب مصرفی',
style: AppFonts.yekan14Bold.copyWith(color: AppColor.textColor2),
),
ObxValue((data) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 10,
children: [
Expanded(
child: containerChips(
onTap: (index) =>
controller.setWaterQualityIndex(index, 'چاه'),
selectedIndex: data.value,
index: 0,
label: 'چاه',
),
),
Expanded(
child: containerChips(
onTap: (index) =>
controller.setWaterQualityIndex(index, 'شهری'),
selectedIndex: data.value,
index: 1,
label: 'شهری',
),
),
Expanded(
child: containerChips(
onTap: (index) =>
controller.setWaterQualityIndex(index, 'تصفیه‌شده'),
selectedIndex: data.value,
index: 2,
label: 'تصفیه‌شده',
),
),
Expanded(
child: containerChips(
onTap: (index) =>
controller.setWaterQualityIndex(index, 'حمل تانکر'),
selectedIndex: data.value,
index: 3,
label: 'حمل تانکر',
),
),
],
);
}, controller.waterQualityIndex),
],
),
),
RTextField(
controller: controller.waterHardnessController,
label: 'درصد سختی آب (PPM)',
filled: true,
filledColor: AppColor.bgLight,
maxLines: 1,
minLines: 1,
keyboardType: TextInputType.numberWithOptions(decimal: true),
),
],
);
}
ChoiceChip formChips({
required int selectedIndex,
required int index,
required String label,
required Function(int) onTap,
}) {
return ChoiceChip(
selectedColor: AppColor.green1Normal,
labelStyle: index == selectedIndex
? AppFonts.yekan14Bold.copyWith(color: Colors.white)
: AppFonts.yekan14.copyWith(color: AppColor.textColor),
surfaceTintColor: Colors.white,
checkmarkColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: index == selectedIndex
? BorderSide.none
: BorderSide(width: 1, color: AppColor.blackLightHover),
),
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
label: Text(label),
selected: index == selectedIndex,
onSelected: (value) => onTap(index),
);
}
Column casualtiesInformation(CreateInspectionBottomSheetLogic controller) {
return Column(
spacing: 10,
children: [
SizedBox(height: 1.h),
RTextField(
controller: controller.normalLossesController,
label: 'تعداد تلفات عادی دوره',
filled: true,
filledColor: AppColor.bgLight,
keyboardType: TextInputType.number,
maxLines: 1,
minLines: 1,
),
RTextField(
controller: controller.abnormalLossesController,
label: 'تلفات غیرعادی',
filled: true,
filledColor: AppColor.bgLight,
keyboardType: TextInputType.number,
maxLines: 1,
minLines: 1,
),
ResourceOverlayDropdown(
items: controller.causeOfUnusualCasualtiesList,
onChanged: (item) => controller.setCauseOfUnusualCasualtiesIndex(item),
itemBuilder: (item) => Text(item),
labelBuilder: (selected) => Text(selected ?? 'علت تلفات غیرعادی'),
),
ObxValue((data) {
return Visibility(
visible: data.value,
child: RTextField(
controller: controller.otherCauseOfUnusualCasualtiesController,
label: 'علت تلفات غیرعادی',
filled: true,
filledColor: AppColor.bgLight,
maxLines: 1,
minLines: 1,
),
);
}, controller.isOthercauseOfUnusualCasualties),
ResourceOverlayDropdown(
items: controller.typeOfDiseaseList,
onChanged: (item) => controller.setTypeOfDiseaseIndex(item),
itemBuilder: (item) => Text(item),
labelBuilder: (selected) =>
Text(selected ?? 'نوع بیماری تشخیص داده‌شده / مشکوک'),
),
ObxValue((data) {
return Visibility(
visible: data.value,
child: RTextField(
controller: controller.otherTypeOfDiseaseController,
label: 'نوع بیماری',
filled: true,
filledColor: AppColor.bgLight,
maxLines: 1,
minLines: 1,
),
);
}, controller.isOtherTypeOfDiseaseSelected),
ResourceOverlayDropdown(
items: Resource.success(['انجام شد', 'انجام نشد']),
onChanged: (item) => controller.setSamplingDone(item),
itemBuilder: (item) => Text(item),
labelBuilder: (selected) => Text(selected ?? 'نمونه‌برداری انجام‌شده'),
),
ObxValue((data) {
return Visibility(
visible: data.value,
child: cardInfo(
padding: EdgeInsets.fromLTRB(12, 12, 12, 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 9,
children: [
Text(
'نوع نمونه',
style: AppFonts.yekan14Bold.copyWith(
color: AppColor.textColor2,
),
),
ObxValue((data) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 8,
children: [
Expanded(
child: containerChips(
onTap: (index) =>
controller.setSampleTypeIndex(index, 'لاشه'),
selectedIndex: data.value,
index: 0,
label: 'لاشه',
),
),
Expanded(
child: containerChips(
onTap: (index) =>
controller.setSampleTypeIndex(index, 'آب'),
selectedIndex: data.value,
index: 1,
label: 'آب',
),
),
Expanded(
child: containerChips(
onTap: (index) =>
controller.setSampleTypeIndex(index, 'دانه'),
selectedIndex: data.value,
index: 2,
label: 'دانه',
),
),
],
);
}, controller.sampleTypeIndex),
],
),
),
);
}, controller.samplingDone),
],
);
}
Column diseasesAndHealthInformation(
CreateInspectionBottomSheetLogic controller,
) {
return Column(
spacing: 10,
children: [
SizedBox(height: 1.h),
RTextField(
controller: controller.technicalHealthOfficerNameController,
label: 'نام مسئول فنی بهداشتی',
filled: true,
filledColor: AppColor.bgLight,
maxLines: 1,
),
RTextField(
controller: controller.technicalEngineeringOfficerNameController,
label: 'نام مسئول فنی نظام مهندسی',
filled: true,
filledColor: AppColor.bgLight,
maxLines: 1,
),
],
);
}
GestureDetector containerChips({
required int selectedIndex,
required int index,
required String label,
required Function(int) onTap,
}) {
return GestureDetector(
onTap: () => onTap(index),
child: Container(
height: 24.h,
decoration: BoxDecoration(
color: index == selectedIndex
? AppColor.green1Normal
: AppColor.whiteGreyNormal,
borderRadius: BorderRadius.circular(8),
border: index == selectedIndex
? Border.fromBorderSide(BorderSide.none)
: Border.all(width: 1, color: AppColor.blackLightHover),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 5,
children: [
if (index == selectedIndex)
Icon(Icons.check, color: Colors.white, size: 16),
Text(
label,
style: index == selectedIndex
? AppFonts.yekan14Bold.copyWith(color: Colors.white)
: AppFonts.yekan14.copyWith(color: AppColor.textColor),
),
],
),
),
);
}

View File

@@ -0,0 +1,517 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/card_info.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/step2_page.dart';
import 'package:rasadyar_core/core.dart';
import 'create_inspection_bottom_sheet_logic.dart';
Widget step3Page(CreateInspectionBottomSheetLogic controller) {
return SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: Column(
children: [
SizedBox(height: 35.h),
Container(
height: 350.h,
clipBehavior: Clip.none,
width: Get.width,
child: farmInfoWidget(
controller: controller,
title: 'نهاده و خوراک',
child: agriculturalInput(controller),
),
),
SizedBox(height: 30.h),
Container(
height: 600.h,
clipBehavior: Clip.none,
width: Get.width,
child: farmInfoWidget(
controller: controller,
title: 'زیرساخت و انرژی',
child: infrastructureAndEnergy(controller),
),
),
SizedBox(height: 24.h),
Container(
height: 310.h,
clipBehavior: Clip.none,
width: Get.width,
child: farmInfoWidget(
controller: controller,
title: 'نیروی انسانی',
child: humanResources(controller),
),
),
SizedBox(height: 24.h),
Container(
height: 320.h,
clipBehavior: Clip.none,
width: Get.width,
child: farmInfoWidget(
controller: controller,
title: 'تسهیلات و حمایت‌ها',
child: facilitiesAndSupport(controller),
),
),
SizedBox(height: 24.h),
],
),
);
}
Column infrastructureAndEnergy(CreateInspectionBottomSheetLogic controller) {
return Column(
spacing: 10,
children: [
SizedBox(height: 1.h),
RTextField(
controller: controller.generatorTypeController,
label: 'نوع ژنراتور',
filled: true,
filledColor: AppColor.bgLight,
maxLines: 1,
),
RTextField(
controller: controller.generatorModelController,
label: 'مدل ژنراتور',
filled: true,
filledColor: AppColor.bgLight,
maxLines: 1,
),
RTextField(
controller: controller.generatorCountController,
label: 'تعداد ژنراتور',
filled: true,
filledColor: AppColor.bgLight,
maxLines: 1,
keyboardType: TextInputType.number,
),
ResourceOverlayDropdown(
items: Resource.success(['گازوئیل', 'نفت گاز']),
itemBuilder: (item) => Text(item),
labelBuilder: (selected) => Text(selected ?? 'نوع سوخت'),
onChanged: (item) => controller.setFuelType(item),
),
RTextField(
controller: controller.generatorCapacityController,
label: 'ظرفیت (KVA)',
filled: true,
filledColor: AppColor.bgLight,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
SeparatorInputFormatter(),
],
maxLines: 1,
),
cardInfo(
padding: EdgeInsets.fromLTRB(12, 12, 12, 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 9,
children: [
Text(
'وضعیت عملکرد ژنراتور',
style: AppFonts.yekan14Bold.copyWith(color: AppColor.textColor2),
),
ObxValue((data) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 10,
children: [
Expanded(
child: containerChips(
onTap: (index) => controller
.setGeneratorOperatingStatusIndex(index, 'سالم'),
selectedIndex: data.value,
index: 0,
label: 'سالم',
),
),
Expanded(
child: containerChips(
onTap: (index) => controller
.setGeneratorOperatingStatusIndex(index, 'نیمه‌سالم'),
selectedIndex: data.value,
index: 1,
label: 'نیمه‌سالم',
),
),
Expanded(
child: containerChips(
onTap: (index) => controller
.setGeneratorOperatingStatusIndex(index, 'معیوب'),
selectedIndex: data.value,
index: 2,
label: 'معیوب',
),
),
],
);
}, controller.generatorOperatingStatusIndex),
],
),
),
RTextField(
controller: controller.emergencyFuelInventoryController,
label: 'میزان موجودی سوخت اضطراری (لیتر)',
filled: true,
filledColor: AppColor.bgLight,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
SeparatorInputFormatter(),
],
maxLines: 1,
),
ResourceOverlayDropdown(
items: Resource.success(['ندارد', 'دارد']),
onChanged: (item) => controller.setPowerCutHistory(item),
itemBuilder: (item) => Text(item),
labelBuilder: (selected) =>
Text(selected ?? 'سابقه قطعی برق دوره جاری'),
),
RTextField(
controller: controller.powerCutDurationController,
label: 'مدت قطعی',
filled: true,
filledColor: AppColor.bgLight,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
SeparatorInputFormatter(),
],
maxLines: 1,
),
RTextField(
controller: controller.powerCutHourController,
label: 'ساعت قطعی',
filled: true,
filledColor: AppColor.bgLight,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
SeparatorInputFormatter(),
],
maxLines: 1,
),
RTextField(
controller: controller.additionalNotesController,
label: 'توضیحات تکمیلی',
filled: true,
maxLines: 5,
minLines: 5,
filledColor: AppColor.bgLight,
),
],
);
}
Column agriculturalInput(CreateInspectionBottomSheetLogic controller) {
return Column(
spacing: 10,
children: [
SizedBox.shrink(),
ResourceOverlayDropdown(
items: controller.inputStatusList,
itemBuilder: (item) => Text(item),
labelBuilder: (selected) => Text(selected ?? 'وضعیت نهاده'),
onChanged: (item) => controller.setInputStatus(item),
),
ObxValue((data) {
if (data.value == 'وابسته') {
return RTextField(
controller: controller.companyNameController,
label: 'نام کارخانه',
filled: true,
filledColor: AppColor.bgLight,
maxLines: 1,
);
} else {
return RTextField(
controller: controller.trackingCodeController,
label: 'شناسه خرید یا کد پیگیری',
filled: true,
filledColor: AppColor.bgLight,
maxLines: 1,
);
}
}, controller.inputStatus),
RTextField(
controller: controller.inputInventoryUntilVisitController,
label: 'موجودی نهاده مصرفی تا روز بازدید (کیلوگرم)',
filled: true,
filledColor: AppColor.bgLight,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
SeparatorInputFormatter(),
],
maxLines: 1,
),
ResourceOverlayDropdown(
items: controller.typeOfGrainList,
itemBuilder: (item) => Text(item),
labelBuilder: (selected) => Text(selected ?? 'نوع دان'),
onChanged: (item) => controller.setTypeOfGrain(item),
),
RTextField(
controller: controller.inputInventoryInWarehouseController,
label: 'موجودی نهاده موجود در انبار (کیلوگرم)',
filled: true,
filledColor: AppColor.bgLight,
maxLines: 1,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
SeparatorInputFormatter(),
],
),
cardInfo(
padding: EdgeInsets.fromLTRB(12, 12, 12, 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 9,
children: [
Text(
'کیفیت دانه',
style: AppFonts.yekan14Bold.copyWith(color: AppColor.textColor2),
),
ObxValue((data) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 10,
children: [
Expanded(
child: containerChips(
onTap: (index) =>
controller.setGrainQualityInput(index, 'متوسط'),
selectedIndex: data.value,
index: 0,
label: 'خوب',
),
),
Expanded(
child: containerChips(
onTap: (index) =>
controller.setGrainQualityInput(index, 'خوب'),
selectedIndex: data.value,
index: 1,
label: 'متوسط',
),
),
Expanded(
child: containerChips(
onTap: (index) =>
controller.setGrainQualityInput(index, 'ضعیف'),
selectedIndex: data.value,
index: 2,
label: 'ضعیف',
),
),
],
);
}, controller.grainQualityInputIndex),
],
),
),
],
);
}
Column humanResources(CreateInspectionBottomSheetLogic controller) {
return Column(
spacing: 10,
children: [
SizedBox(height: 1.h),
RTextField(
controller: controller.employedWorkersCountController,
label: 'تعداد افراد شاغل',
filled: true,
filledColor: AppColor.bgLight,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
SeparatorInputFormatter(),
],
maxLines: 1,
),
RTextField(
controller: controller.nativeWorkersCountController,
label: 'تعداد افراد بومی',
filled: true,
filledColor: AppColor.bgLight,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
SeparatorInputFormatter(),
],
maxLines: 1,
),
RTextField(
controller: controller.nonNativeWorkersCountController,
label: 'تعداد افراد غیر بومی',
filled: true,
filledColor: AppColor.bgLight,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
SeparatorInputFormatter(),
],
maxLines: 1,
),
cardInfo(
padding: EdgeInsets.fromLTRB(12, 12, 12, 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 9,
children: [
Text(
'وضعیت قرارداد کارگران',
style: AppFonts.yekan14Bold.copyWith(color: AppColor.textColor2),
),
ObxValue((data) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 8,
children: [
Expanded(
child: containerChips(
onTap: (index) => controller.setWorkerContractStatusIndex(
index,
'دائم',
),
selectedIndex: data.value,
index: 0,
label: 'دائم',
),
),
Expanded(
child: containerChips(
onTap: (index) => controller.setWorkerContractStatusIndex(
index,
'موقت',
),
selectedIndex: data.value,
index: 1,
label: 'موقت',
),
),
Expanded(
child: containerChips(
onTap: (index) => controller.setWorkerContractStatusIndex(
index,
'روزمزدی',
),
selectedIndex: data.value,
index: 2,
label: 'روزمزدی',
),
),
],
);
}, controller.workerContractStatusIndex),
],
),
),
ResourceOverlayDropdown(
items: Resource.success(['بله', 'خیر']),
onChanged: (item) => controller.setTrainingStatus(item),
itemBuilder: (item) => Text(item),
labelBuilder: (selected) =>
Text(selected ?? 'آموزش‌دیده در حوزه بهداشت و امنیت زیستی'),
),
],
);
}
Column facilitiesAndSupport(CreateInspectionBottomSheetLogic controller) {
return Column(
spacing: 10,
children: [
SizedBox(height: 1.h),
RTextField(
controller: controller.activeFacilityController,
label: 'تسهیلات دریافتی فعال',
filled: true,
filledColor: AppColor.bgLight,
maxLines: 1,
),
RTextField(
controller: controller.facilityTypeController,
label: 'نوع تسهیلات',
filled: true,
filledColor: AppColor.bgLight,
maxLines: 1,
),
RTextField(
controller: controller.facilityAmountController,
label: 'مبلغ',
filled: true,
filledColor: AppColor.bgLight,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
SeparatorInputFormatter(),
],
maxLines: 1,
),
RTextField(
controller: controller.paymentYearController,
label: 'سال دریافت',
filled: true,
readonly: true,
onTap: () => controller.showPaymentYearDatePicker(),
filledColor: AppColor.bgLight,
maxLines: 1,
),
ResourceOverlayDropdown(
items: Resource.success(['دارای معوقه', 'منظم']),
onChanged: (item) => controller.setOverdueStatus(item),
itemBuilder: (item) => Text(item),
labelBuilder: (selected) => Text(selected ?? 'وضعیت بازپرداخت'),
),
ResourceOverlayDropdown(
items: Resource.success(['نهاده', 'تسهیلات', 'واکسن', 'تجهیزات']),
onChanged: (item) => controller.setNewBeneficiaryRequest(item),
itemBuilder: (item) => Text(item),
labelBuilder: (selected) => Text(selected ?? 'درخواست جدید بهره بردار'),
),
],
);
}

View File

@@ -0,0 +1,495 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/card_info.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/step2_page.dart';
import 'package:rasadyar_core/core.dart';
import 'create_inspection_bottom_sheet_logic.dart';
Widget step4Page(CreateInspectionBottomSheetLogic controller) {
return SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: Column(
children: [
SizedBox(height: 35.h),
Container(
height: 430.h,
clipBehavior: Clip.none,
width: Get.width,
child: farmInfoWidget(
controller: controller,
title: 'مستندات',
child: documents(controller),
),
),
SizedBox(height: 30.h),
Container(
height: 235.h,
clipBehavior: Clip.none,
width: Get.width,
child: farmInfoWidget(
controller: controller,
title: 'جمع‌بندی بازرس',
child: inspectorConclusion(controller),
),
),
SizedBox(height: 24.h),
],
),
);
}
Column documents(CreateInspectionBottomSheetLogic controller) {
return Column(
spacing: 10,
children: [
cardInfo(
padding: EdgeInsets.fromLTRB(0, 12, 12, 6),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 9,
children: [
SizedBox(height: 10.h),
Obx(
() => SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
spacing: 8,
children: [
...controller.hallImages.entries
.map(
(entry) => Stack(
children: [
Container(
height: 80.h,
width: 80.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: Border.all(
width: 1,
color: AppColor.blackLightHover,
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.file(
File(entry.key.path),
fit: BoxFit.cover,
),
),
),
// Delete button
Positioned(
top: -4,
right: -4,
child: GestureDetector(
onTap: () =>
controller.removeImage(entry.key),
child: Container(
width: 24,
height: 24,
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: Icon(
Icons.close,
color: Colors.white,
size: 16,
),
),
),
),
// Upload indicator
if (entry.value == false)
Positioned.fill(
child: Container(
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.5),
borderRadius: BorderRadius.circular(8),
),
child: Center(
child: SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor:
AlwaysStoppedAnimation<Color>(
Colors.white,
),
),
),
),
),
),
],
),
)
.toList(),
// Add image button
GestureDetector(
onTap: () => controller.pickImageFromCamera(),
child: Container(
height: 80.h,
width: 80.w,
padding: EdgeInsets.all(22),
decoration: BoxDecoration(
color: Color(0xFFE9E9E9),
border: Border.all(
width: 1,
color: AppColor.blackLightHover,
),
borderRadius: BorderRadius.circular(8),
),
child: Assets.vec.galleryAddSvg.svg(
width: 36,
height: 36,
),
),
),
],
),
),
),
RichText(
text: TextSpan(
children: [
TextSpan(
text: 'ثبت عکس سالن (حداقل ۳ زاویه) ',
style: AppFonts.yekan14.copyWith(
color: AppColor.textColorLight,
),
),
TextSpan(
text: '*',
style: AppFonts.yekan14.copyWith(color: Colors.red),
),
],
),
),
],
),
),
cardInfo(
padding: EdgeInsets.fromLTRB(0, 12, 12, 6),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 9,
children: [
Obx(
() => SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
spacing: 8,
children: [
...controller.inputWarehouseImages.entries
.map(
(entry) => Stack(
children: [
Container(
height: 80.h,
width: 80.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: Border.all(
width: 1,
color: AppColor.blackLightHover,
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.file(
File(entry.key.path),
fit: BoxFit.cover,
),
),
),
// Delete button
Positioned(
top: -4,
right: -4,
child: GestureDetector(
onTap: () => controller
.removeInputWarehouseImage(entry.key),
child: Container(
width: 24,
height: 24,
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: Icon(
Icons.close,
color: Colors.white,
size: 16,
),
),
),
),
// Upload indicator
if (entry.value == false)
Positioned.fill(
child: Container(
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.5),
borderRadius: BorderRadius.circular(8),
),
child: Center(
child: SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor:
AlwaysStoppedAnimation<Color>(
Colors.white,
),
),
),
),
),
),
],
),
)
.toList(),
// Add image button
GestureDetector(
onTap: () => controller.pickImageFromCamera(),
child: Container(
height: 80.h,
width: 80.w,
padding: EdgeInsets.all(22),
decoration: BoxDecoration(
color: Color(0xFFE9E9E9),
border: Border.all(
width: 1,
color: AppColor.blackLightHover,
),
borderRadius: BorderRadius.circular(8),
),
child: Assets.vec.galleryAddSvg.svg(
width: 36,
height: 36,
),
),
),
],
),
),
),
Text(
'ثبت عکس انبار نهاده‌ها',
style: AppFonts.yekan14.copyWith(color: AppColor.textColorLight),
),
],
),
),
cardInfo(
padding: EdgeInsets.fromLTRB(0, 12, 12, 6),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 9,
children: [
Obx(
() => SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
spacing: 8,
children: [
...controller.lossesImages.entries
.map(
(entry) => Stack(
children: [
Container(
height: 80.h,
width: 80.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: Border.all(
width: 1,
color: AppColor.blackLightHover,
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.file(
File(entry.key.path),
fit: BoxFit.cover,
),
),
),
// Delete button
Positioned(
top: -4,
right: -4,
child: GestureDetector(
onTap: () => controller
.removeInputWarehouseImage(entry.key),
child: Container(
width: 24,
height: 24,
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: Icon(
Icons.close,
color: Colors.white,
size: 16,
),
),
),
),
// Upload indicator
if (entry.value == false)
Positioned.fill(
child: Container(
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.5),
borderRadius: BorderRadius.circular(8),
),
child: Center(
child: SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor:
AlwaysStoppedAnimation<Color>(
Colors.white,
),
),
),
),
),
),
],
),
)
.toList(),
// Add image button
GestureDetector(
onTap: () => controller.pickImageFromCamera(),
child: Container(
height: 80.h,
width: 80.w,
padding: EdgeInsets.all(22),
decoration: BoxDecoration(
color: Color(0xFFE9E9E9),
border: Border.all(
width: 1,
color: AppColor.blackLightHover,
),
borderRadius: BorderRadius.circular(8),
),
child: Assets.vec.galleryAddSvg.svg(
width: 36,
height: 36,
),
),
),
],
),
),
),
Text(
'ثبت عکس تلفات',
style: AppFonts.yekan14.copyWith(color: AppColor.textColorLight),
),
],
),
),
],
);
}
Column inspectorConclusion(CreateInspectionBottomSheetLogic controller) {
return Column(
spacing: 10,
children: [
SizedBox(height: 1.h),
cardInfo(
padding: EdgeInsets.fromLTRB(12, 12, 12, 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 9,
children: [
Text(
'وضعیت کلی واحد',
style: AppFonts.yekan14Bold.copyWith(color: AppColor.textColor2),
),
ObxValue((data) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 8,
children: [
Expanded(
child: containerChips(
onTap: (index) => controller.setInspectorConclusionIndex(
index,
'تایید شده',
),
selectedIndex: data.value,
index: 0,
label: 'تایید شده',
),
),
Expanded(
child: containerChips(
onTap: (index) => controller.setInspectorConclusionIndex(
index,
'نیازمند اصلاح',
),
selectedIndex: data.value,
index: 1,
label: 'نیازمند اصلاح',
),
),
Expanded(
child: containerChips(
onTap: (index) => controller.setInspectorConclusionIndex(
index,
'پرریسک',
),
selectedIndex: data.value,
index: 2,
label: 'پرریسک',
),
),
],
);
}, controller.inspectorConclusionIndex),
],
),
),
RTextField(
height: 100.h,
controller: controller.inspectorConclusionDescriptionController,
hintText: 'توصیه‌ها / اخطارها / اقدامات اصلاحی ...',
hintStyle: AppFonts.yekan14.copyWith(color: AppColor.textColorLight),
maxLines: 5,
minLines: 5,
filled: true,
filledColor: AppColor.bgLight,
),
],
);
}

View File

@@ -0,0 +1,599 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/create_inspection_bottom_sheet_logic.dart';
import 'package:rasadyar_core/core.dart';
Widget step5Page(CreateInspectionBottomSheetLogic controller) {
return SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: detailsWidget(controller),
);
}
Widget detailsWidget(CreateInspectionBottomSheetLogic controller) {
return Column(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 4,
vertical: 10,
),
child: Text(
'جزییات',
style: AppFonts.yekan18Bold.copyWith(
color: AppColor.iconColor,
),
),
),
],
),
Divider(color: AppColor.blackLightHover, height: 1, thickness: 1),
ObxValue((data) {
return tabBarWidget(
['اطلاعات', 'پاییش سلامت', 'زیرساخت', 'مستندات'],
controller.selectedTabIndex.value,
(index) => controller.changeTab(index),
);
}, controller.selectedTabIndex),
ObxValue((data) {
switch (data.value) {
case 0:
return infoTable();
case 1:
return healthTable();
case 2:
return infrastructureTable();
case 3:
return documentsTable();
default:
return infoTable();
}
}, controller.selectedTabIndex),
],
),
// Expanded در سطح بالاتر
],
);
}
Column infoTable() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(height: 10),
Row(
children: [
Text(
'مشخصات کلی',
style: AppFonts.yekan16Bold.copyWith(color: AppColor.iconColor),
),
],
),
SizedBox(height: 10),
Container(
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: Column(
children: [
rTableRow(title: 'نام واحد مرغداری', value: 'لذیذ'),
rTableRow(title: 'کد یکتا / شناسه واحد', value: '2541415'),
rTableRow(title: 'نام مالک / بهره‌بردار', value: 'مرغداری احمدی'),
rTableRow(title: 'موجودی سوخت اضطراری', value: '200 لیتر'),
rTableRow(title: 'شهر/تعاونی', value: 'خرم آباد/تعاونی خرم آباد'),
rTableRow(title: 'شماره تلفن واحد', value: '021-12345678'),
rTableRow(title: 'دامپزشک فارم', value: 'dd dd (05218569685)'),
rTableRow(title: 'سالن', value: '2'),
rTableRow(
title: 'تاریخ ثبت جوجه ریزی',
value: '1402/09/19 (10:12)',
),
rTableRow(title: 'شهر/تعاونی', value: 'خرم آباد/تعاونی خرم آباد'),
rTableRow(title: 'شماره تلفن واحد', value: '021-12345678'),
rTableRow(title: 'دامپزشک فارم', value: 'dd dd (05218569685)'),
rTableRow(title: 'سالن', value: '2'),
rTableRow(
title: 'تاریخ ثبت جوجه ریزی',
value: '1402/09/19 (10:12)',
),
],
),
),
],
);
}
Row rTableRow({String? title, String? value}) {
return Row(
children: [
Expanded(
flex: 1,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 9, vertical: 11),
alignment: Alignment.centerRight,
decoration: BoxDecoration(
color: AppColor.bgLight2,
border: Border(
bottom: BorderSide(color: AppColor.blackLightHover, width: 1),
),
),
child: Text(
title ?? '',
style: AppFonts.yekan14Bold.copyWith(color: AppColor.iconColor),
),
),
),
Expanded(
flex: 1,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 9, vertical: 11),
alignment: Alignment.centerRight,
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(color: AppColor.blackLightHover, width: 1),
),
),
child: Text(
value ?? '',
style: AppFonts.yekan14Bold.copyWith(color: AppColor.textColor),
),
),
),
],
);
}
Widget tabBarWidget(
List<String> tabs,
int selectedIndex,
Function(int) onTabSelected,
) {
return SizedBox(
height: 38.h,
width: Get.width,
child: Stack(
fit: StackFit.expand,
children: [
Positioned(
right: 0,
top: 0,
bottom: 0,
child: Row(
children: [
...tabs.map(
(tab) => GestureDetector(
onTap: () => onTabSelected(tabs.indexOf(tab)),
behavior: HitTestBehavior.opaque,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 11),
decoration: BoxDecoration(
border: tab == tabs[selectedIndex]
? Border(
bottom: BorderSide(
color: AppColor.blueNormalOld,
width: 3,
),
)
: null,
),
child: Text(
tab,
style: AppFonts.yekan12Bold.copyWith(
color: tab == tabs[selectedIndex]
? AppColor.blueNormalOld
: AppColor.mediumGrey,
),
),
),
),
),
],
),
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Divider(
color: AppColor.blackLightHover,
height: 1,
thickness: 1,
),
),
],
),
);
}
Widget healthTable() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(height: 10),
Row(
children: [
Text(
'پاییش سلامت',
style: AppFonts.yekan16Bold.copyWith(color: AppColor.iconColor),
),
],
),
SizedBox(height: 10),
Container(
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: Column(
children: [
rTableRow(title: 'نام واحد مرغداری', value: 'لذیذ'),
rTableRow(title: 'کد یکتا / شناسه واحد', value: '2541415'),
rTableRow(title: 'نام مالک / بهره‌بردار', value: 'مرغداری احمدی'),
rTableRow(title: 'موجودی سوخت اضطراری', value: '200 لیتر'),
rTableRow(title: 'شهر/تعاونی', value: 'خرم آباد/تعاونی خرم آباد'),
rTableRow(title: 'شماره تلفن واحد', value: '021-12345678'),
rTableRow(title: 'دامپزشک فارم', value: 'dd dd (05218569685)'),
rTableRow(title: 'سالن', value: '2'),
rTableRow(
title: 'تاریخ ثبت جوجه ریزی',
value: '1402/09/19 (10:12)',
),
rTableRow(title: 'شهر/تعاونی', value: 'خرم آباد/تعاونی خرم آباد'),
rTableRow(title: 'شماره تلفن واحد', value: '021-12345678'),
rTableRow(title: 'دامپزشک فارم', value: 'dd dd (05218569685)'),
rTableRow(title: 'سالن', value: '2'),
rTableRow(
title: 'تاریخ ثبت جوجه ریزی',
value: '1402/09/19 (10:12)',
),
],
),
),
],
);
}
Widget infrastructureTable() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(height: 10),
Row(
children: [
Text(
'زیرساخت',
style: AppFonts.yekan16Bold.copyWith(color: AppColor.iconColor),
),
],
),
SizedBox(height: 10),
Container(
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: Column(
children: [
rTableRow(title: 'نام واحد مرغداری', value: 'لذیذ'),
rTableRow(title: 'کد یکتا / شناسه واحد', value: '2541415'),
rTableRow(title: 'نام مالک / بهره‌بردار', value: 'مرغداری احمدی'),
rTableRow(title: 'موجودی سوخت اضطراری', value: '200 لیتر'),
rTableRow(title: 'شهر/تعاونی', value: 'خرم آباد/تعاونی خرم آباد'),
rTableRow(title: 'شماره تلفن واحد', value: '021-12345678'),
rTableRow(title: 'دامپزشک فارم', value: 'dd dd (05218569685)'),
rTableRow(title: 'سالن', value: '2'),
rTableRow(
title: 'تاریخ ثبت جوجه ریزی',
value: '1402/09/19 (10:12)',
),
rTableRow(title: 'شهر/تعاونی', value: 'خرم آباد/تعاونی خرم آباد'),
rTableRow(title: 'شماره تلفن واحد', value: '021-12345678'),
rTableRow(title: 'دامپزشک فارم', value: 'dd dd (05218569685)'),
rTableRow(title: 'سالن', value: '2'),
rTableRow(
title: 'تاریخ ثبت جوجه ریزی',
value: '1402/09/19 (10:12)',
),
],
),
),
],
);
}
Widget documentsTable() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(height: 10),
Row(
children: [
Text(
'مستندات',
style: AppFonts.yekan16Bold.copyWith(color: AppColor.iconColor),
),
],
),
SizedBox(height: 16),
Container(
height: 135.h,
width: Get.width,
decoration: BoxDecoration(
color: AppColor.bgLight,
borderRadius: BorderRadius.circular(8),
),
child: Column(
spacing: 10,
children: [
SizedBox(
height: 100.h,
width: Get.width,
child: ListView.separated(
itemCount: 10,
padding: EdgeInsets.symmetric(horizontal: 12),
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return Container(
width: 80.w,
height: 80.h,
decoration: BoxDecoration(
color: Color(0x33000000),
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(
"https://picsum.photos/150/150?random=$index",
),
),
borderRadius: BorderRadius.circular(8),
),
child: Stack(
fit: StackFit.expand,
children: [
Positioned(
top: 6,
left: 6,
child: Container(
width: 24.w,
height: 24.h,
padding: EdgeInsets.all(4),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.80),
borderRadius: BorderRadius.circular(4),
),
child: Assets.vec.trashSvg.svg(
width: 16.w,
height: 16.h,
colorFilter: ColorFilter.mode(
AppColor.redNormal,
BlendMode.srcIn,
),
),
),
),
],
),
);
},
separatorBuilder: (context, index) => SizedBox(width: 10),
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 18),
child: Row(
children: [
Text(
'200 فارم در این سالن تخمین زده شده است.',
textAlign: TextAlign.right,
style: AppFonts.yekan14Bold.copyWith(
color: AppColor.textColor,
),
),
],
),
),
],
),
),
SizedBox(height: 16),
Container(
height: 135.h,
width: Get.width,
decoration: BoxDecoration(
color: AppColor.bgLight,
borderRadius: BorderRadius.circular(8),
),
child: Column(
spacing: 10,
children: [
SizedBox(
height: 100.h,
width: Get.width,
child: ListView.separated(
itemCount: 10,
padding: EdgeInsets.symmetric(horizontal: 12),
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return Container(
width: 80.w,
height: 80.h,
decoration: BoxDecoration(
color: Color(0x33000000),
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(
"https://picsum.photos/150/150?random=${index * 2 + 1}",
),
),
borderRadius: BorderRadius.circular(8),
),
child: Stack(
fit: StackFit.expand,
children: [
Positioned(
top: 6,
left: 6,
child: Container(
width: 24.w,
height: 24.h,
padding: EdgeInsets.all(4),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.80),
borderRadius: BorderRadius.circular(4),
),
child: Assets.vec.trashSvg.svg(
width: 16.w,
height: 16.h,
colorFilter: ColorFilter.mode(
AppColor.redNormal,
BlendMode.srcIn,
),
),
),
),
],
),
);
},
separatorBuilder: (context, index) => SizedBox(width: 10),
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 18),
child: Row(
children: [
Text(
'انبار نهاده ها',
textAlign: TextAlign.right,
style: AppFonts.yekan14Bold.copyWith(
color: AppColor.textColor,
),
),
],
),
),
],
),
),
SizedBox(height: 16),
Container(
height: 135.h,
width: Get.width,
decoration: BoxDecoration(
color: AppColor.bgLight,
borderRadius: BorderRadius.circular(8),
),
child: Column(
spacing: 10,
children: [
SizedBox(
height: 100.h,
width: Get.width,
child: ListView.separated(
itemCount: 10,
padding: EdgeInsets.symmetric(horizontal: 12),
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return Container(
width: 80.w,
height: 80.h,
decoration: BoxDecoration(
color: Color(0x33000000),
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(
"https://picsum.photos/150/150?random=${index * 3 + 1}",
),
),
borderRadius: BorderRadius.circular(8),
),
child: Stack(
fit: StackFit.expand,
children: [
Positioned(
top: 6,
left: 6,
child: Container(
width: 24.w,
height: 24.h,
padding: EdgeInsets.all(4),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.80),
borderRadius: BorderRadius.circular(4),
),
child: Assets.vec.trashSvg.svg(
width: 16.w,
height: 16.h,
colorFilter: ColorFilter.mode(
AppColor.redNormal,
BlendMode.srcIn,
),
),
),
),
],
),
);
},
separatorBuilder: (context, index) => SizedBox(width: 10),
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 18),
child: Row(
children: [
Text(
'تلفات',
textAlign: TextAlign.right,
style: AppFonts.yekan14Bold.copyWith(
color: AppColor.textColor,
),
),
],
),
),
],
),
),
],
);
}

View File

@@ -1,10 +1,15 @@
import 'package:rasadyar_chicken/features/common/presentation/page/profile/logic.dart';
import 'package:rasadyar_chicken/presentation/widget/base_page/logic.dart';
import 'package:rasadyar_core/core.dart';
class GlobalBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => BaseLogic(), fenix: true);
// Register ChickenBaseLogic and also as BasePageLogic/BaseLogic for compatibility
// Since ChickenBaseLogic extends BasePageLogic, we can use the same instance
Get.lazyPut<ChickenBaseLogic>(() => ChickenBaseLogic(), fenix: true);
// Register the same instance as BasePageLogic/BaseLogic
Get.lazyPut<BasePageLogic>(() => Get.find<ChickenBaseLogic>(), fenix: true);
Get.lazyPut(() => ProfileLogic(), fenix: true);
//root logics