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.

729 lines
28KB

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