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.

712 lines
27KB

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