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.

461 lines
21KB

  1. import 'dart:convert';
  2. import 'package:farm_tpf/custom_model/Spraying.dart';
  3. import 'package:farm_tpf/custom_model/SuppliesUsing.dart';
  4. import 'package:farm_tpf/data/api/app_exception.dart';
  5. import 'package:farm_tpf/data/repository/repository.dart';
  6. import 'package:farm_tpf/presentation/custom_widgets/app_bar_widget.dart';
  7. import 'package:farm_tpf/presentation/custom_widgets/bloc/media_helper_bloc.dart';
  8. import 'package:farm_tpf/presentation/custom_widgets/button_widget.dart';
  9. import 'package:farm_tpf/presentation/custom_widgets/widget_loading.dart';
  10. import 'package:farm_tpf/presentation/custom_widgets/widget_media_picker.dart';
  11. import 'package:farm_tpf/presentation/custom_widgets/widget_text_field_description.dart';
  12. import 'package:farm_tpf/presentation/custom_widgets/widget_text_form_field.dart';
  13. import 'package:farm_tpf/presentation/custom_widgets/widget_utils.dart';
  14. import 'package:farm_tpf/presentation/screens/actions/bloc/action_detail_bloc.dart';
  15. import 'package:farm_tpf/presentation/screens/actions/controller/ChangeFieldInForm.dart';
  16. import 'package:farm_tpf/presentation/screens/actions/controller/ChangeSupplyUsing.dart';
  17. import 'package:farm_tpf/presentation/screens/actions/spraying/widget_spraying_supply.dart';
  18. import 'package:farm_tpf/presentation/screens/actions/state_management_helper/change_file_controller.dart';
  19. import 'package:farm_tpf/presentation/screens/actions/state_management_helper/change_supply.dart';
  20. import 'package:farm_tpf/utils/const_common.dart';
  21. import 'package:farm_tpf/utils/const_string.dart';
  22. import 'package:farm_tpf/utils/const_style.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_bloc/flutter_bloc.dart';
  27. import 'package:flutter_datetime_picker/flutter_datetime_picker.dart';
  28. import 'package:get/get.dart';
  29. import 'package:intl/intl.dart';
  30. import 'package:keyboard_dismisser/keyboard_dismisser.dart';
  31. import 'package:pattern_formatter/pattern_formatter.dart';
  32. import 'package:farm_tpf/utils/formatter.dart';
  33. import '../util_action.dart';
  34. class EditActionSprayingScreen extends StatefulWidget {
  35. final int cropId;
  36. final bool isEdit;
  37. final int? activityId;
  38. EditActionSprayingScreen({required this.cropId, this.isEdit = false, this.activityId});
  39. @override
  40. _EditActionSprayingScreenState createState() => _EditActionSprayingScreenState();
  41. }
  42. class _EditActionSprayingScreenState extends State<EditActionSprayingScreen> {
  43. final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
  44. final _repository = Repository();
  45. GlobalKey<FormState> _formKey = GlobalKey();
  46. bool _autoValidate = false;
  47. Spraying _spraying = Spraying();
  48. var pref = LocalPref();
  49. final _descriptionController = TextEditingController();
  50. final _purposeController = TextEditingController();
  51. final _weatherController = TextEditingController();
  52. final _executeByController = TextEditingController();
  53. final _quarantinePeriodController = TextEditingController();
  54. List<SuppliesUsing> suppliesUsing = <SuppliesUsing>[];
  55. String executeTimeView = '';
  56. DateTime executeTime = DateTime.now();
  57. String resultAtView = '';
  58. DateTime resultAtTime = DateTime.now();
  59. List<String> filePaths = <String>[];
  60. var changeFileController = Get.put(ChangeFileController());
  61. Future<Null> getSharedPrefs() async {
  62. var currentFullName = await pref.getString(DATA_CONST.CURRENT_FULL_NAME);
  63. _executeByController.text = currentFullName ?? "";
  64. }
  65. @override
  66. void initState() {
  67. super.initState();
  68. getSharedPrefs();
  69. changeFileController.initValue();
  70. _spraying.suppliesUsing = <SuppliesUsing>[];
  71. _spraying.executeDate = executeTime.convertLocalDateTimeToStringUtcDateTime();
  72. executeTimeView = executeTime.displayDateTime_DDMMYYYY_HHmm();
  73. _spraying.cropId = widget.cropId;
  74. }
  75. _validateInputs() async {
  76. if (_formKey.currentState!.validate()) {
  77. _formKey.currentState!.save();
  78. LoadingDialog.showLoadingDialog(context);
  79. filePaths = Get.find<ChangeFileController>().newFiles;
  80. var newSups = <SuppliesUsing>[];
  81. suppliesUsing.forEach((sup) {
  82. var newSup = sup;
  83. newSup.suppliesInWarehouseId = sup.tbSuppliesInWarehouseId;
  84. newSup.equipmentOfCustomerId = sup.tbEquipmentOfCustomerId;
  85. newSups.add(newSup);
  86. });
  87. _spraying.suppliesUsing = newSups;
  88. _spraying.mediaDel = Get.find<ChangeFileController>().deleteFiles;
  89. var activitySpraying = jsonEncode(_spraying.toJson()).toString();
  90. //ADD NEW
  91. if (_spraying.activityId == null) {
  92. _repository.createAction((value) {
  93. LoadingDialog.hideLoadingDialog(context);
  94. Get.back(result: value);
  95. Utils.showSnackBarSuccess(message: label_add_success);
  96. }, (error) {
  97. LoadingDialog.hideLoadingDialog(context);
  98. Utils.showSnackBarError(message: AppException.handleError(error));
  99. },
  100. apiAddAction: ConstCommon.apiAddSpraying,
  101. paramActivity: ConstCommon.paramsActionSpraying,
  102. activityAction: activitySpraying,
  103. filePaths: filePaths);
  104. } else {
  105. //UPDATE
  106. _repository.updateAction((value) {
  107. LoadingDialog.hideLoadingDialog(context);
  108. Get.back(result: value);
  109. Utils.showSnackBarSuccess(message: label_update_success);
  110. }, (error) {
  111. LoadingDialog.hideLoadingDialog(context);
  112. Utils.showSnackBarError(message: AppException.handleError(error));
  113. },
  114. apiUpdateAction: ConstCommon.apiUpdateSpraying,
  115. paramActivity: ConstCommon.paramsActionSpraying,
  116. activityAction: activitySpraying,
  117. filePaths: filePaths);
  118. }
  119. } else {
  120. _autoValidate = true;
  121. }
  122. }
  123. Widget _btnExecuteTimePicker() {
  124. return FlatButton(
  125. padding: const EdgeInsets.only(top: 0.0, right: 0.0, bottom: 0.0, left: 0.0),
  126. onPressed: () {
  127. DatePicker.showDateTimePicker(context, showTitleActions: true, onChanged: (date) {}, onConfirm: (date) {
  128. setState(() {
  129. if (resultAtTime != null && date.isAfter(resultAtTime)) {
  130. Utils.showSnackBarWarning(message: label_execute_date_must_after_result_time);
  131. } else {
  132. executeTime = date;
  133. _spraying.executeDate = executeTime.convertLocalDateTimeToStringUtcDateTime();
  134. executeTimeView = executeTime.displayDateTime_DDMMYYYY_HHmm();
  135. }
  136. });
  137. }, currentTime: executeTime, locale: LocaleType.vi);
  138. },
  139. child: Container(
  140. padding: const EdgeInsets.only(top: 0.0, right: 0.0, bottom: 10.5, left: 0.0),
  141. decoration: const BoxDecoration(
  142. border: kBorderTextField,
  143. ),
  144. child: Row(
  145. children: [
  146. Expanded(
  147. child: Text(
  148. //TODO: check condition
  149. executeTimeView == null ? "$executeTime" : executeTimeView,
  150. style: const TextStyle(fontSize: 14.0, color: Colors.black87),
  151. )),
  152. const Icon(
  153. Icons.date_range,
  154. color: Colors.grey,
  155. ),
  156. ],
  157. )));
  158. }
  159. Widget _btnResultAtTimePicker() {
  160. return FlatButton(
  161. padding: const EdgeInsets.only(top: 0.0, right: 0.0, bottom: 0.0, left: 0.0),
  162. onPressed: () {
  163. DatePicker.showDateTimePicker(context, showTitleActions: true, onChanged: (date) {}, onConfirm: (date) {
  164. setState(() {
  165. if (executeTime.isAfter(date)) {
  166. Utils.showSnackBarWarning(message: label_execute_date_must_after_result_time);
  167. } else {
  168. resultAtTime = date;
  169. _spraying.resultAt = resultAtTime.convertLocalDateTimeToStringUtcDateTime();
  170. resultAtView = resultAtTime.displayDateTime_DDMMYYYY_HHmm();
  171. }
  172. });
  173. }, currentTime: resultAtTime, locale: LocaleType.vi);
  174. },
  175. child: Container(
  176. padding: const EdgeInsets.only(top: 0.0, right: 0.0, bottom: 10.5, left: 0.0),
  177. decoration: const BoxDecoration(
  178. border: kBorderTextField,
  179. ),
  180. child: Row(
  181. children: [
  182. Expanded(
  183. child: Text(
  184. resultAtView == null ? "" : resultAtView,
  185. style: const TextStyle(fontSize: 14.0, color: Colors.black87),
  186. )),
  187. const Icon(
  188. Icons.date_range,
  189. color: Colors.grey,
  190. ),
  191. ],
  192. )));
  193. }
  194. Widget _purposeField() {
  195. return TextFormField(
  196. keyboardType: TextInputType.text,
  197. decoration: const InputDecoration(labelText: "Lý do sử dụng"),
  198. controller: _purposeController,
  199. onSaved: (newValue) {
  200. _spraying.purpose = newValue ?? '';
  201. },
  202. );
  203. }
  204. Widget _quarantinePeriodField() {
  205. return WidgetTextFormFieldNumber(
  206. hintValue: "Thời gian cách ly",
  207. textController: _quarantinePeriodController,
  208. onSaved: (newValue) {
  209. _spraying.quarantinePeriod = (newValue ?? '0').parseDoubleThousand();
  210. },
  211. validator: (_) {},
  212. onChanged: (_) {},
  213. );
  214. }
  215. Widget _weatherField() {
  216. return TextFormField(
  217. keyboardType: TextInputType.text,
  218. decoration: const InputDecoration(labelText: "Thời tiết"),
  219. controller: _weatherController,
  220. onSaved: (newValue) {
  221. _spraying.weatherConditions = newValue ?? '';
  222. },
  223. );
  224. }
  225. Widget _desciptionField() {
  226. return TextFieldDescriptionWidget(
  227. controller: _descriptionController,
  228. onSaved: (newValue) {
  229. _spraying.description = newValue;
  230. },
  231. );
  232. }
  233. Widget _executeByField() {
  234. return TextFormField(
  235. keyboardType: TextInputType.text,
  236. decoration: const InputDecoration(labelText: "Người thực hiện"),
  237. enabled: false,
  238. controller: _executeByController,
  239. onSaved: (newValue) {},
  240. );
  241. }
  242. @override
  243. Widget build(BuildContext context) => KeyboardDismisser(
  244. gestures: [
  245. GestureType.onTap,
  246. GestureType.onPanUpdateDownDirection,
  247. ],
  248. child: Scaffold(
  249. backgroundColor: Colors.white,
  250. key: _scaffoldKey,
  251. appBar: AppBarWidget(
  252. isBack: true,
  253. action: InkWell(
  254. child: const Text(
  255. 'Huỷ',
  256. style: TextStyle(color: Colors.red, fontWeight: FontWeight.normal),
  257. ),
  258. onTap: () {
  259. if (Get.isSnackbarOpen) Get.back();
  260. Get.back();
  261. },
  262. ),
  263. ),
  264. body: KeyboardDismisser(
  265. child: MultiBlocProvider(
  266. providers: [
  267. BlocProvider<ActionDetailBloc>(
  268. create: (context) => ActionDetailBloc(repository: Repository())
  269. ..add(
  270. FetchData(
  271. isNeedFetchData: widget.isEdit,
  272. apiActivity: ConstCommon.apiDetailSpraying,
  273. activityId: widget.activityId ?? -1,
  274. ),
  275. ),
  276. ),
  277. BlocProvider<MediaHelperBloc>(
  278. create: (context) => MediaHelperBloc()..add(ChangeListMedia(items: [])),
  279. )
  280. ],
  281. child: Form(
  282. key: _formKey,
  283. // autovalidate: _autoValidate,
  284. child: SafeArea(
  285. child: SingleChildScrollView(
  286. child: BlocConsumer<ActionDetailBloc, ActionDetailState>(
  287. listener: (context, state) async {
  288. if (state is ActionDetailFailure) {
  289. LoadingDialog.hideLoadingDialog(context);
  290. } else if (state is ActionDetailSuccess) {
  291. LoadingDialog.hideLoadingDialog(context);
  292. _spraying = Spraying.fromJson(state.item);
  293. _spraying.activityId = widget.activityId ?? -1;
  294. _purposeController.text = _spraying.purpose ?? "";
  295. _quarantinePeriodController.text = _spraying.quarantinePeriod?.formatNumtoStringDecimal() ?? '';
  296. _weatherController.text = _spraying.weatherConditions ?? "";
  297. _descriptionController.text = _spraying.description ?? '';
  298. _executeByController.text = _spraying.executeBy ?? '';
  299. executeTime = _spraying.executeDate?.convertStringServerDateTimeToLocalDateTime() ?? DateTime.now();
  300. executeTimeView = executeTime.displayDateTime_DDMMYYYY_HHmm();
  301. resultAtTime = _spraying.resultAt?.convertStringServerDateTimeToLocalDateTime() ?? DateTime.now();
  302. resultAtView = resultAtTime.displayDateTime_DDMMYYYY_HHmm();
  303. //Show media
  304. if (Validators.stringNotNullOrEmpty(_spraying.media ?? '')) {
  305. BlocProvider.of<MediaHelperBloc>(context)
  306. .add(ChangeListMedia(items: UtilAction.convertFilePathToMedia(_spraying.media ?? '')));
  307. }
  308. //list supply
  309. suppliesUsing = _spraying.suppliesUsing ?? <SuppliesUsing>[];
  310. Get.find<ChangeSupplyUsing>().changeInitList(suppliesUsing);
  311. } else if (state is ActionDetailInitial) {
  312. print("init");
  313. } else if (state is ActionDetailLoading) {
  314. print("loading");
  315. LoadingDialog.showLoadingDialog(context);
  316. }
  317. },
  318. builder: (context, state) {
  319. return Column(
  320. children: [
  321. Padding(
  322. padding: const EdgeInsets.all(8.0),
  323. child: Column(
  324. crossAxisAlignment: CrossAxisAlignment.start,
  325. children: <Widget>[
  326. const Text(
  327. plot_action_spraying,
  328. style: TextStyle(fontWeight: FontWeight.w500, fontSize: 22),
  329. ),
  330. const SizedBox(
  331. height: 8.0,
  332. ),
  333. Container(
  334. width: double.infinity,
  335. child: const Text(
  336. "Ngày thực hiện *",
  337. style: TextStyle(color: Colors.black54, fontSize: 13.0),
  338. ),
  339. ),
  340. _btnExecuteTimePicker(),
  341. const SizedBox(
  342. height: 8.0,
  343. ),
  344. _purposeField(),
  345. const SizedBox(
  346. height: 8.0,
  347. ),
  348. _quarantinePeriodField(),
  349. const SizedBox(
  350. height: 8.0,
  351. ),
  352. Container(
  353. width: double.infinity,
  354. child: const Text(
  355. "Thời gian ghi nhận kết quả",
  356. style: TextStyle(color: Colors.black54, fontSize: 13.0),
  357. ),
  358. ),
  359. _btnResultAtTimePicker(),
  360. const SizedBox(
  361. height: 8.0,
  362. ),
  363. _weatherField(),
  364. const SizedBox(
  365. height: 8.0,
  366. ),
  367. _desciptionField(),
  368. const SizedBox(
  369. height: 8.0,
  370. ),
  371. _executeByField(),
  372. const SizedBox(
  373. height: 8.0,
  374. ),
  375. ],
  376. ),
  377. ),
  378. Container(
  379. width: double.infinity,
  380. height: 16,
  381. color: Colors.grey[200],
  382. ),
  383. WidgetSprayingSupply(
  384. currentItems: [],
  385. onChangeSupplies: (value) {
  386. suppliesUsing = value;
  387. }),
  388. Container(
  389. width: double.infinity,
  390. height: 16,
  391. color: Colors.grey[200],
  392. ),
  393. const SizedBox(
  394. height: 8.0,
  395. ),
  396. BlocBuilder<MediaHelperBloc, MediaHelperState>(builder: (context, state) {
  397. if (state is MediaHelperSuccess) {
  398. return WidgetMediaPicker(
  399. currentItems: state.items,
  400. onChangeFiles: (newPathFiles, deletePathFiles) async {
  401. Get.find<ChangeFileController>().change(newPathFiles, deletePathFiles);
  402. });
  403. } else {
  404. return const Center(child: CircularProgressIndicator());
  405. }
  406. }),
  407. Padding(
  408. padding: const EdgeInsets.all(8.0),
  409. child: ButtonWidget(
  410. title: 'CẬP NHẬT',
  411. onPressed: () {
  412. var currentFocus = FocusScope.of(context);
  413. if (!currentFocus.hasPrimaryFocus) {
  414. currentFocus.unfocus();
  415. }
  416. if (Get.find<ChangeFieldFormSupply>().isChanged) {
  417. Utils.showDialogConfirmSupply(onConfirm: () {
  418. Get.back();
  419. _validateInputs();
  420. });
  421. } else {
  422. _validateInputs();
  423. }
  424. }),
  425. ),
  426. ],
  427. );
  428. },
  429. ),
  430. ),
  431. ))))));
  432. @override
  433. void dispose() {
  434. _quarantinePeriodController.dispose();
  435. _purposeController.dispose();
  436. _weatherController.dispose();
  437. _descriptionController.dispose();
  438. _executeByController.dispose();
  439. super.dispose();
  440. }
  441. }