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.

692 lines
26KB

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