You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

611 lines
24KB

  1. import 'dart:convert';
  2. import 'package:farm_tpf/custom_model/SuppliesUsing.dart';
  3. import 'package:farm_tpf/custom_model/action_form/ActionUIField.dart';
  4. import 'package:farm_tpf/custom_model/action_form/ActionUIForm.dart';
  5. import 'package:farm_tpf/custom_model/action_form/CommonData.dart';
  6. import 'package:farm_tpf/custom_model/action_form/RequestActivity.dart';
  7. import 'package:farm_tpf/data/api/app_exception.dart';
  8. import 'package:farm_tpf/data/repository/repository.dart';
  9. import 'package:farm_tpf/presentation/custom_widgets/app_bar_widget.dart';
  10. import 'package:farm_tpf/presentation/custom_widgets/bloc/media_helper_bloc.dart';
  11. import 'package:farm_tpf/presentation/custom_widgets/button_widget.dart';
  12. import 'package:farm_tpf/presentation/custom_widgets/dropdown_supply_widget.dart';
  13. import 'package:farm_tpf/presentation/custom_widgets/widget_action_field_date.dart';
  14. import 'package:farm_tpf/presentation/custom_widgets/widget_field_time_picker.dart';
  15. import 'package:farm_tpf/presentation/custom_widgets/widget_loading.dart';
  16. import 'package:farm_tpf/presentation/custom_widgets/widget_media_picker.dart';
  17. import 'package:farm_tpf/presentation/custom_widgets/widget_text_field_area.dart';
  18. import 'package:farm_tpf/presentation/custom_widgets/widget_text_form_field.dart';
  19. import 'package:farm_tpf/presentation/custom_widgets/widget_utils.dart';
  20. import 'package:farm_tpf/presentation/screens/actions/cubit/action_ui_cubit.dart';
  21. import 'package:farm_tpf/utils/const_string.dart';
  22. import 'package:farm_tpf/utils/pref.dart';
  23. import 'package:farm_tpf/utils/validators.dart';
  24. import 'package:flutter/material.dart';
  25. import 'package:flutter/scheduler.dart';
  26. import 'package:flutter_bloc/flutter_bloc.dart';
  27. import 'package:get/get.dart';
  28. import 'package:keyboard_dismisser/keyboard_dismisser.dart';
  29. import 'package:farm_tpf/utils/formatter.dart';
  30. import 'controller/ChangeFieldInForm.dart';
  31. import 'controller/ChangeSupplyUsing.dart';
  32. import 'controller/ChangeWorker.dart';
  33. import 'dung/widget_dung_supply.dart';
  34. import 'nursery/widget_worker.dart';
  35. import 'plant/widget_plant_supply.dart';
  36. import 'spraying/widget_spraying_supply.dart';
  37. import 'state_management_helper/change_dropdown_controller.dart';
  38. import 'state_management_helper/change_file_controller.dart';
  39. import 'util_action.dart';
  40. class ActionScreen extends StatefulWidget {
  41. final bool isEdit;
  42. final int cropId;
  43. final int idAction;
  44. final String activityType;
  45. final String title;
  46. final int activityId;
  47. ActionScreen(
  48. {@required this.isEdit,
  49. @required this.cropId,
  50. @required this.idAction,
  51. @required this.title,
  52. @required this.activityType,
  53. @required this.activityId});
  54. @override
  55. _ActionScreenState createState() => _ActionScreenState();
  56. }
  57. class _ActionScreenState extends State<ActionScreen> {
  58. var _formKey = GlobalKey<FormState>();
  59. var pref = LocalPref();
  60. final _executeByController = TextEditingController();
  61. DateTime executeTime = DateTime.now();
  62. List<String> filePaths = <String>[];
  63. var changeFileController = Get.put(ChangeFileController());
  64. Map<String, TextEditingController> textFieldControllers = {};
  65. Map<String, String> valueObjects = {};
  66. var _requestActivity = RequestActivity();
  67. final _repository = Repository();
  68. var _actionUIForm = ActionUIForm();
  69. var _nurseryDetails = <TbNurseryDetailsDTO>[];
  70. var _supplyUsings = <SuppliesUsing>[];
  71. Future<Null> getSharedPrefs() async {
  72. var currentFullName = await pref.getString(DATA_CONST.CURRENT_FULL_NAME);
  73. _executeByController.text = currentFullName ?? "";
  74. }
  75. @override
  76. void initState() {
  77. super.initState();
  78. getSharedPrefs();
  79. changeFileController.initValue();
  80. }
  81. _submitForm() async {
  82. switch (widget.activityType) {
  83. case 'ACTIVE_TYPE_NURSERY':
  84. if (Get.find<ChangeFieldFormSupply>().isChanged) {
  85. Utils.showDialog(
  86. title: 'Thông tin người thực hiện chưa cập nhật',
  87. message: 'Bạn có muốn cập nhật',
  88. textConfirm: 'Tiếp tục',
  89. textCancel: 'Xem lại',
  90. onConfirm: () {
  91. Get.back();
  92. _validateInputs();
  93. });
  94. } else {
  95. _validateInputs();
  96. }
  97. break;
  98. case 'ACTIVE_TYPE_PLANTING':
  99. case 'ACTIVE_TYPE_FERTILIZE':
  100. case 'ACTIVE_TYPE_SPRAYING_PESTICIDES':
  101. if (Get.find<ChangeFieldFormSupply>().isChanged) {
  102. Utils.showDialogConfirmSupply(onConfirm: () {
  103. Get.back();
  104. _validateInputs();
  105. });
  106. } else {
  107. _validateInputs();
  108. }
  109. break;
  110. default:
  111. _validateInputs();
  112. break;
  113. }
  114. }
  115. _validateInputs() async {
  116. if (_formKey.currentState.validate()) {
  117. _formKey.currentState.save();
  118. try {
  119. LoadingDialog.showLoadingDialog(context);
  120. filePaths = Get.find<ChangeFileController>().newFiles;
  121. //Create request general model
  122. _requestActivity
  123. ..tbActivityTypeId = widget.idAction
  124. ..tbCropId = widget.cropId;
  125. if (_actionUIForm.activityExtendTypeDTOList.isNotEmpty) {
  126. _requestActivity
  127. ..externalTable = _actionUIForm
  128. ?.activityExtendTypeDTOList?.first?.externalTable ??
  129. '';
  130. }
  131. filePaths = Get.find<ChangeFileController>().newFiles;
  132. textFieldControllers.forEach((key, value) {
  133. print(textFieldControllers[key].text);
  134. valueObjects[key] = textFieldControllers[key].text;
  135. });
  136. //tbObjectUpdateDTOList
  137. var _objectPrameters = <TbObjectUpdateDTO>[];
  138. valueObjects.forEach((key, value) {
  139. var objectUpdate = TbObjectUpdateDTO()
  140. ..tbObjectParameterId = int.tryParse(key)
  141. ..index = value;
  142. _objectPrameters.add(objectUpdate);
  143. });
  144. _requestActivity.tbObjectUpdateDTOList = _objectPrameters;
  145. //CHECK NURSERY
  146. if (widget.activityType == 'ACTIVE_TYPE_NURSERY') {
  147. _requestActivity.tbNurseryDetailsDTOList = _nurseryDetails;
  148. } else if (widget.activityType == 'ACTIVE_TYPE_PLANTING' ||
  149. widget.activityType == 'ACTIVE_TYPE_FERTILIZE' ||
  150. widget.activityType == 'ACTIVE_TYPE_SPRAYING_PESTICIDES') {
  151. _requestActivity.tbSuppliesUsingDetailsDTOs = _supplyUsings;
  152. }
  153. //convert data to json
  154. var activityCommonData =
  155. jsonEncode(_requestActivity.toJson()).toString();
  156. print(activityCommonData);
  157. if (widget.activityId < 0) {
  158. //ADD New
  159. _repository.createActionCommon((data) {
  160. LoadingDialog.hideLoadingDialog(context);
  161. Get.back(result: 'ok');
  162. Utils.showSnackBarSuccess(message: label_add_success);
  163. }, (error) {
  164. LoadingDialog.hideLoadingDialog(context);
  165. Utils.showSnackBarError(message: AppException.handleError(error));
  166. },
  167. activityType: widget.activityType,
  168. activityData: activityCommonData,
  169. filePaths: filePaths);
  170. } else {
  171. //UPDATE
  172. _repository.updateActionCommon((data) {
  173. LoadingDialog.hideLoadingDialog(context);
  174. Get.back(result: 'ok');
  175. Utils.showSnackBarSuccess(message: label_update_success);
  176. }, (error) {
  177. LoadingDialog.hideLoadingDialog(context);
  178. Utils.showSnackBarError(message: AppException.handleError(error));
  179. },
  180. activityType: widget.activityType,
  181. activityData: activityCommonData,
  182. activityId: widget.activityId,
  183. filePaths: filePaths);
  184. }
  185. //ADD NEW
  186. //Update
  187. } catch (e) {
  188. LoadingDialog.hideLoadingDialog(context);
  189. print(e.toString());
  190. }
  191. } else {
  192. //
  193. }
  194. }
  195. Widget _btnExecuteTimePicker() {
  196. return WidgetFieldDateTimePicker(
  197. initDateTime: executeTime,
  198. onUpdateDateTime: (selectedDate) {
  199. _requestActivity.executeDate =
  200. selectedDate.convertLocalDateTimeToStringUtcDateTime();
  201. });
  202. }
  203. Widget _executeByField() {
  204. return TextFormField(
  205. keyboardType: TextInputType.text,
  206. decoration: InputDecoration(labelText: "Người thực hiện"),
  207. enabled: false,
  208. controller: _executeByController,
  209. onSaved: (newValue) {},
  210. );
  211. }
  212. //
  213. // GENERATE DYNAMIC FORM
  214. //
  215. Widget generateTextField(List<ActionUIField> fields) {
  216. return Wrap(
  217. children: [
  218. ListView.separated(
  219. shrinkWrap: true,
  220. physics: NeverScrollableScrollPhysics(),
  221. itemCount: fields.length,
  222. separatorBuilder: (context, index) {
  223. return SizedBox(
  224. height: 8,
  225. );
  226. },
  227. itemBuilder: (context, index) {
  228. var field = fields[index];
  229. if (field.tbControlTypeName == 'text') {
  230. return TextFormField(
  231. keyboardType: TextInputType.text,
  232. decoration: InputDecoration(labelText: field.description),
  233. controller: textFieldControllers[field.id.toString()],
  234. onSaved: (newValue) {},
  235. validator: field.isMandatory
  236. ? (String value) {
  237. return Validators.validateNotNullOrEmpty(
  238. value, 'Vui lòng nhập ${field.description}');
  239. }
  240. : null,
  241. );
  242. } else if (field.tbControlTypeName == 'number') {
  243. return WidgetTextFormFieldNumber(
  244. hintValue: field.description,
  245. textController: textFieldControllers[field.id.toString()],
  246. onSaved: (newValue) {},
  247. validator: field.isMandatory
  248. ? (String value) {
  249. return Validators.validNumberOrEmpty(
  250. value, 'Vui lòng nhập ${field.description}');
  251. }
  252. : null,
  253. );
  254. } else if (field.tbControlTypeName == 'textarea') {
  255. return TextFieldAreaWidget(
  256. hint: field.description,
  257. controller: textFieldControllers[field.id.toString()],
  258. onSaved: (newValue) {});
  259. } else if (field.tbControlTypeName == 'dropdown' ||
  260. field.tbControlTypeName == 'radiobutton') {
  261. return DropdownSupplyWidget(
  262. titleName: field.description ?? '',
  263. tbSupply: field.tbActivityExtendTypeExternalTable ?? '',
  264. tag: field.name,
  265. value: field.description,
  266. hint:
  267. '${field.description} ${field.isMandatory ? '*' : ''}',
  268. condition: field.tbActivityExtendTypeCondition,
  269. invalidMessage: '',
  270. onPressed: (commonData) {
  271. valueObjects[field.id.toString()] =
  272. commonData.id.toString();
  273. });
  274. } else if (field.tbControlTypeName == 'date') {
  275. return FieldDateWidget(
  276. tag: field.name,
  277. value: field.description,
  278. hint:
  279. '${field.description} ${field.isMandatory ? '*' : ''}',
  280. invalidMessage: '',
  281. onPressed: (selectedDate) {
  282. valueObjects[field.id.toString()] = selectedDate
  283. .convertLocalDateTimeToStringUtcDateTime();
  284. });
  285. } else {
  286. return Container();
  287. }
  288. })
  289. ],
  290. );
  291. }
  292. //
  293. // GENERATE SUPPLY
  294. //
  295. Widget generateSupply(String activityType) {
  296. switch (activityType) {
  297. case 'ACTIVE_TYPE_NURSERY':
  298. return WidgetWorker(onChangeWorkers: (nurseryDetails) {
  299. _nurseryDetails = nurseryDetails;
  300. });
  301. break;
  302. case 'ACTIVE_TYPE_PLANTING':
  303. return Column(
  304. children: [
  305. Container(
  306. width: double.infinity,
  307. height: 16,
  308. color: Colors.grey[200],
  309. ),
  310. WidgetPlantSupply(
  311. currentItems: [],
  312. onChangeSupplies: (value) {
  313. _supplyUsings = value;
  314. }),
  315. ],
  316. );
  317. break;
  318. case 'ACTIVE_TYPE_FERTILIZE':
  319. return Column(
  320. children: [
  321. Container(
  322. width: double.infinity,
  323. height: 16,
  324. color: Colors.grey[200],
  325. ),
  326. WidgetDungSupply(
  327. currentItems: [],
  328. onChangeSupplies: (value) {
  329. _supplyUsings = value;
  330. }),
  331. ],
  332. );
  333. break;
  334. case 'ACTIVE_TYPE_SPRAYING_PESTICIDES':
  335. return Column(
  336. children: [
  337. Container(
  338. width: double.infinity,
  339. height: 16,
  340. color: Colors.grey[200],
  341. ),
  342. WidgetSprayingSupply(
  343. currentItems: [],
  344. onChangeSupplies: (value) {
  345. _supplyUsings = value;
  346. }),
  347. ],
  348. );
  349. break;
  350. default:
  351. return Container();
  352. break;
  353. }
  354. }
  355. void showDataWhenEdit() {
  356. //Show media
  357. if (Validators.stringNotNullOrEmpty(_requestActivity.media)) {
  358. BlocProvider.of<MediaHelperBloc>(context).add(ChangeListMedia(
  359. items: UtilAction.convertFilePathToMedia(_requestActivity.media)));
  360. }
  361. SchedulerBinding.instance.addPostFrameCallback((_) {
  362. if (widget.activityType == 'ACTIVE_TYPE_PLANTING' ||
  363. widget.activityType == 'ACTIVE_TYPE_FERTILIZE' ||
  364. widget.activityType == 'ACTIVE_TYPE_SPRAYING_PESTICIDES') {
  365. //list supply
  366. Get.find<ChangeSupplyUsing>()
  367. .changeInitList(_requestActivity.tbSuppliesUsingDetailsDTOs);
  368. } else if (widget.activityType == 'ACTIVE_TYPE_NURSERY') {
  369. //list nursery
  370. Get.find<ChangeWorker>()
  371. .changeInitList(_requestActivity.tbNurseryDetailsDTOList);
  372. }
  373. });
  374. //Show value textfield
  375. if (_requestActivity.tbObjectUpdateDTOList != null) {
  376. print(textFieldControllers.keys.toList());
  377. _requestActivity.tbObjectUpdateDTOList.forEach((element) {
  378. if (element.tbObjectParameterDTO.tbControlTypeName == 'text' ||
  379. element.tbObjectParameterDTO.tbControlTypeName == 'textarea') {
  380. SchedulerBinding.instance.addPostFrameCallback((_) {
  381. textFieldControllers[element.tbObjectParameterId.toString()].text =
  382. element.index;
  383. });
  384. } else if (element.tbObjectParameterDTO.tbControlTypeName == 'number') {
  385. SchedulerBinding.instance.addPostFrameCallback((_) {
  386. textFieldControllers[element.tbObjectParameterId.toString()].text =
  387. element.index.formatStringToStringDecimal();
  388. });
  389. } else {
  390. SchedulerBinding.instance.addPostFrameCallback((_) {
  391. print("SchedulerBinding");
  392. if (element.tbObjectParameterDTO.tbControlTypeName == 'dropdown' ||
  393. element.tbObjectParameterDTO.tbControlTypeName == 'radio') {
  394. var dropdownValueName = '';
  395. if (element.tbObjectParameterDTO
  396. .tbActivityExtendTypeDropDownDTOList.isNotEmpty ||
  397. element.tbObjectParameterDTO
  398. .tbActivityExtendTypeDropDownDTOList !=
  399. null) {
  400. element.tbObjectParameterDTO.tbActivityExtendTypeDropDownDTOList
  401. .forEach((dropdownData) {
  402. if (dropdownData.id == int.tryParse(element.index)) {
  403. dropdownValueName = dropdownData.name;
  404. }
  405. });
  406. }
  407. var commonData = CommonData()
  408. ..id = int.tryParse(element.index)
  409. ..name = dropdownValueName;
  410. Get.find<ChangeDropdownController>(
  411. tag: element.tbObjectParameterDTO.name)
  412. .change(commonData);
  413. } else if (element.tbObjectParameterDTO.tbControlTypeName ==
  414. 'date') {
  415. Get.find<ChangeDateTimePicker>(
  416. tag: element.tbObjectParameterDTO.name)
  417. .change(element.index
  418. .convertStringServerDateTimeToLocalDateTime());
  419. }
  420. });
  421. }
  422. });
  423. } else {
  424. //
  425. }
  426. }
  427. @override
  428. Widget build(BuildContext context) => KeyboardDismisser(
  429. gestures: [
  430. GestureType.onTap,
  431. GestureType.onPanUpdateDownDirection,
  432. ],
  433. child: Scaffold(
  434. backgroundColor: Colors.white,
  435. // key: _scaffoldKey,
  436. appBar: AppBarWidget(
  437. isBack: true,
  438. action: InkWell(
  439. child: Text(
  440. 'Lưu',
  441. style: TextStyle(
  442. color: Colors.red, fontWeight: FontWeight.normal),
  443. ),
  444. onTap: () {
  445. FocusScopeNode currentFocus = FocusScope.of(context);
  446. if (!currentFocus.hasPrimaryFocus) {
  447. currentFocus.unfocus();
  448. }
  449. _submitForm();
  450. },
  451. ),
  452. ),
  453. body: MultiBlocProvider(
  454. providers: [
  455. BlocProvider<ActionUiCubit>(
  456. create: (context) =>
  457. ActionUiCubit(repository: Repository())
  458. ..getActionUIForm(
  459. actionId: widget.idAction,
  460. actionType: widget.activityType,
  461. isEdit: widget.isEdit,
  462. activityId: widget.activityId)),
  463. BlocProvider<MediaHelperBloc>(
  464. create: (context) =>
  465. MediaHelperBloc()..add(ChangeListMedia(items: [])),
  466. )
  467. ],
  468. child: Form(
  469. key: _formKey,
  470. child: SafeArea(
  471. child: SingleChildScrollView(
  472. child: BlocBuilder<ActionUiCubit, ActionUiState>(
  473. builder: (context, state) {
  474. if (state is ActionUiLoading) {
  475. print('loading...');
  476. return Center(child: CircularProgressIndicator());
  477. } else if (state is ActionUiSuccess) {
  478. _actionUIForm = state.actionUIForm;
  479. _requestActivity = state.activityDetail;
  480. //CREATE UI
  481. _actionUIForm.objectParameterDTOList
  482. .forEach((element) {
  483. //generate controller
  484. if (element.tbControlTypeName == 'text' ||
  485. element.tbControlTypeName == 'number' ||
  486. element.tbControlTypeName == 'textarea') {
  487. var textEditingController =
  488. new TextEditingController();
  489. textFieldControllers.putIfAbsent(
  490. element.id.toString(),
  491. () => textEditingController);
  492. }
  493. // generate value each parameter
  494. valueObjects.putIfAbsent(
  495. element.id.toString(), () => '');
  496. });
  497. //SHOW EDIT DATA
  498. if (widget.isEdit) {
  499. showDataWhenEdit();
  500. }
  501. return Column(
  502. children: [
  503. Padding(
  504. padding: const EdgeInsets.all(8.0),
  505. child: Column(
  506. children: <Widget>[
  507. Container(
  508. width: double.infinity,
  509. child: Text(
  510. "Ngày thực hiện *",
  511. style: TextStyle(
  512. color: Colors.black54,
  513. fontSize: 13.0),
  514. ),
  515. ),
  516. _btnExecuteTimePicker(),
  517. SizedBox(
  518. height: 8.0,
  519. ),
  520. generateTextField(_actionUIForm
  521. .objectParameterDTOList),
  522. _executeByField(),
  523. SizedBox(
  524. height: 8.0,
  525. ),
  526. ],
  527. ),
  528. ),
  529. generateSupply(widget.activityType),
  530. Container(
  531. width: double.infinity,
  532. height: 16,
  533. color: Colors.grey[200],
  534. ),
  535. BlocBuilder<MediaHelperBloc,
  536. MediaHelperState>(
  537. builder: (context, state) {
  538. if (state is MediaHelperSuccess) {
  539. return WidgetMediaPicker(
  540. currentItems: state.items,
  541. onChangeFiles: (newPathFiles,
  542. deletePathFiles) async {
  543. Get.find<ChangeFileController>()
  544. .change(newPathFiles,
  545. deletePathFiles);
  546. });
  547. } else {
  548. return Center(
  549. child: CircularProgressIndicator());
  550. }
  551. }),
  552. Padding(
  553. padding: const EdgeInsets.all(8.0),
  554. child: ButtonWidget(
  555. title: 'CẬP NHẬT',
  556. onPressed: () {
  557. FocusScopeNode currentFocus =
  558. FocusScope.of(context);
  559. if (!currentFocus.hasPrimaryFocus) {
  560. currentFocus.unfocus();
  561. }
  562. _submitForm();
  563. }),
  564. ),
  565. ],
  566. );
  567. } else if (state is ActionUiFailure) {
  568. return Text(state.errorString);
  569. }
  570. return Container();
  571. },
  572. ),
  573. ),
  574. )),
  575. )));
  576. @override
  577. void dispose() {
  578. _executeByController.dispose();
  579. textFieldControllers.forEach((key, value) {
  580. textFieldControllers[key].dispose();
  581. });
  582. super.dispose();
  583. }
  584. }