Browse Source

refactor: update dynamic API for supplier and change logo

bugfix/20250501
tranleanhquoc 3 months ago
parent
commit
346356ea9c
32 changed files with 1255 additions and 263 deletions
  1. +6
    -6
      ios/Runner.xcodeproj/project.pbxproj
  2. +38
    -1
      lib/custom_model/CropPlot.dart
  3. +1
    -1
      lib/custom_model/ErrorCommon.dart
  4. +9
    -0
      lib/custom_model/action_form/ActionUIForm.dart
  5. +72
    -0
      lib/custom_model/action_form/ActivityTypeDTO.dart
  6. +16
    -1
      lib/data/api/app_exception.dart
  7. +4
    -4
      lib/data/api/rest_client.dart
  8. +4
    -5
      lib/data/api/rest_client.g.dart
  9. +18
    -9
      lib/data/repository/repository.dart
  10. +1
    -1
      lib/presentation/custom_widgets/widget_loading.dart
  11. +1
    -2
      lib/presentation/screens/actions/cubit/action_ui_cubit.dart
  12. +3
    -3
      lib/presentation/screens/actions/dung/widget_dung_supply.dart
  13. +3
    -1
      lib/presentation/screens/actions/harvest_process/widget_harvest_process_supply.dart
  14. +3
    -2
      lib/presentation/screens/actions/plant/widget_plant_supply.dart
  15. +221
    -162
      lib/presentation/screens/actions/sc_action.dart
  16. +3
    -2
      lib/presentation/screens/actions/spraying/widget_spraying_supply.dart
  17. +733
    -0
      lib/presentation/screens/actions/supply_widget.dart
  18. +9
    -7
      lib/presentation/screens/login/login_page.dart
  19. +0
    -1
      lib/presentation/screens/notification/sc_notification.dart
  20. +2
    -3
      lib/presentation/screens/plot_detail/sc_plot_action.dart
  21. +2
    -2
      lib/presentation/screens/plot_detail/sc_plot_history.dart
  22. +66
    -17
      lib/presentation/screens/plot_detail/sc_plot_information.dart
  23. +5
    -5
      lib/presentation/screens/resources/bloc/supply_bloc.dart
  24. +6
    -6
      lib/presentation/screens/resources/bloc/supply_event.dart
  25. +12
    -12
      lib/presentation/screens/resources/sc_resource_helper.dart
  26. +4
    -4
      lib/presentation/screens/resources/widget_search.dart
  27. +1
    -1
      lib/presentation/screens/task/bloc/task_bloc.dart
  28. +4
    -1
      lib/presentation/screens/task/bloc/task_state.dart
  29. +3
    -1
      lib/utils/app_images.dart
  30. +2
    -0
      lib/utils/const_string.dart
  31. +2
    -2
      pubspec.lock
  32. +1
    -1
      pubspec.yaml

+ 6
- 6
ios/Runner.xcodeproj/project.pbxproj View File

@@ -376,7 +376,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 36;
CURRENT_PROJECT_VERSION = 37;
DEVELOPMENT_TEAM = C3DTD2JH94;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
@@ -393,7 +393,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
MARKETING_VERSION = 1.1.16;
MARKETING_VERSION = 1.1.17;
PRODUCT_BUNDLE_IDENTIFIER = vn.azteam.farmdemo;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -522,7 +522,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 36;
CURRENT_PROJECT_VERSION = 37;
DEVELOPMENT_TEAM = C3DTD2JH94;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
@@ -539,7 +539,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
MARKETING_VERSION = 1.1.16;
MARKETING_VERSION = 1.1.17;
PRODUCT_BUNDLE_IDENTIFIER = vn.azteam.farmdemo;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -560,7 +560,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 36;
CURRENT_PROJECT_VERSION = 37;
DEVELOPMENT_TEAM = C3DTD2JH94;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
@@ -577,7 +577,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
MARKETING_VERSION = 1.1.16;
MARKETING_VERSION = 1.1.17;
PRODUCT_BUNDLE_IDENTIFIER = vn.azteam.farmdemo;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";

+ 38
- 1
lib/custom_model/CropPlot.dart View File

@@ -10,6 +10,8 @@ class CropPlot {
int? numberPlants;
int? numberCurrentPlants;
String? endOfFarmingDate;
int? numberRemovals;
List<ObjectParameterDataDTOS>? objectParameterDataDTOS;

CropPlot({
this.tbCropDTO,
@@ -21,16 +23,26 @@ class CropPlot {
this.numberPlants,
this.numberCurrentPlants,
this.endOfFarmingDate,
this.numberRemovals,
this.objectParameterDataDTOS,
});

CropPlot.fromJson(Map<String, dynamic> json) {
tbCropDTO = json['tbCropDTO'] != null ? new TbCropDTO.fromJson(json['tbCropDTO']) : null;
tbCropDTO = json['tbCropDTO'] != null
? new TbCropDTO.fromJson(json['tbCropDTO'])
: null;
if (json['activities'] != null) {
activities = <Activities>[];
json['activities'].forEach((v) {
activities?.add(new Activities.fromJson(v));
});
}
if (json['objectParameterDataDTOS'] != null) {
objectParameterDataDTOS = <ObjectParameterDataDTOS>[];
json['objectParameterDataDTOS'].forEach((v) {
objectParameterDataDTOS?.add(new ObjectParameterDataDTOS.fromJson(v));
});
}
sowingDate = json['sowingDate'];
plantingDate = json['plantingDate'];
soakSeedsTime = json['soakSeedsTime'];
@@ -38,6 +50,7 @@ class CropPlot {
numberPlants = json['numberPlants'];
numberCurrentPlants = json['numberCurrentPlants'];
endOfFarmingDate = json['endOfFarmingDate'];
numberRemovals = json['numberRemovals'];
}

Map<String, dynamic> toJson() {
@@ -48,12 +61,36 @@ class CropPlot {
if (this.activities != null) {
data['activities'] = this.activities?.map((v) => v.toJson()).toList();
}
if (this.objectParameterDataDTOS != null) {
data['objectParameterDataDTOS'] =
this.objectParameterDataDTOS?.map((v) => v.toJson()).toList();
}
data['sowingDate'] = this.sowingDate;
data['soakSeedsTime'] = this.soakSeedsTime;
data['seedIncubationTime'] = this.seedIncubationTime;
data['numberPlants'] = this.numberPlants;
data['numberCurrentPlants'] = this.numberCurrentPlants;
data['endOfFarmingDate'] = this.endOfFarmingDate;
data['numberRemovals'] = this.numberRemovals;
return data;
}
}

class ObjectParameterDataDTOS {
String? name;
String? index;

ObjectParameterDataDTOS({this.name, this.index});

ObjectParameterDataDTOS.fromJson(Map<String, dynamic> json) {
name = json['name'];
index = json['index'];
}

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['name'] = this.name;
data['index'] = this.index;
return data;
}
}

+ 1
- 1
lib/custom_model/ErrorCommon.dart View File

@@ -1,7 +1,7 @@
class ErrorCommon {
String? entityName;
String? errorKey;
int? status;
dynamic status;

ErrorCommon({
this.entityName,

+ 9
- 0
lib/custom_model/action_form/ActionUIForm.dart View File

@@ -1,13 +1,19 @@
import 'package:farm_tpf/custom_model/action_form/ActivityTypeDTO.dart';

import 'ActionUIField.dart';
import 'ActionUISupply.dart';

class ActionUIForm {
ActivityTypeDTO? tbActivityTypeDTO;
List<ActionUIField>? objectParameterDTOList;
List<ActionUISupply>? activityExtendTypeDTOList;

ActionUIForm({this.objectParameterDTOList, this.activityExtendTypeDTOList});

ActionUIForm.fromJson(Map<String, dynamic> json) {
if (json['tbActivityTypeDTO'] != null) {
tbActivityTypeDTO = new ActivityTypeDTO.fromJson(json['tbActivityTypeDTO']);
}
if (json['objectParameterDTOList'] != null) {
objectParameterDTOList = <ActionUIField>[];
json['objectParameterDTOList'].forEach((v) {
@@ -24,6 +30,9 @@ class ActionUIForm {

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
if (this.tbActivityTypeDTO != null) {
data['tbActivityTypeDTO'] = this.tbActivityTypeDTO!.toJson();
}
if (this.objectParameterDTOList != null) {
data['objectParameterDTOList'] = this.objectParameterDTOList?.map((v) => v.toJson()).toList();
}

+ 72
- 0
lib/custom_model/action_form/ActivityTypeDTO.dart View File

@@ -0,0 +1,72 @@
class ActivityTypeDTO {
int? id;
String? name;
String? description;
bool? isCanDelete;
bool? isGuidelineUsing;
bool? isTriggerEvent;
int? position;
bool? isStart;
bool? isEnd;
String? supplyGroup;
String? guideType;
bool? isUseSupply;
String? urlSupply;
bool? isTraceability;
bool? afterHarvest;

ActivityTypeDTO(
{this.id,
this.name,
this.description,
this.isCanDelete,
this.isGuidelineUsing,
this.isTriggerEvent,
this.position,
this.isStart,
this.isEnd,
this.supplyGroup,
this.guideType,
this.isUseSupply,
this.urlSupply,
this.isTraceability,
this.afterHarvest});

ActivityTypeDTO.fromJson(Map<String, dynamic> json) {
id = json['id'];
name = json['name'];
description = json['description'];
isCanDelete = json['isCanDelete'];
isGuidelineUsing = json['isGuidelineUsing'];
isTriggerEvent = json['isTriggerEvent'];
position = json['position'];
isStart = json['isStart'];
isEnd = json['isEnd'];
supplyGroup = json['supplyGroup'];
guideType = json['guideType'];
isUseSupply = json['isUseSupply'];
urlSupply = json['urlSupply'];
isTraceability = json['isTraceability'];
afterHarvest = json['afterHarvest'];
}

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['name'] = this.name;
data['description'] = this.description;
data['isCanDelete'] = this.isCanDelete;
data['isGuidelineUsing'] = this.isGuidelineUsing;
data['isTriggerEvent'] = this.isTriggerEvent;
data['position'] = this.position;
data['isStart'] = this.isStart;
data['isEnd'] = this.isEnd;
data['supplyGroup'] = this.supplyGroup;
data['guideType'] = this.guideType;
data['isUseSupply'] = this.isUseSupply;
data['urlSupply'] = this.urlSupply;
data['isTraceability'] = this.isTraceability;
data['afterHarvest'] = this.afterHarvest;
return data;
}
}

+ 16
- 1
lib/data/api/app_exception.dart View File

@@ -16,10 +16,22 @@ class AppException {
if (statusCode == 400) {
errorDescription = customMessageError ?? exception_dio_400;
try {
Map<String, dynamic> errorMap = jsonDecode(dioError.response?.data);
final rawData = dioError.response?.data;
Map<String, dynamic> errorMap;

if (rawData is String) {
errorMap = json.decode(rawData);
} else if (rawData is Map) {
errorMap = Map<String, dynamic>.from(rawData);
} else {
throw Exception("Unsupported error format");
}
var errorCode = ErrorCommon.fromJson(errorMap).errorKey;

switch (errorCode) {
case '-1':
errorDescription = exception_dio_minus_1;
break;
case '1000':
errorDescription = exception_dio_1000;
break;
@@ -86,6 +98,9 @@ class AppException {
case '1021':
errorDescription = exception_dio_1021;
break;
case '9999':
errorDescription = exception_dio_9999;
break;
default:
errorDescription = customMessageError ?? exception_dio_400;
}

+ 4
- 4
lib/data/api/rest_client.dart View File

@@ -92,7 +92,7 @@ abstract class RestClient {
{@Path() int page = 0, @Path() int size = 20, @Path() String query = '', @DioOptions() Options? options});

//Crop
@GET("/api/tb-crops-detail-for-app/{cropId}")
@GET("/api/v2/tb-crops-detail-for-app/{cropId}")
Future<CropPlot> getCropDetail(@Path() int cropId, {@Path() int page = 0, @Path() int size = 20});

@GET("/api/tb-crops-scan-qrCode/{cropCode}")
@@ -107,9 +107,9 @@ abstract class RestClient {
@GET("/api/list-environment-updates-display/{cropId}?page={page}&size={size}")
Future<List<EnvironmentParameter>> getEnvironmentParameters(@Path() int cropId, {@Path() int page = 0, @Path() int size = 20});
//NEW Activity implement
@GET('/api/display-object-param-dynamic-form/{idAction}')
@GET('/api/v2/display-object-param-dynamic-form/{idAction}')
Future<ActionUIForm> getActionUIForm(@Path() idAction, @DioOptions() Options? options);

@GET('/api/get-detail-common-activity/{actionType}/{activityId}')
Future<RequestActivity> getDetailActivityCommon(@Path() actionType, @Path() activityId);
@GET('/api/v2/get-detail-common-activity/{activityId}')
Future<RequestActivity> getDetailActivityCommon(@Path() activityId);
}

+ 4
- 5
lib/data/api/rest_client.g.dart View File

@@ -337,7 +337,7 @@ class _RestClient implements RestClient {
queryParameters.removeWhere((k, v) => v == null);
final _data = <String, dynamic>{};
var _result = await _dio.request(
'/api/tb-crops-detail-for-app/$cropId',
'/api/v2/tb-crops-detail-for-app/$cropId',
queryParameters: queryParameters,
data: _data,
);
@@ -440,20 +440,19 @@ class _RestClient implements RestClient {
newOptions.extra.addAll(_extra);
newOptions.headers.addAll(<String, dynamic>{});
final _result =
await _dio.request<Map<String, dynamic>>('/api/display-object-param-dynamic-form/$idAction', queryParameters: queryParameters, data: _data);
await _dio.request<Map<String, dynamic>>('/api/v2/display-object-param-dynamic-form/$idAction', queryParameters: queryParameters, data: _data);
final value = ActionUIForm.fromJson(_result.data ?? Map<String, dynamic>());
return value;
}

@override
getDetailActivityCommon(actionType, activityId) async {
ArgumentError.checkNotNull(actionType, 'actionType');
getDetailActivityCommon(activityId) async {
ArgumentError.checkNotNull(activityId, 'activityId');
const _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
queryParameters.removeWhere((k, v) => v == null);
final _data = <String, dynamic>{};
final _result = await _dio.request<Map<String, dynamic>>('/api/get-detail-common-activity/$actionType/$activityId',
final _result = await _dio.request<Map<String, dynamic>>('/api/v2/get-detail-common-activity/$activityId',
queryParameters: queryParameters, data: _data);
final value = RequestActivity.fromJson(_result.data ?? Map<String, dynamic>());
return value;

+ 18
- 9
lib/data/repository/repository.dart View File

@@ -125,10 +125,19 @@ class Repository {
return value;
}

Future<List<Supply>> getSupplies(String type, {String query = ""}) async {
final client = RestClient(dio);
Future<List<Supply>> getSupplies(String urlSupply, {String query = ""}) async {
var op = buildConfigurableCacheOptions(forceRefresh: true, maxAge: Duration(days: ConstCommon.kMaxAgeCache));
return client.getSupplies(type, query: query, options: op);
try {
final result = await dio.get("${ConstCommon.baseUrl}$urlSupply?q=$query", options: op);
final data = result.data;
if (data is List) {
return data.map((i) => Supply.fromJson(i as Map<String, dynamic>)).toList();
} else {
return [];
}
} on DioError catch (e) {
return [];
}
}

Future<List<Device>> getDeviceForActivity() async {
@@ -260,7 +269,7 @@ class Repository {
}

Future<void> createActionCommon(Function(dynamic) onSuccess, Function(dynamic) onError,
{required String activityType, required String activityData, List<String>? filePaths}) async {
{required String activityData, List<String>? filePaths}) async {
var formData = FormData();
if (filePaths != null) {
filePaths.forEach((f) {
@@ -270,7 +279,7 @@ class Repository {

formData.fields.add(MapEntry('tbCommonActivityDTO', activityData));
try {
await dio.post("${ConstCommon.baseUrl}/api/create-common-activity/$activityType", data: formData).then((value) {
await dio.post("${ConstCommon.baseUrl}/api/create-common-activity", data: formData).then((value) {
onSuccess(value.data);
});
} on DioError catch (e) {
@@ -279,7 +288,7 @@ class Repository {
}

Future<void> updateActionCommon(Function(dynamic) onSuccess, Function(dynamic) onError,
{required String activityType, required String activityData, List<String>? filePaths}) async {
{required String activityData, List<String>? filePaths}) async {
var formData = FormData();
if (filePaths != null) {
filePaths.forEach((f) {
@@ -289,7 +298,7 @@ class Repository {

formData.fields.add(MapEntry('tbCommonActivityDTO', activityData));
try {
await dio.post("${ConstCommon.baseUrl}/api/update-common-activity/$activityType", data: formData).then((value) {
await dio.post("${ConstCommon.baseUrl}/api/update-common-activity", data: formData).then((value) {
onSuccess(value.data);
});
} on DioError catch (e) {
@@ -297,9 +306,9 @@ class Repository {
}
}

Future<RequestActivity> detailCommonAction({required int activityId, required String activityType}) async {
Future<RequestActivity> detailCommonAction({required int activityId}) async {
final client = RestClient(dio);
return client.getDetailActivityCommon(activityType, activityId);
return client.getDetailActivityCommon(activityId);
}

// Stamp

+ 1
- 1
lib/presentation/custom_widgets/widget_loading.dart View File

@@ -30,7 +30,7 @@ class LoadingDialog {
child: SizedBox(
width: 25,
height: 25,
child: Image.asset(AppAssets.logo)),
child: Image.asset(AppAssets.logoWithSlogan)),
),
Container(
alignment: Alignment.center,

+ 1
- 2
lib/presentation/screens/actions/cubit/action_ui_cubit.dart View File

@@ -16,7 +16,6 @@ class ActionUiCubit extends Cubit<ActionUiState> {
Future<void> getActionUIForm({
required bool isEdit,
required int actionId,
required String actionType,
required int activityId,
}) async {
try {
@@ -25,7 +24,7 @@ class ActionUiCubit extends Cubit<ActionUiState> {
var actionUIForm = await repository.getActionUIForm(idAction: actionId);
if (isEdit) {
try {
actionDetail = await repository.detailCommonAction(activityId: activityId, activityType: actionType);
actionDetail = await repository.detailCommonAction(activityId: activityId);
print(actionDetail);
} catch (e) {
print(e);

+ 3
- 3
lib/presentation/screens/actions/dung/widget_dung_supply.dart View File

@@ -12,7 +12,6 @@ import 'package:farm_tpf/presentation/screens/actions/controller/ChangeUnit.dart
import 'package:farm_tpf/presentation/screens/actions/resource_device_activity/sc_device_activity.dart';
import 'package:farm_tpf/presentation/screens/actions/state_management_helper/change_supply.dart';
import 'package:farm_tpf/presentation/screens/resources/sc_resource_helper.dart';
import 'package:farm_tpf/utils/const_common.dart';
import 'package:farm_tpf/utils/const_string.dart';
import 'package:farm_tpf/utils/const_style.dart';
import 'package:farm_tpf/utils/validators.dart';
@@ -25,7 +24,8 @@ import '../util_action.dart';
class WidgetDungSupply extends StatefulWidget {
final List<SuppliesUsing> currentItems;
final Function(List<SuppliesUsing> supplyChanges) onChangeSupplies;
WidgetDungSupply({required this.currentItems, required this.onChangeSupplies});
final String urlSupply;
WidgetDungSupply({required this.currentItems, required this.onChangeSupplies, required this.urlSupply});
@override
_WidgetDungSupplyState createState() => _WidgetDungSupplyState();
}
@@ -160,7 +160,7 @@ class _WidgetDungSupplyState extends State<WidgetDungSupply> {
.push(MaterialPageRoute(
builder: (_) => ResourceHelperScreen(
titleName: "Phân bón",
type: ConstCommon.supplyTypeDung,
urlSupply: widget.urlSupply,
selectedId: changeSelectedSupply.selectedSupplyId ?? -1,
currentItems: changeSupplyUsing.currentItems ?? [],
currentEditId: ((currentIndexEdit ?? 0) >= 0)

+ 3
- 1
lib/presentation/screens/actions/harvest_process/widget_harvest_process_supply.dart View File

@@ -25,7 +25,9 @@ import '../util_action.dart';
class WidgetHarvestProcessSupply extends StatefulWidget {
final List<SuppliesUsing>? currentItems;
final Function(List<SuppliesUsing> supplyChanges) onChangeSupplies;
final String urlSupply;
WidgetHarvestProcessSupply({
required this.urlSupply,
this.currentItems,
required this.onChangeSupplies,
});
@@ -163,7 +165,7 @@ class _WidgetHarvestProcessSupplyState extends State<WidgetHarvestProcessSupply>
.push(MaterialPageRoute(
builder: (_) => ResourceHelperScreen(
titleName: "Hoá chất xử lý",
type: ConstCommon.supplyTypeAll,
urlSupply: widget.urlSupply,
selectedId: changeSelectedSupply.selectedSupplyId ?? -1,
currentItems: changeSupplyUsing.currentItems ?? [],
currentEditId: ((currentIndexEdit ?? -1) >= 0)

+ 3
- 2
lib/presentation/screens/actions/plant/widget_plant_supply.dart View File

@@ -22,7 +22,8 @@ import 'package:farm_tpf/utils/formatter.dart';
class WidgetPlantSupply extends StatefulWidget {
final List<SuppliesUsing>? currentItems;
final Function(List<SuppliesUsing> supplyChanges) onChangeSupplies;
WidgetPlantSupply({this.currentItems, required this.onChangeSupplies});
final String urlSupply;
WidgetPlantSupply({required this.urlSupply, this.currentItems, required this.onChangeSupplies});
@override
_WidgetPlantSupplyState createState() => _WidgetPlantSupplyState();
}
@@ -147,7 +148,7 @@ class _WidgetPlantSupplyState extends State<WidgetPlantSupply> {
.push(MaterialPageRoute(
builder: (_) => ResourceHelperScreen(
titleName: "Hoá chất xử lý",
type: ConstCommon.supplyTypeAll,
urlSupply: widget.urlSupply,
selectedId: changeSelectedSupply.selectedSupplyId ?? -1,
currentItems: changeSupplyUsing.currentItems ?? [],
currentEditId: ((currentIndexEdit ?? -1) >= 0)

+ 221
- 162
lib/presentation/screens/actions/sc_action.dart View File

@@ -21,6 +21,7 @@ import 'package:farm_tpf/presentation/custom_widgets/widget_text_field_area.dart
import 'package:farm_tpf/presentation/custom_widgets/widget_text_form_field.dart';
import 'package:farm_tpf/presentation/custom_widgets/widget_utils.dart';
import 'package:farm_tpf/presentation/screens/actions/cubit/action_ui_cubit.dart';
import 'package:farm_tpf/presentation/screens/actions/supply_widget.dart';
import 'package:farm_tpf/utils/const_string.dart';
import 'package:farm_tpf/utils/pref.dart';
import 'package:farm_tpf/utils/validators.dart';
@@ -35,10 +36,7 @@ import '../../../utils/local_storage.dart';
import 'controller/ChangeFieldInForm.dart';
import 'controller/ChangeSupplyUsing.dart';
import 'controller/ChangeWorker.dart';
import 'dung/widget_dung_supply.dart';
import 'nursery/widget_worker.dart';
import 'plant/widget_plant_supply.dart';
import 'spraying/widget_spraying_supply.dart';
import 'state_management_helper/change_dropdown_controller.dart';
import 'state_management_helper/change_file_controller.dart';
import 'util_action.dart';
@@ -47,16 +45,16 @@ class ActionScreen extends StatefulWidget {
final bool isEdit;
final int cropId;
final int idAction;
final String activityType;
final String title;
final int activityId;
final DateTime? executeDate;
ActionScreen({
required this.isEdit,
required this.cropId,
required this.idAction,
required this.title,
required this.activityType,
required this.activityId,
this.executeDate,
});

@override
@@ -94,40 +92,41 @@ class _ActionScreenState extends State<ActionScreen> {
super.initState();
getSharedPrefs();
changeFileController.initValue();
if (widget.executeDate != null) {
executeTime = widget.executeDate!;
}
}

_submitForm() async {
switch (widget.activityType) {
case 'ACTIVE_TYPE_NURSERY':
if (Get.find<ChangeFieldFormSupply>().isChanged ?? false) {
Utils.showDialog(
title: 'Thông tin người thực hiện chưa cập nhật',
message: 'Bạn có muốn cập nhật',
textConfirm: 'Tiếp tục',
textCancel: 'Xem lại',
onConfirm: () {
Get.back();
_validateInputs();
});
} else {
_validateInputs();
}
break;
// case 'ACTIVE_TYPE_PLANTING':
case 'ACTIVE_TYPE_FERTILIZE':
case 'ACTIVE_TYPE_SPRAYING_PESTICIDES':
if (Get.find<ChangeFieldFormSupply>().isChanged ?? false) {
Utils.showDialogConfirmSupply(onConfirm: () {
Get.back();
_validateInputs();
});
} else {
final tbActivityType = _actionUIForm.tbActivityTypeDTO;
final isUseSupply = tbActivityType?.isUseSupply ?? false;
final isNursery =
tbActivityType?.isStart == true && tbActivityType?.position == 1;
if (isNursery) {
if (Get.find<ChangeFieldFormSupply>().isChanged ?? false) {
Utils.showDialog(
title: 'Thông tin người thực hiện chưa cập nhật',
message: 'Bạn có muốn cập nhật',
textConfirm: 'Tiếp tục',
textCancel: 'Xem lại',
onConfirm: () {
Get.back();
_validateInputs();
});
} else {
_validateInputs();
}
} else if (isUseSupply) {
if (Get.find<ChangeFieldFormSupply>().isChanged ?? false) {
Utils.showDialogConfirmSupply(onConfirm: () {
Get.back();
_validateInputs();
}
break;
default:
});
} else {
_validateInputs();
break;
}
} else {
_validateInputs();
}
}

@@ -143,7 +142,10 @@ class _ActionScreenState extends State<ActionScreen> {
..tbActivityTypeId = widget.idAction
..tbCropId = widget.cropId;
if ((_actionUIForm.activityExtendTypeDTOList ?? []).isNotEmpty) {
_requestActivity..externalTable = _actionUIForm?.activityExtendTypeDTOList?.first?.externalTable ?? '';
_requestActivity
..externalTable = _actionUIForm
?.activityExtendTypeDTOList?.first?.externalTable ??
'';
}

filePaths = Get.find<ChangeFileController>().newFiles ?? [];
@@ -158,8 +160,10 @@ class _ActionScreenState extends State<ActionScreen> {
_requestActivity.tbObjectUpdateDTOList?.forEach((element) {
print(valueObjects[element.tbObjectParameterId.toString()]);
var updateValue = '';
if (Validators.stringNotNullOrEmpty(valueObjects[element.tbObjectParameterId.toString()] ?? '')) {
updateValue = valueObjects[element.tbObjectParameterId.toString()] ?? '';
if (Validators.stringNotNullOrEmpty(
valueObjects[element.tbObjectParameterId.toString()] ?? '')) {
updateValue =
valueObjects[element.tbObjectParameterId.toString()] ?? '';
} else {
updateValue = element.index ?? '';
}
@@ -183,19 +187,24 @@ class _ActionScreenState extends State<ActionScreen> {
_requestActivity.tbObjectUpdateDTOList = _objectPrameters;
}

final tbActivityType = _actionUIForm.tbActivityTypeDTO;
final isUseSupply = tbActivityType?.isUseSupply ?? false;
final isNursery =
tbActivityType?.isStart == true && tbActivityType?.position == 1;
//CHECK NURSERY
if (widget.activityType == 'ACTIVE_TYPE_NURSERY') {
if (isNursery) {
_requestActivity.tbNurseryDetailsDTOList = _nurseryDetails;
} else if (
// widget.activityType == 'ACTIVE_TYPE_PLANTING' ||
widget.activityType == 'ACTIVE_TYPE_FERTILIZE' ||
widget.activityType == 'ACTIVE_TYPE_SPRAYING_PESTICIDES') {
isUseSupply) {
_requestActivity.tbSuppliesUsingDetailsDTOs = _supplyUsings;
}
//delete images
_requestActivity.deletedImages = Get.find<ChangeFileController>().deleteFiles;
_requestActivity.deletedImages =
Get.find<ChangeFileController>().deleteFiles;
//convert data to json
var activityCommonData = jsonEncode(_requestActivity.toJson()).toString();
var activityCommonData =
jsonEncode(_requestActivity.toJson()).toString();
print(activityCommonData);

if (widget.activityId < 0) {
@@ -207,7 +216,7 @@ class _ActionScreenState extends State<ActionScreen> {
}, (error) {
LoadingDialog.hideLoadingDialog(context);
Utils.showSnackBarError(message: AppException.handleError(error));
}, activityType: widget.activityType, activityData: activityCommonData, filePaths: filePaths);
}, activityData: activityCommonData, filePaths: filePaths);
} else {
//UPDATE
_repository.updateActionCommon((data) {
@@ -217,7 +226,7 @@ class _ActionScreenState extends State<ActionScreen> {
}, (error) {
LoadingDialog.hideLoadingDialog(context);
Utils.showSnackBarError(message: AppException.handleError(error));
}, activityType: widget.activityType, activityData: activityCommonData, filePaths: filePaths);
}, activityData: activityCommonData, filePaths: filePaths);
}

//ADD NEW
@@ -238,7 +247,8 @@ class _ActionScreenState extends State<ActionScreen> {
return WidgetFieldDateTimePicker(
initDateTime: executeTime,
onUpdateDateTime: (selectedDate) {
_requestActivity.executeDate = selectedDate.convertLocalDateTimeToStringUtcDateTime();
_requestActivity.executeDate =
selectedDate.convertLocalDateTimeToStringUtcDateTime();
});
}

@@ -256,7 +266,8 @@ class _ActionScreenState extends State<ActionScreen> {
// GENERATE DYNAMIC FORM
//
Widget groupName(String name) {
if (_previousGroupFieldName == name || !Validators.stringNotNullOrEmpty(name)) {
if (_previousGroupFieldName == name ||
!Validators.stringNotNullOrEmpty(name)) {
return SizedBox();
} else {
_previousGroupFieldName = name ?? '';
@@ -303,15 +314,17 @@ class _ActionScreenState extends State<ActionScreen> {
TextFormField(
keyboardType: TextInputType.text,
decoration: InputDecoration(
labelText: ((field.isMandatory ?? false) ?? false) ? '${field.description} *' : '${field.description}',
hintText: field.description),
labelText: ((field.isMandatory ?? false) ?? false)
? '${field.name} *'
: '${field.name}',
hintText: field.name),
controller: textFieldControllers[field.id.toString()],
onSaved: (newValue) {},
validator: ((field.isMandatory ?? false) ?? false)
? (String? value) {
return Validators.validateNotNullOrEmpty(
value ?? '',
'Vui lòng nhập ${field.description}',
'Vui lòng nhập ${field.name}',
);
}
: null,
@@ -323,15 +336,19 @@ class _ActionScreenState extends State<ActionScreen> {
children: [
groupName(field.groupName ?? ''),
WidgetTextFormFieldNumber(
hintValue: field.description ?? '',
labelText: ((field.isMandatory ?? false) ?? false) ? '${field.description} *' : '${field.description}',
textController: textFieldControllers[field.id.toString()] ?? TextEditingController(),
hintValue: field.name ?? '',
labelText: ((field.isMandatory ?? false) ?? false)
? '${field.name} *'
: '${field.name}',
textController:
textFieldControllers[field.id.toString()] ??
TextEditingController(),
onSaved: (newValue) {},
validator: ((field.isMandatory ?? false) ?? false)
? (String? value) {
return Validators.validNumberOrEmpty(
value ?? '',
'Vui lòng nhập ${field.description}',
'Vui lòng nhập ${field.name}',
);
}
: null,
@@ -343,15 +360,18 @@ class _ActionScreenState extends State<ActionScreen> {
children: [
groupName(field.groupName ?? ''),
TextFieldAreaWidget(
hint: field.description ?? '',
labelText: (field.isMandatory ?? false) ? '${field.description} *' : '${field.description}',
controller: textFieldControllers[field.id.toString()] ?? TextEditingController(),
hint: field.name ?? '',
labelText: (field.isMandatory ?? false)
? '${field.name} *'
: '${field.name}',
controller: textFieldControllers[field.id.toString()] ??
TextEditingController(),
onSaved: (newValue) {},
validator: (field.isMandatory ?? false)
? (String? value) {
return Validators.validateNotNullOrEmpty(
value ?? '',
'Vui lòng nhập ${field.description}',
'Vui lòng nhập ${field.name}',
);
}
: null,
@@ -363,15 +383,17 @@ class _ActionScreenState extends State<ActionScreen> {
children: [
groupName(field.groupName ?? ''),
DropdownSupplyWidget(
titleName: field.description ?? '',
titleName: field.name ?? '',
tbSupply: field.tbActivityExtendTypeExternalTable ?? '',
tag: field.name ?? '',
value: field.description,
hint: '${field.description} ${(field.isMandatory ?? false) ? '*' : ''}',
value: field.name,
hint:
'${field.name} ${(field.isMandatory ?? false) ? '*' : ''}',
condition: field.tbActivityExtendTypeCondition ?? '',
invalidMessage: '',
onPressed: (commonData) {
valueObjects[field.id.toString()] = commonData.id.toString();
valueObjects[field.id.toString()] =
commonData.id.toString();
},
),
],
@@ -385,7 +407,8 @@ class _ActionScreenState extends State<ActionScreen> {
condition: field.tbActivityExtendTypeCondition ?? '',
supply: field.tbActivityExtendTypeExternalTable ?? '',
onPressed: (commonData) {
valueObjects[field.id.toString()] = commonData.id.toString();
valueObjects[field.id.toString()] =
commonData.id.toString();
},
),
],
@@ -397,14 +420,17 @@ class _ActionScreenState extends State<ActionScreen> {
FieldDateWidget(
tag: field.name ?? '',
isValidated: isValidated,
value: field.description,
hint: '${field.description} ${(field.isMandatory ?? false) ? '*' : ''}',
value: field.name,
hint:
'${field.name} ${(field.isMandatory ?? false) ? '*' : ''}',
onPressed: (selectedDate) {
valueObjects[field.id.toString()] = selectedDate.convertLocalDateTimeToStringUtcDateTime();
valueObjects[field.id.toString()] = selectedDate
.convertLocalDateTimeToStringUtcDateTime();
},
validator: (field.isMandatory ?? false)
? (String? value) {
return Validators.validateNotNullOrEmpty(value ?? '', 'Vui lòng nhập ${field.description}');
return Validators.validateNotNullOrEmpty(
value ?? '', 'Vui lòng nhập ${field.name}');
}
: null,
),
@@ -422,65 +448,37 @@ class _ActionScreenState extends State<ActionScreen> {
// GENERATE SUPPLY
//

Widget generateSupply(String activityType) {
switch (activityType) {
case 'ACTIVE_TYPE_NURSERY':
return WidgetWorker(onChangeWorkers: (nurseryDetails) {
_nurseryDetails = nurseryDetails;
});
break;
// case 'ACTIVE_TYPE_PLANTING':
// return Column(
// children: [
// Container(
// width: double.infinity,
// height: 16,
// color: Colors.grey[200],
// ),
// WidgetPlantSupply(
// currentItems: [],
// onChangeSupplies: (value) {
// _supplyUsings = value;
// }),
// ],
// );
// break;
case 'ACTIVE_TYPE_FERTILIZE':
return Column(
children: [
Container(
width: double.infinity,
height: 16,
color: Colors.grey[200],
),
WidgetDungSupply(
currentItems: [],
onChangeSupplies: (value) {
_supplyUsings = value;
}),
],
);
break;
case 'ACTIVE_TYPE_SPRAYING_PESTICIDES':
return Column(
children: [
Container(
width: double.infinity,
height: 16,
color: Colors.grey[200],
),
WidgetSprayingSupply(
currentItems: [],
onChangeSupplies: (value) {
_supplyUsings = value;
}),
],
);
break;
default:
return Container();
break;
Widget generateSupply({
required String supplyGroup,
bool isUseSupply = false,
bool isStart = false,
int position = 0,
required String urlSupply,
}) {
if (isStart && position == 1) {
return WidgetWorker(onChangeWorkers: (nurseryDetails) {
_nurseryDetails = nurseryDetails;
});
}
if (isUseSupply) {
return Column(
children: [
Container(
width: double.infinity,
height: 16,
color: Colors.grey[200],
),
SupplyWidget(
currentItems: [],
urlSupply: urlSupply,
supplyGroup: supplyGroup,
onChangeSupplies: (value) {
_supplyUsings = value;
}),
],
);
}
return SizedBox.shrink();
}

void showDataWhenEdit(BuildContext context) {
@@ -500,15 +498,18 @@ class _ActionScreenState extends State<ActionScreen> {
}

SchedulerBinding.instance.addPostFrameCallback((_) {
if (
// widget.activityType == 'ACTIVE_TYPE_PLANTING' ||
widget.activityType == 'ACTIVE_TYPE_FERTILIZE' ||
widget.activityType == 'ACTIVE_TYPE_SPRAYING_PESTICIDES') {
final tbActivityType = _actionUIForm.tbActivityTypeDTO;
final isUseSupply = tbActivityType?.isUseSupply ?? false;
final isNursery =
tbActivityType?.isStart == true && tbActivityType?.position == 1;
if (isUseSupply) {
//list supply
Get.find<ChangeSupplyUsing>().changeInitList(_requestActivity.tbSuppliesUsingDetailsDTOs ?? []);
} else if (widget.activityType == 'ACTIVE_TYPE_NURSERY') {
Get.find<ChangeSupplyUsing>()
.changeInitList(_requestActivity.tbSuppliesUsingDetailsDTOs ?? []);
} else if (isNursery) {
//list nursery
Get.find<ChangeWorker>().changeInitList(_requestActivity.tbNurseryDetailsDTOList ?? []);
Get.find<ChangeWorker>()
.changeInitList(_requestActivity.tbNurseryDetailsDTOList ?? []);
}
});

@@ -516,22 +517,35 @@ class _ActionScreenState extends State<ActionScreen> {
if (_requestActivity.tbObjectUpdateDTOList != null) {
print(textFieldControllers.keys.toList());
_requestActivity.tbObjectUpdateDTOList?.forEach((element) {
if (element.tbObjectParameterDTO?.tbControlTypeName == 'text' || element.tbObjectParameterDTO?.tbControlTypeName == 'textarea') {
if (element.tbObjectParameterDTO?.tbControlTypeName == 'text' ||
element.tbObjectParameterDTO?.tbControlTypeName == 'textarea') {
SchedulerBinding.instance.addPostFrameCallback((_) {
textFieldControllers[element.tbObjectParameterId.toString()]?.text = element.index ?? '';
textFieldControllers[element.tbObjectParameterId.toString()]?.text =
element.index ?? '';
});
} else if (element.tbObjectParameterDTO?.tbControlTypeName == 'number') {
} else if (element.tbObjectParameterDTO?.tbControlTypeName ==
'number') {
SchedulerBinding.instance.addPostFrameCallback((_) {
textFieldControllers[element.tbObjectParameterId.toString()]?.text = element.index?.formatStringToStringDecimal() ?? '';
textFieldControllers[element.tbObjectParameterId.toString()]?.text =
element.index?.formatStringToStringDecimal() ?? '';
});
} else {
SchedulerBinding.instance.addPostFrameCallback((_) {
if (element.tbObjectParameterDTO?.tbControlTypeName == 'dropdown' ||
element.tbObjectParameterDTO?.tbControlTypeName?.toLowerCase() == 'radiobutton') {
element.tbObjectParameterDTO?.tbControlTypeName
?.toLowerCase() ==
'radiobutton') {
var dropdownValueName = '';
if ((element.tbObjectParameterDTO?.tbActivityExtendTypeDropDownDTOList ?? []).isNotEmpty ||
element.tbObjectParameterDTO?.tbActivityExtendTypeDropDownDTOList != null) {
element.tbObjectParameterDTO?.tbActivityExtendTypeDropDownDTOList?.forEach((dropdownData) {
if ((element.tbObjectParameterDTO
?.tbActivityExtendTypeDropDownDTOList ??
[])
.isNotEmpty ||
element.tbObjectParameterDTO
?.tbActivityExtendTypeDropDownDTOList !=
null) {
element
.tbObjectParameterDTO?.tbActivityExtendTypeDropDownDTOList
?.forEach((dropdownData) {
if (dropdownData.id == int.tryParse(element.index ?? '-1')) {
dropdownValueName = dropdownData.name ?? '';
}
@@ -540,10 +554,16 @@ class _ActionScreenState extends State<ActionScreen> {
var commonData = CommonData()
..id = int.tryParse(element.index ?? '-1')
..name = dropdownValueName;
Get.find<ChangeDropdownController>(tag: element.tbObjectParameterDTO?.name).change(commonData);
} else if (element.tbObjectParameterDTO?.tbControlTypeName == 'date') {
Get.find<ChangeDateTimePicker>(tag: element.tbObjectParameterDTO?.name).change(
element.index?.convertStringServerDateTimeToLocalDateTime() ?? DateTime.now(),
Get.find<ChangeDropdownController>(
tag: element.tbObjectParameterDTO?.name)
.change(commonData);
} else if (element.tbObjectParameterDTO?.tbControlTypeName ==
'date') {
Get.find<ChangeDateTimePicker>(
tag: element.tbObjectParameterDTO?.name)
.change(
element.index?.convertStringServerDateTimeToLocalDateTime() ??
DateTime.now(),
);
}
});
@@ -567,7 +587,8 @@ class _ActionScreenState extends State<ActionScreen> {
action: InkWell(
child: Text(
'Lưu',
style: TextStyle(color: Colors.red, fontWeight: FontWeight.normal),
style: TextStyle(
color: Colors.red, fontWeight: FontWeight.normal),
),
onTap: () {
FocusScopeNode currentFocus = FocusScope.of(context);
@@ -581,11 +602,15 @@ class _ActionScreenState extends State<ActionScreen> {
body: MultiBlocProvider(
providers: [
BlocProvider<ActionUiCubit>(
create: (context) => ActionUiCubit(repository: Repository())
..getActionUIForm(
actionId: widget.idAction, actionType: widget.activityType, isEdit: widget.isEdit, activityId: widget.activityId)),
create: (context) =>
ActionUiCubit(repository: Repository())
..getActionUIForm(
actionId: widget.idAction,
isEdit: widget.isEdit,
activityId: widget.activityId)),
BlocProvider<MediaHelperBloc>(
create: (context) => MediaHelperBloc()..add(ChangeListMedia(items: [])),
create: (context) =>
MediaHelperBloc()..add(ChangeListMedia(items: [])),
)
],
child: Form(
@@ -599,20 +624,27 @@ class _ActionScreenState extends State<ActionScreen> {
child: LoadingListPage(),
);
} else if (state is ActionUiSuccess) {
_actionUIForm = state.actionUIForm ?? ActionUIForm();
_requestActivity = state.activityDetail ?? RequestActivity();
_actionUIForm =
state.actionUIForm ?? ActionUIForm();
_requestActivity =
state.activityDetail ?? RequestActivity();

//CREATE UI
_actionUIForm.objectParameterDTOList?.forEach((element) {
_actionUIForm.objectParameterDTOList
?.forEach((element) {
//generate controller
if (element.tbControlTypeName == 'text' ||
element.tbControlTypeName == 'number' ||
element.tbControlTypeName == 'textarea') {
var textEditingController = new TextEditingController();
textFieldControllers.putIfAbsent(element.id.toString(), () => textEditingController);
var textEditingController =
new TextEditingController();
textFieldControllers.putIfAbsent(
element.id.toString(),
() => textEditingController);
}
// generate value each parameter
valueObjects.putIfAbsent(element.id.toString(), () => '');
valueObjects.putIfAbsent(
element.id.toString(), () => '');
});
//SHOW EDIT DATA
if (widget.isEdit) {
@@ -630,14 +662,18 @@ class _ActionScreenState extends State<ActionScreen> {
width: double.infinity,
child: Text(
"Ngày thực hiện *",
style: TextStyle(color: Colors.black54, fontSize: 13.0),
style: TextStyle(
color: Colors.black54,
fontSize: 13.0),
),
),
_btnExecuteTimePicker(),
SizedBox(
height: 8.0,
),
generateField(_actionUIForm.objectParameterDTOList ?? []),
generateField(_actionUIForm
.objectParameterDTOList ??
[]),
_executeByField(),
SizedBox(
height: 8.0,
@@ -645,21 +681,43 @@ class _ActionScreenState extends State<ActionScreen> {
],
),
),
generateSupply(widget.activityType),
generateSupply(
supplyGroup: _actionUIForm
.tbActivityTypeDTO?.supplyGroup ??
'',
isUseSupply: _actionUIForm
.tbActivityTypeDTO?.isUseSupply ??
false,
isStart: _actionUIForm
.tbActivityTypeDTO?.isStart ??
false,
position: _actionUIForm
.tbActivityTypeDTO?.position ??
0,
urlSupply: _actionUIForm
.tbActivityTypeDTO?.urlSupply ??
'',
),
Container(
width: double.infinity,
height: 16,
color: Colors.grey[200],
),
BlocBuilder<MediaHelperBloc, MediaHelperState>(builder: (context, state) {
BlocBuilder<MediaHelperBloc,
MediaHelperState>(
builder: (context, state) {
if (state is MediaHelperSuccess) {
return WidgetMediaPicker(
currentItems: state.items,
onChangeFiles: (newPathFiles, deletePathFiles) async {
Get.find<ChangeFileController>().change(newPathFiles, deletePathFiles);
onChangeFiles: (newPathFiles,
deletePathFiles) async {
Get.find<ChangeFileController>()
.change(newPathFiles,
deletePathFiles);
});
} else {
return Center(child: CircularProgressIndicator());
return Center(
child: CircularProgressIndicator());
}
}),
Padding(
@@ -667,7 +725,8 @@ class _ActionScreenState extends State<ActionScreen> {
child: ButtonWidget(
title: 'CẬP NHẬT',
onPressed: () {
FocusScopeNode currentFocus = FocusScope.of(context);
FocusScopeNode currentFocus =
FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}

+ 3
- 2
lib/presentation/screens/actions/spraying/widget_spraying_supply.dart View File

@@ -26,7 +26,8 @@ import '../util_action.dart';
class WidgetSprayingSupply extends StatefulWidget {
final List<SuppliesUsing>? currentItems;
final Function(List<SuppliesUsing> supplyChanges) onChangeSupplies;
WidgetSprayingSupply({this.currentItems, required this.onChangeSupplies});
final String urlSupply;
WidgetSprayingSupply({required this.urlSupply, this.currentItems, required this.onChangeSupplies});
@override
_WidgetSprayingSupplyState createState() => _WidgetSprayingSupplyState();
}
@@ -162,7 +163,7 @@ class _WidgetSprayingSupplyState extends State<WidgetSprayingSupply> {
.push(MaterialPageRoute(
builder: (_) => ResourceHelperScreen(
titleName: "Thuốc BVTV",
type: ConstCommon.supplyTypeProtectPlant,
urlSupply: widget.urlSupply,
selectedId: changeSelectedSupply.selectedSupplyId ?? -1,
currentItems: changeSupplyUsing.currentItems ?? [],
currentEditId: ((currentIndexEdit ?? -1) >= 0)

+ 733
- 0
lib/presentation/screens/actions/supply_widget.dart View File

@@ -0,0 +1,733 @@
import 'package:farm_tpf/custom_model/Device.dart';
import 'package:farm_tpf/custom_model/SuppliesUsing.dart';
import 'package:farm_tpf/custom_model/Supply.dart';
import 'package:farm_tpf/presentation/custom_widgets/WidgetErrorTextField.dart';
import 'package:farm_tpf/presentation/custom_widgets/widget_text_form_field.dart';
import 'package:farm_tpf/presentation/custom_widgets/widget_utils.dart';
import 'package:farm_tpf/presentation/screens/actions/controller/ChangeDevice.dart';
import 'package:farm_tpf/presentation/screens/actions/controller/ChangeFieldInForm.dart';
import 'package:farm_tpf/presentation/screens/actions/controller/ChangeFormButton.dart';
import 'package:farm_tpf/presentation/screens/actions/controller/ChangeSupplyUsing.dart';
import 'package:farm_tpf/presentation/screens/actions/controller/ChangeUnit.dart';
import 'package:farm_tpf/presentation/screens/actions/resource_device_activity/sc_device_activity.dart';
import 'package:farm_tpf/presentation/screens/actions/state_management_helper/change_supply.dart';
import 'package:farm_tpf/presentation/screens/resources/sc_resource_helper.dart';
import 'package:farm_tpf/utils/const_string.dart';
import 'package:farm_tpf/utils/const_style.dart';
import 'package:farm_tpf/utils/validators.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:farm_tpf/utils/formatter.dart';

import 'util_action.dart';

class SupplyWidget extends StatefulWidget {
final List<SuppliesUsing> currentItems;
final Function(List<SuppliesUsing> supplyChanges) onChangeSupplies;
final String urlSupply;
final String supplyGroup;

SupplyWidget({
required this.currentItems,
required this.onChangeSupplies,
required this.urlSupply,
required this.supplyGroup,
});

@override
_SupplyWidgetState createState() => _SupplyWidgetState();
}

class _SupplyWidgetState extends State<SupplyWidget> {
final _dosageController = TextEditingController();
final _quantityController = TextEditingController();
final _howToUseController = TextEditingController();
final changeSelectedSupply = Get.put(ChangeSupply());
final changeSupplyUsing = Get.put(ChangeSupplyUsing());
final changeUnit = Get.put(ChangeUnit());
final changeButton = Get.put(ChangeButtonInForm());
final changeSelectedDevice = Get.put(ChangeDevice());
final changeFormField = Get.put(ChangeFieldFormSupply());
GlobalKey<FormState> _formSupplyKey = GlobalKey();

@override
void initState() {
super.initState();
changeSelectedSupply.initValue();
changeSelectedDevice.initValue();
changeSupplyUsing.init(widget.currentItems);
changeUnit.initValue();
changeButton.resetValue();
changeFormField.init();
}

Widget _buildListSupply() {
return GetBuilder<ChangeSupplyUsing>(
init: changeSupplyUsing,
builder: (value) {
widget.onChangeSupplies(value.currentItems ?? []);
if (value.currentItems!.isEmpty) {
return Container();
} else {
return Container(
height: 120,
child: ListView.builder(
physics: ClampingScrollPhysics(),
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: value.currentItems?.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
print("edit");
changeSupplyUsing.changeIndexEdit(index);
changeButton.updateToEdit(true);
var editedSupplyUsing =
value.currentItems?[index] ?? SuppliesUsing();
var editedSupply = Supply()
..id = editedSupplyUsing.tbSuppliesInWarehouseId
..tbSuppliesName = editedSupplyUsing.supplyName
..unit = editedSupplyUsing.supplyUnit;
changeSelectedSupply.change(editedSupply);

var editedDevice = Device()
..id = editedSupplyUsing.tbEquipmentOfCustomerId
..name = editedSupplyUsing.equipmentName;
changeSelectedDevice.change(editedDevice);

changeUnit.updateListByUnitName(
editedSupplyUsing.supplyUnit ?? '');
changeUnit.updateSelected(
editedSupplyUsing.supplyUnit ?? '');
_dosageController.text =
editedSupplyUsing.dosage ?? '';
_howToUseController.text =
editedSupplyUsing.howToUse ?? '';
_quantityController.text = editedSupplyUsing
.quantity
?.formatNumtoStringDecimal() ??
'0';
},
child: Card(
child: Stack(
alignment: Alignment.bottomCenter,
children: <Widget>[
Positioned(
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Container(
padding: EdgeInsets.all(4),
width: 150,
child: Column(
crossAxisAlignment:
CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
height: 12.0,
),
Flexible(
child: Text(
value.currentItems?[index]
.supplyName ??
"",
overflow: TextOverflow.ellipsis,
maxLines: 1),
),
Validators.stringNotNullOrEmpty(value
.currentItems?[index]
.dosage ??
'')
? Flexible(
child: Text(
"${value.currentItems?[index].dosage ?? ""}"))
: SizedBox(),
Validators.stringNotNullOrEmpty(value
.currentItems?[index].quantity
?.formatNumtoStringDecimal() ??
'0')
? Flexible(
child: Text(
"${value.currentItems?[index].quantity?.formatNumtoStringDecimal() ?? ""} ${value.currentItems?[index].supplyUnit ?? ""}"))
: SizedBox(),
Validators.stringNotNullOrEmpty(value
.currentItems?[index]
.equipmentName ??
'')
? Flexible(
child: Text(
"${value.currentItems?[index].equipmentName ?? ""}"))
: SizedBox(),
Validators.stringNotNullOrEmpty(value
.currentItems?[index]
.howToUse ??
'')
? Flexible(
child: Text(
"${value.currentItems?[index].howToUse ?? ""}"))
: SizedBox(),
],
),
),
)),
Positioned(
top: -10,
right: -10,
child: IconButton(
icon: Icon(
Icons.cancel,
color: Colors.redAccent,
),
onPressed: () {
changeSupplyUsing.deleteSupply(index);
}),
)
],
)));
}));
}
});
}

Widget _btnSelectSubstrates() {
return GetBuilder<ChangeSupply>(
init: changeSelectedSupply,
builder: (data) {
return TextButton(
onPressed: () {
var currentIndexEdit = changeSupplyUsing.currentIndex;
Navigator.of(context)
.push(MaterialPageRoute(
builder: (_) => ResourceHelperScreen(
titleName: widget.supplyGroup,
urlSupply: widget.urlSupply,
selectedId:
changeSelectedSupply.selectedSupplyId ?? -1,
currentItems:
changeSupplyUsing.currentItems ?? [],
currentEditId: ((currentIndexEdit ?? 0) >= 0)
? changeSupplyUsing
.currentItems![currentIndexEdit!]
.tbSuppliesInWarehouseId ??
-1
: -1,
),
fullscreenDialog: false))
.then((value) {
if (value != null) {
var result = value as Supply;
changeSelectedSupply.change(result);
changeUnit.updateListByUnitName(result.unit ?? '');
changeFormField.change(true);
}
});
},
child: GetBuilder<ChangeSupply>(
init: changeSelectedSupply,
builder: (_) {
var isValid = changeSelectedSupply.isValid;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.only(
top: 0.0, right: 0.0, bottom: 10.5, left: 0.0),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 1,
color: (isValid ?? false)
? Colors.grey
: Colors.red)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Validators.stringNotNullOrEmpty(
changeSelectedSupply
.selectedSupplyName ??
'')
? Text(
'Tên thương mại *',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.normal,
color: (isValid ?? false)
? Colors.black54
: Colors.red[600]),
)
: Text(
'',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.normal,
color: (isValid ?? false)
? Colors.black54
: Colors.red[600]),
),
Row(
children: [
Expanded(
child: Validators.stringNotNullOrEmpty(
changeSelectedSupply
.selectedSupplyName ??
'')
? Text(
changeSelectedSupply
.selectedSupplyName ??
'',
style: TextStyle(
fontSize: 14.0,
color: Colors.black87))
: Text("Tên thương mại*",
style: TextStyle(
fontSize: 14.0,
color: Colors.black54)),
),
Icon(
Icons.arrow_drop_down,
color: Colors.grey,
),
],
)
],
)),
(isValid ?? false) ? SizedBox() : WidgetErrorTextField()
],
);
}));
});
}

Widget _btnSelectDevice() {
return GetBuilder<ChangeDevice>(
init: changeSelectedDevice,
builder: (data) {
return TextButton(
onPressed: () {
Navigator.of(context)
.push(MaterialPageRoute(
builder: (_) => ListDeviceActivity(
selectedId:
changeSelectedDevice.selectedDeviceId ?? -1),
fullscreenDialog: false))
.then((value) {
if (value != null) {
var result = value as Device;
changeSelectedDevice.change(result);
changeFormField.change(true);
}
});
},
child: Container(
padding: EdgeInsets.only(
top: 0.0, right: 0.0, bottom: 10.5, left: 0.0),
decoration: BoxDecoration(
border: kBorderTextField,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Validators.stringNotNullOrEmpty(
changeSelectedDevice.selectedDeviceName ?? '')
? Text(
'Thiết bị',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.normal,
color: Colors.black54),
)
: Text(
'',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.normal,
color: Colors.black54),
),
Row(
children: [
GetBuilder<ChangeSupply>(
init: changeSelectedSupply,
builder: (_) => Expanded(
child: Validators.stringNotNullOrEmpty(
changeSelectedDevice
.selectedDeviceName ??
'')
? Text(
changeSelectedDevice
.selectedDeviceName ??
'',
style: TextStyle(
fontSize: 14.0,
color: Colors.black87))
: Text("Thiết bị",
style: TextStyle(
fontSize: 14.0,
color: Colors.black54)),
)),
Icon(
Icons.arrow_drop_down,
color: Colors.grey,
),
],
),
],
)));
});
}

Widget _dropdownUnitTypes() {
return GetBuilder<ChangeUnit>(
init: changeUnit,
builder: (data) {
return DropdownButtonFormField<String>(
itemHeight: 50,
value: !Validators.stringNotNullOrEmpty(data.selectedUnit ?? '')
? null
: data.selectedUnit,
items: data.currentUnits!
.map((label) => DropdownMenuItem(
child: Text(label),
value: label,
))
.toList(),
onChanged: (value) {
var currentQuantity = _quantityController.text;
num assignValue = currentQuantity.parseDoubleThousand();
if (assignValue != null) {
var oldSelected = data.selectedUnit;
if (oldSelected == value) {
} else {
assignValue = UtilAction.convertUnit(
inputValue: assignValue,
oldUnit: oldSelected ?? '',
newUnit: value ?? '',
);
}
_quantityController.text =
assignValue.formatNumtoStringDecimal();
}
changeUnit.updateSelected(value ?? '');
},
);
});
}

_quantityField() {
return WidgetTextFormFieldNumber(
hintValue: "Tổng lượng sử dụng *",
labelText: "Tổng lượng sử dụng *",
textController: _quantityController,
validator: (String? value) {
return Validators.validateNotNullOrEmpty(
value ?? '', label_validate_input_empty);
},
onChanged: (value) {
if (!Validators.stringNotNullOrEmpty(value ?? '') &&
!Validators.stringNotNullOrEmpty(_howToUseController.text) &&
!Validators.stringNotNullOrEmpty(_dosageController.text) &&
(Get.find<ChangeSupply>().selectedSupplyId ?? -1) <= 0 &&
(changeSelectedDevice.selectedDeviceId ?? -1) <= 0) {
changeFormField.change(false);
} else {
changeFormField.change(true);
}
},
);
}

_buttonInForm() {
return GetBuilder<ChangeButtonInForm>(
init: changeButton,
builder: (_) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_.isEdit ?? false
? TextButton(
child: Text("Huỷ"),
onPressed: () {
changeButton.resetValue();
_resetForm();
_hidenKeyboard(context);
})
: SizedBox(),
_.isEdit ?? false
? Expanded(
child: TextButton(
onPressed: () {
if (_formSupplyKey.currentState!.validate()) {
_formSupplyKey.currentState!.save();
if ((changeSelectedSupply.selectedSupplyId ??
-1) <=
0) {
changeSelectedSupply.changeValid(false);
} else {
changeSelectedSupply.changeValid(true);
}
var currentSupply =
changeSelectedSupply.currentSupply;
var currentDevice =
changeSelectedDevice.currentDevice;
var currentQuantity = _quantityController.text
.parseDoubleThousand();
if (currentSupply?.id != null &&
(currentQuantity ?? 0) > 0) {
var quantityWithCurrentSupplyUnit =
UtilAction.convertUnit(
inputValue: currentQuantity,
oldUnit: changeUnit.selectedUnit ?? '',
newUnit: changeSelectedSupply
.currentSupply?.unit ??
'',
);
SuppliesUsing newSup = SuppliesUsing()
..dosage = _dosageController.text
..howToUse = _howToUseController.text
..quantity = quantityWithCurrentSupplyUnit
..tbSuppliesInWarehouseId = currentSupply?.id
..suppliesInWarehouseId = currentSupply?.id
..supplyName = currentSupply?.tbSuppliesName
..tbEquipmentOfCustomerId =
currentDevice?.id ?? -1
..equipmentOfCustomerId =
currentDevice?.id ?? -1
..equipmentName = currentDevice?.name ?? ''
..supplyUnit = currentSupply?.unit
..unit = currentSupply?.unit;
changeSupplyUsing.editSupply(
changeSupplyUsing.currentIndex ?? -1,
newSup);
_resetForm();
_hidenKeyboard(context);
} else if (currentSupply?.id == null ||
((currentQuantity ?? 0) <= 0)) {
Utils.showSnackBarWarning(
message:
"Vui lòng nhập vật tư và số lượng");
}
} else {
Utils.showSnackBarWarning(
message: "Vui lòng nhập vật tư và số lượng");
if ((changeSelectedSupply.selectedSupplyId ??
-1) <=
0) {
changeSelectedSupply.changeValid(false);
} else {
changeSelectedSupply.changeValid(true);
}
}
},
child: Text(
"Sửa ${widget.supplyGroup}",
style: TextStyle(color: Colors.blue),
)),
)
: Expanded(
child: TextButton(
onPressed: () {
if (_formSupplyKey.currentState!.validate()) {
_formSupplyKey.currentState!.save();
if ((changeSelectedSupply.selectedSupplyId ??
-1) <=
0) {
changeSelectedSupply.changeValid(false);
} else {
changeSelectedSupply.changeValid(true);
}
var currentSupply =
changeSelectedSupply.currentSupply;
var currentDevice =
changeSelectedDevice.currentDevice;
var currentQuantity = _quantityController.text
.parseDoubleThousand();
if (currentSupply?.id != null &&
(currentQuantity ?? 0) > 0) {
var quantityWithCurrentSupplyUnit =
UtilAction.convertUnit(
inputValue: currentQuantity,
oldUnit: changeUnit.selectedUnit ?? '',
newUnit: changeSelectedSupply
.currentSupply?.unit ??
'',
);
SuppliesUsing newSup = SuppliesUsing()
..dosage = _dosageController.text
..howToUse = _howToUseController.text
..quantity = quantityWithCurrentSupplyUnit
..tbSuppliesInWarehouseId = currentSupply?.id
..suppliesInWarehouseId = currentSupply?.id
..supplyName = currentSupply?.tbSuppliesName
..supplyUnit = currentSupply?.unit
..tbEquipmentOfCustomerId =
currentDevice?.id ?? -1
..equipmentOfCustomerId =
currentDevice?.id ?? -1
..equipmentName = currentDevice?.name
..unit = currentSupply?.unit;
changeSupplyUsing.addSupply(newSup);
_resetForm();
_hidenKeyboard(context);
} else if (currentSupply?.id == null ||
((currentQuantity ?? 0) <= 0)) {
Utils.showSnackBarWarning(
message:
"Vui lòng nhập vật tư và số lượng");
}
} else {
Utils.showSnackBarWarning(
message: "Vui lòng nhập vật tư và số lượng");
if ((changeSelectedSupply.selectedSupplyId ??
-1) <=
0) {
changeSelectedSupply.changeValid(false);
} else {
changeSelectedSupply.changeValid(true);
}
//
}
},
child: Text(
"+ Thêm ${widget.supplyGroup}",
style: TextStyle(color: Colors.blue),
)),
)
],
);
});
}

Widget _formEdit() {
return Form(
key: _formSupplyKey,
child: Column(
children: [
Container(
padding: EdgeInsets.all(8.0),
margin: EdgeInsets.all(8.0),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(10),
color: Colors.white,
border: Border.all(color: Colors.grey),
),
child: Column(
children: [
_btnSelectSubstrates(),
TextFormField(
keyboardType: TextInputType.text,
controller: _dosageController,
decoration: InputDecoration(labelText: "Liều lượng sử dụng"),
onSaved: (newValue) {},
onChanged: (value) {
if (!Validators.stringNotNullOrEmpty(
_quantityController.text) &&
!Validators.stringNotNullOrEmpty(
_howToUseController.text) &&
!Validators.stringNotNullOrEmpty(value) &&
(Get.find<ChangeSupply>().selectedSupplyId ?? -1) <=
0 &&
(changeSelectedDevice.selectedDeviceId ?? -1) <= 0) {
changeFormField.change(false);
} else {
changeFormField.change(true);
}
},
),
Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
flex: 2,
child: Container(
height: 70,
child: _quantityField(),
),
),
SizedBox(
width: 16.0,
),
Expanded(
flex: 1,
child: Align(
alignment: Alignment.bottomCenter,
child: _dropdownUnitTypes(),
)),
]),
_btnSelectDevice(),
TextFormField(
keyboardType: TextInputType.text,
controller: _howToUseController,
decoration: InputDecoration(labelText: "Phương pháp sử dụng"),
onSaved: (newValue) {},
onChanged: (value) {
if (!Validators.stringNotNullOrEmpty(
_quantityController.text) &&
!Validators.stringNotNullOrEmpty(value) &&
!Validators.stringNotNullOrEmpty(
_dosageController.text) &&
(Get.find<ChangeSupply>().selectedSupplyId ?? -1) <=
0 &&
(changeSelectedDevice.selectedDeviceId ?? -1) <= 0) {
changeFormField.change(false);
} else {
changeFormField.change(true);
}
},
),
],
),
),
_buttonInForm()
],
),
);
}

_resetForm() {
changeSupplyUsing.changeIndexEdit(-1);
changeButton.resetValue();
_dosageController.text = "";
_howToUseController.text = "";
_quantityController.text = "";
changeUnit.initValue();
changeSelectedSupply.initValue();
changeSelectedDevice.initValue();
changeFormField.change(false);
}

_hidenKeyboard(BuildContext context) {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
}

@override
Widget build(BuildContext context) {
return Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
widget.supplyGroup,
style: TextStyle(color: Colors.black54, fontSize: 14),
),
),
),
_buildListSupply(),
SizedBox(
height: 8.0,
),
_formEdit(),
SizedBox(
height: 8.0,
),
],
);
}
}

+ 9
- 7
lib/presentation/screens/login/login_page.dart View File

@@ -35,10 +35,10 @@ class _LoginPageState extends State<LoginPage> {
@override
void initState() {
super.initState();
// if (kDebugMode) {
// loginBloc.usernameCtl.text = 'quanly1';
// loginBloc.passwordCtl.text = 'Abcd@1234';
// }
if (kDebugMode) {
loginBloc.usernameCtl.text = 'quanly1';
loginBloc.passwordCtl.text = '1234567890';
}
prepareData();
}

@@ -137,7 +137,7 @@ class _LoginPageState extends State<LoginPage> {
right: 40.w,
left: 40.w,
child: Image.asset(
AssetPNG.logo,
AssetPNG.logoWithSlogan,
height: 150,
),
),
@@ -216,7 +216,8 @@ class _LoginPageState extends State<LoginPage> {
ValueListenableBuilder<String>(
valueListenable: loginErrorMessage,
builder: (context, errorMessage, _) {
if (Validators.stringNotNullOrEmpty(errorMessage)) {
if (Validators.stringNotNullOrEmpty(
errorMessage)) {
return Container(
decoration: BoxDecoration(
color: AppColors.semantic7,
@@ -225,7 +226,8 @@ class _LoginPageState extends State<LoginPage> {
margin: const EdgeInsets.only(
top: 16,
),
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
padding: const EdgeInsets.symmetric(
vertical: 4, horizontal: 8),
child: Row(
children: [
SvgPicture.asset(AssetSVG.icWarning),

+ 0
- 1
lib/presentation/screens/notification/sc_notification.dart View File

@@ -284,7 +284,6 @@ class ItemInfinityWidget extends StatelessWidget {
isEdit: true,
cropId: item.tbCropId ?? -1,
activityId: item.externalId ?? -1,
activityType: item.activityTypeName ?? '',
title: 'ActionScreen',
idAction: item.activityTypeId ?? -1,
));

+ 2
- 3
lib/presentation/screens/plot_detail/sc_plot_action.dart View File

@@ -53,7 +53,6 @@ class _PlotActionScreenState extends State<PlotActionScreen> with AutomaticKeepA
cropId: widget.cropId ?? -1,
idAction: actionType.id ?? -1,
title: actionType.description ?? '',
activityType: actionType.name ?? '',
activityId: -1,
isEdit: false,
));
@@ -86,7 +85,7 @@ class _PlotActionScreenState extends State<PlotActionScreen> with AutomaticKeepA
color: Colors.grey[300],
),
errorWidget: (context, url, error) => Image.asset(
AppAssets.logo,
AppAssets.logoWithSlogan,
),
),
),
@@ -96,7 +95,7 @@ class _PlotActionScreenState extends State<PlotActionScreen> with AutomaticKeepA
child: Align(
alignment: Alignment.center,
child: AutoSizeText(
actionType.description ?? '',
actionType.name ?? '',
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,

+ 2
- 2
lib/presentation/screens/plot_detail/sc_plot_history.dart View File

@@ -165,7 +165,7 @@ class ItemInfinityWidget extends StatelessWidget {
return GestureDetector(
child: WidgetRowPlotInfo(
color: index % 2 == 0 ? AppColors.DEFAULT.withOpacity(0.3) : AppColors.DEFAULT.withOpacity(0.1),
name: '${item.activityTypeDescription ?? ''}',
name: '${item.activityTypeName ?? ''}',
value: item.executeDate?.fromUtcToLocal() ?? ''),
onTap: () {
if (item.activityTypeName == 'ACTIVE_TYPE_UPDATE_ENV') {
@@ -179,9 +179,9 @@ class ItemInfinityWidget extends StatelessWidget {
isEdit: true,
cropId: item.cropId ?? -1,
activityId: item.id ?? -1,
activityType: item.activityTypeName ?? '',
title: 'ActionScreen',
idAction: item.activityTypeId ?? -1,
executeDate: item.executeDate?.convertStringServerDateTimeToLocalDateTime(),
));
}
});

+ 66
- 17
lib/presentation/screens/plot_detail/sc_plot_information.dart View File

@@ -24,7 +24,8 @@ class PlotInformationScreen extends StatefulWidget {
_PlotInformationScreenState createState() => _PlotInformationScreenState();
}

class _PlotInformationScreenState extends State<PlotInformationScreen> with AutomaticKeepAliveClientMixin {
class _PlotInformationScreenState extends State<PlotInformationScreen>
with AutomaticKeepAliveClientMixin {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
GlobalKey<FormState> _formKey = GlobalKey();
TextEditingController _descriptionController = TextEditingController();
@@ -54,7 +55,8 @@ class _PlotInformationScreenState extends State<PlotInformationScreen> with Auto
default:
statusCrop = plot_status_unknown;
}
_descriptionController.text = _crop.description == null ? "" : _crop.description.toString();
_descriptionController.text =
_crop.description == null ? "" : _crop.description.toString();
if (_crop.tbDetailUsers != null) {
for (var i = 0; i < (_crop.tbDetailUsers ?? []).length; i++) {
if (i == 0) {
@@ -92,7 +94,10 @@ class _PlotInformationScreenState extends State<PlotInformationScreen> with Auto
keyboardType: TextInputType.text,
controller: _descriptionController,
decoration: InputDecoration(
labelText: "Ghi chú", hintText: 'Ghi chú', enabledBorder: UnderlineInputBorder(borderSide: BorderSide(color: Colors.grey, width: 0.35))),
labelText: "Ghi chú",
hintText: 'Ghi chú',
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.grey, width: 0.35))),
onSaved: (newValue) {
_crop.description = newValue;
},
@@ -121,7 +126,8 @@ class _PlotInformationScreenState extends State<PlotInformationScreen> with Auto
body: KeyboardDismisser(
child: StreamBuilder(
stream: getPlotInfoBloc.actions,
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
builder:
(BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.hasData) {
return Form(
key: _formKey,
@@ -130,33 +136,74 @@ class _PlotInformationScreenState extends State<PlotInformationScreen> with Auto
child: Column(
children: <Widget>[
WidgetRowPlotInfo(
color: AppColors.DEFAULT.withOpacity(0.1), name: 'Mã lô', value: '${cropPlot.tbCropDTO?.code ?? ''}'),
WidgetRowPlotInfo(color: AppColors.DEFAULT.withOpacity(0.3), name: 'Trạng thái', value: '$statusCrop'),
color: AppColors.DEFAULT.withOpacity(0.1),
name: 'Mã lô',
value: '${cropPlot.tbCropDTO?.code ?? ''}'),
WidgetRowPlotInfo(
color: AppColors.DEFAULT.withOpacity(0.3),
name: 'Trạng thái',
value: '$statusCrop'),
WidgetRowPlotInfo(
color: AppColors.DEFAULT.withOpacity(0.1),
name: 'Nhà màng',
value: '${cropPlot.tbCropDTO?.netHouseName ?? '--'}'),
value:
'${cropPlot.tbCropDTO?.netHouseName ?? '--'}'),
WidgetRowPlotInfo(
color: AppColors.DEFAULT.withOpacity(0.3), name: 'Giống', value: '${cropPlot.tbCropDTO?.suppliesName ?? '--'}'),
color: AppColors.DEFAULT.withOpacity(0.3),
name: 'Giống',
value:
'${cropPlot.tbCropDTO?.suppliesName ?? '--'}'),
WidgetRowPlotInfo(
color: AppColors.DEFAULT.withOpacity(0.1),
name: 'Ngày gieo trồng',
value: '${cropPlot.plantingDate?.fromUtcToLocal() ?? '--'}'),
name: 'Ngày ươm',
value:
'${cropPlot.sowingDate?.fromUtcToLocal() ?? '--'}'),
WidgetRowPlotInfo(
color: AppColors.DEFAULT.withOpacity(0.3), name: 'Số lượng cây trồng', value: '${cropPlot.numberPlants ?? '--'}'),
color: AppColors.DEFAULT.withOpacity(0.3),
name: 'Ngày gieo trồng',
value:
'${cropPlot.plantingDate?.fromUtcToLocal() ?? '--'}'),
WidgetRowPlotInfo(
color: AppColors.DEFAULT.withOpacity(0.1),
name: 'Số lượng cây trồng',
value: '${cropPlot.numberPlants ?? '--'}'),
WidgetRowPlotInfo(
color: AppColors.DEFAULT.withOpacity(0.3),
name: 'Số lượng cây hiện tại',
value: '${cropPlot.numberCurrentPlants ?? '--'}'),
value:
'${cropPlot.numberCurrentPlants ?? '--'}'),
WidgetRowPlotInfo(
color: AppColors.DEFAULT.withOpacity(0.1),
name: 'Số lượng loại bỏ',
value:
'${cropPlot.numberRemovals ?? '--'}'),
WidgetRowPlotInfo(
color: AppColors.DEFAULT.withOpacity(0.3),
name: 'Ngày kết thúc canh tác',
value: '${cropPlot.tbCropDTO?.endDate?.fromUtcToLocal() ?? '--'}'),
WidgetRowPlotInfo(color: AppColors.DEFAULT.withOpacity(0.1), name: 'Kỹ sư trực tiếp', value: '$technicians'),
value:
'${cropPlot.tbCropDTO?.endDate?.fromUtcToLocal() ?? '--'}'),
WidgetRowPlotInfo(
color: AppColors.DEFAULT.withOpacity(0.1),
name: 'Kỹ sư trực tiếp',
value: '$technicians'),
WidgetRowPlotInfo(
color: AppColors.DEFAULT.withOpacity(0.3),
name: 'Diện tích (m\u00B2)',
value: '${cropPlot.tbCropDTO?.areaM2 == null ? '--' : cropPlot.tbCropDTO!.areaM2!.formatNumtoStringDecimal()}'),
value:
'${cropPlot.tbCropDTO?.areaM2 == null ? '--' : cropPlot.tbCropDTO!.areaM2!.formatNumtoStringDecimal()}'),
if (cropPlot.objectParameterDataDTOS != null)
...cropPlot.objectParameterDataDTOS!
.asMap()
.entries
.map((e) {
final index = e.key;
final value = e.value;
return WidgetRowPlotInfo(
color: AppColors.DEFAULT.withOpacity(
index % 2 == 0 ? 0.1 : 0.3),
name: value.name ?? '--',
value: value.index ?? '--');
}).toList(),
SizedBox(
height: 8,
),
@@ -172,8 +219,10 @@ class _PlotInformationScreenState extends State<PlotInformationScreen> with Auto
onPressed: controller.isChanged == false
? () {}
: () {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
FocusScopeNode currentFocus =
FocusScope.of(context);
if (!currentFocus
.hasPrimaryFocus) {
currentFocus.unfocus();
}
_validateInputs();

+ 5
- 5
lib/presentation/screens/resources/bloc/supply_bloc.dart View File

@@ -4,7 +4,6 @@ import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:farm_tpf/custom_model/Supply.dart';
import 'package:farm_tpf/data/repository/repository.dart';
import 'package:meta/meta.dart';

part 'supply_event.dart';
part 'supply_state.dart';
@@ -19,7 +18,7 @@ class SupplyBloc extends Bloc<SupplyEvent, SupplyState> {
) async* {
if (event is DataFetched) {
try {
final response = await repository.getSupplies(event.type);
final response = await repository.getSupplies(event.urlSupply);
List<Supply> supplies = response.map((supply) {
if (supply.id == event.selectedId) {
supply.isSelected = true;
@@ -27,12 +26,12 @@ class SupplyBloc extends Bloc<SupplyEvent, SupplyState> {
return supply;
}).toList();
yield SupplySuccess(items: supplies);
} catch (_) {
} catch (e) {
yield SupplyFailure();
}
} else if (event is OnRefresh) {
try {
final response = await repository.getSupplies(event.type);
final response = await repository.getSupplies(event.urlSupply);
List<Supply> supplies = response.map((supply) {
if (supply.id == event.selectedId) {
supply.isSelected = true;
@@ -45,7 +44,8 @@ class SupplyBloc extends Bloc<SupplyEvent, SupplyState> {
}
} else if (event is OnSearch) {
try {
final response = await repository.getSupplies(event.type ?? '', query: event.searchString ?? '');
final response = await repository.getSupplies(event.urlSupply,
query: event.searchString ?? '');
List<Supply> supplies = response.map((supply) {
if (supply.id == event.selectedId) {
supply.isSelected = true;

+ 6
- 6
lib/presentation/screens/resources/bloc/supply_event.dart View File

@@ -9,19 +9,19 @@ abstract class SupplyEvent extends Equatable {

class DataFetched extends SupplyEvent {
final int? selectedId;
final String type;
DataFetched({this.selectedId, required this.type});
final String urlSupply;
DataFetched({this.selectedId, required this.urlSupply});
}

class OnRefresh extends SupplyEvent {
final int? selectedId;
final String type;
OnRefresh({this.selectedId, required this.type});
final String urlSupply;
OnRefresh({this.selectedId, required this.urlSupply});
}

class OnSearch extends SupplyEvent {
final String? searchString;
final int? selectedId;
final String? type;
OnSearch({this.searchString, this.selectedId, this.type});
final String urlSupply;
OnSearch({this.searchString, this.selectedId, required this.urlSupply});
}

+ 12
- 12
lib/presentation/screens/resources/sc_resource_helper.dart View File

@@ -14,17 +14,17 @@ import 'package:get/get.dart';
import 'package:farm_tpf/utils/formatter.dart';

class ResourceHelperScreen extends StatefulWidget {
final String type;
final int selectedId;
final String titleName;
final List<SuppliesUsing> currentItems;
final int currentEditId;
final String urlSupply;
ResourceHelperScreen({
required this.type,
required this.selectedId,
required this.titleName,
required this.currentItems,
required this.currentEditId,
required this.urlSupply,
});
@override
_ResourceHelperScreenState createState() => _ResourceHelperScreenState();
@@ -34,10 +34,10 @@ class _ResourceHelperScreenState extends State<ResourceHelperScreen> {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => SupplyBloc(repository: Repository())..add(DataFetched(type: widget.type, selectedId: widget.selectedId)),
create: (context) => SupplyBloc(repository: Repository())..add(DataFetched(urlSupply: widget.urlSupply, selectedId: widget.selectedId)),
child: HoldInfinityWidget(
selectedId: widget.selectedId,
type: widget.type,
urlSupply: widget.urlSupply,
titleName: widget.titleName,
currentItems: widget.currentItems,
currentEditId: widget.currentEditId,
@@ -48,12 +48,12 @@ class _ResourceHelperScreenState extends State<ResourceHelperScreen> {

class HoldInfinityWidget extends StatelessWidget {
final int selectedId;
final String type;
final String titleName;
final List<SuppliesUsing> currentItems;
final int currentEditId;
final String urlSupply;
HoldInfinityWidget(
{required this.selectedId, required this.type, required this.titleName, required this.currentItems, required this.currentEditId});
{required this.selectedId, required this.urlSupply, required this.titleName, required this.currentItems, required this.currentEditId});
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
@override
Widget build(BuildContext context) {
@@ -72,13 +72,13 @@ class HoldInfinityWidget extends StatelessWidget {
),
),
WidgetSearch(
type: type,
urlSupply: urlSupply,
selectedId: selectedId,
),
Expanded(
child: InfinityView(
selectedId: selectedId,
type: type,
urlSupply: urlSupply,
currentItems: currentItems,
currentEditId: currentEditId,
))
@@ -89,10 +89,10 @@ class HoldInfinityWidget extends StatelessWidget {

class InfinityView extends StatefulWidget {
final int selectedId;
final String type;
final String urlSupply;
final List<SuppliesUsing> currentItems;
final int currentEditId;
InfinityView({required this.selectedId, required this.type, required this.currentItems, required this.currentEditId});
InfinityView({required this.selectedId, required this.urlSupply, required this.currentItems, required this.currentEditId});
@override
_InfinityViewState createState() => _InfinityViewState();
}
@@ -103,7 +103,7 @@ class _InfinityViewState extends State<InfinityView> {
@override
void initState() {
_supplyBloc = BlocProvider.of<SupplyBloc>(context);
_supplyBloc?.add(DataFetched(type: widget.type, selectedId: widget.selectedId));
_supplyBloc?.add(DataFetched(urlSupply: widget.urlSupply, selectedId: widget.selectedId));
super.initState();
}

@@ -132,7 +132,7 @@ class _InfinityViewState extends State<InfinityView> {
itemCount: state.items?.length ?? 0,
),
onRefresh: () async {
_supplyBloc?.add(OnRefresh(type: widget.type, selectedId: widget.selectedId));
_supplyBloc?.add(OnRefresh(urlSupply: widget.urlSupply, selectedId: widget.selectedId));
});
}
return Center(

+ 4
- 4
lib/presentation/screens/resources/widget_search.dart View File

@@ -4,9 +4,9 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class WidgetSearch extends StatefulWidget {
final String? type;
final String urlSupply;
final int? selectedId;
WidgetSearch({this.type, this.selectedId});
WidgetSearch({required this.urlSupply, this.selectedId});
@override
_WidgetSearchState createState() => _WidgetSearchState();
}
@@ -54,7 +54,7 @@ class _WidgetSearchState extends State<WidgetSearch> {
BlocProvider.of<SupplyBloc>(_blocContext!).add(
OnSearch(
searchString: _searchController.text,
type: widget.type ?? '',
urlSupply: widget.urlSupply,
selectedId: widget.selectedId ?? -1,
),
);
@@ -66,7 +66,7 @@ class _WidgetSearchState extends State<WidgetSearch> {
BlocProvider.of<SupplyBloc>(_blocContext!).add(
OnSearch(
searchString: value,
type: widget.type ?? '',
urlSupply: widget.urlSupply,
selectedId: widget.selectedId ?? -1,
),
);

+ 1
- 1
lib/presentation/screens/task/bloc/task_bloc.dart View File

@@ -60,7 +60,7 @@ class TaskBloc extends Bloc<TaskEvent, TaskState> {
yield response.isEmpty
? (currentState is TaskSuccess ? currentState.copyWith(hasReachedMax: true) : TaskSuccess(items: [], hasReachedMax: true))
: (currentState is TaskSuccess
? TaskSuccess(items: (currentState.items ?? []) + response, hasReachedMax: hasReachedMax)
? currentState.copyWith(items: (currentState.items ?? []) + response, hasReachedMax: hasReachedMax)
: TaskSuccess(items: response, hasReachedMax: hasReachedMax));
} catch (e) {
isFetching = false;

+ 4
- 1
lib/presentation/screens/task/bloc/task_state.dart View File

@@ -4,7 +4,7 @@ abstract class TaskState extends Equatable {
const TaskState();

@override
List<Object> get props => [];
List<Object?> get props => [];
}

class TaskInitial extends TaskState {}
@@ -30,4 +30,7 @@ class TaskSuccess<Task> extends TaskState {
hasReachedMax: hasReachedMax ?? this.hasReachedMax,
);
}

@override
List<Object?> get props => [items, page, hasReachedMax];
}

+ 3
- 1
lib/utils/app_images.dart View File

@@ -16,6 +16,7 @@ class AssetPNG {
static const icActionPlant = baseAssets + 'icActionPlant.png';
static const icActionSpraying = baseAssets + 'icActionSpraying.png';
static const icActionUseWater = baseAssets + 'icActionUseWater.png';
static const logoWithSlogan = baseAssets + 'logoWithSlogan.png';
}

class AssetSVG {
@@ -70,7 +71,8 @@ class AssetSVG {
static const icVeterinaryMedicine = baseAssets + 'ic_veterinary_medicine.svg';
static const icMeadow = baseAssets + 'ic_meadow.svg';
static const icFoodProcessing = baseAssets + 'ic_food_processing.svg';
static const icFertilizerProduction = baseAssets + 'ic_fertilizer_production.svg';
static const icFertilizerProduction =
baseAssets + 'ic_fertilizer_production.svg';
static const icLogout = baseAssets + 'ic_logout.svg';
static const icCart = baseAssets + 'ic_cart.svg';
static const icCartChecked = baseAssets + 'ic_cart_checked.svg';

+ 2
- 0
lib/utils/const_string.dart View File

@@ -98,3 +98,5 @@ const String exception_dio_1019 =
const String exception_dio_1020 =
"Người dùng hiện tại không thể truy cập thông tin này";
const String exception_dio_1021 = "Số lượng vật tư lớn hơn trong kho";
const String exception_dio_9999 = "Đã quá thời gian sửa hoạt động";
const String exception_dio_minus_1 = "Số lượng vượt quá giới hạn";

+ 2
- 2
pubspec.lock View File

@@ -77,10 +77,10 @@ packages:
dependency: transitive
description:
name: build
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
version: "2.3.1"
build_config:
dependency: transitive
description:

+ 1
- 1
pubspec.yaml View File

@@ -2,7 +2,7 @@ name: farm_tpf
description: A new Flutter project.

publish_to: 'none'
version: 1.1.16+36
version: 1.1.18+38

environment:
sdk: ">=3.0.0 <4.0.0"

Loading…
Cancel
Save