| @@ -14,8 +14,8 @@ class ActionUIField { | |||
| String tbActivityExtendTypeCondition; | |||
| String relationTable; | |||
| String foreignKey; | |||
| int isGuidelineUsing; | |||
| int isMandatory; | |||
| bool isGuidelineUsing; | |||
| bool isMandatory; | |||
| String groupName; | |||
| ActionUIField( | |||
| @@ -0,0 +1,22 @@ | |||
| class CommonData { | |||
| String description; | |||
| int id; | |||
| String name; | |||
| bool isSelected; | |||
| CommonData({this.description, this.id, this.name, this.isSelected = false}); | |||
| CommonData.fromJson(Map<String, dynamic> json) { | |||
| description = json['description']; | |||
| id = json['id']; | |||
| name = json['name']; | |||
| } | |||
| Map<String, dynamic> toJson() { | |||
| final Map<String, dynamic> data = new Map<String, dynamic>(); | |||
| data['description'] = this.description; | |||
| data['id'] = this.id; | |||
| data['name'] = this.name; | |||
| return data; | |||
| } | |||
| } | |||
| @@ -1,5 +1,3 @@ | |||
| import 'dart:convert'; | |||
| import 'package:dio/dio.dart'; | |||
| import 'package:dio_http_cache/dio_http_cache.dart'; | |||
| import 'package:farm_tpf/custom_model/ActionType.dart'; | |||
| @@ -14,12 +12,12 @@ import 'package:farm_tpf/custom_model/TbCropDTO.dart'; | |||
| import 'package:farm_tpf/custom_model/UpdateNoti.dart'; | |||
| import 'package:farm_tpf/custom_model/WaterType.dart'; | |||
| import 'package:farm_tpf/custom_model/action_form/ActionUIForm.dart'; | |||
| import 'package:farm_tpf/custom_model/action_form/CommonData.dart'; | |||
| import 'package:farm_tpf/custom_model/user.dart'; | |||
| import 'package:farm_tpf/custom_model/user_request.dart'; | |||
| import 'package:farm_tpf/data/api/dio_provider.dart'; | |||
| import 'package:farm_tpf/data/api/rest_client.dart'; | |||
| import 'package:farm_tpf/models/PagedResult.dart'; | |||
| import 'package:farm_tpf/models/index.dart'; | |||
| import 'package:farm_tpf/utils/const_common.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| @@ -235,4 +233,16 @@ class Repository { | |||
| forceRefresh: true, maxAge: Duration(days: ConstCommon.kMaxAgeCache)); | |||
| return client.getActionUIForm(idAction, op); | |||
| } | |||
| Future<List<CommonData>> getCommonData( | |||
| {String tableSupply, String condition}) async { | |||
| var url = | |||
| '${ConstCommon.baseUrl}/api/get-data-from-table-common?tableName=$tableSupply&condition=$condition'; | |||
| final Response<List<dynamic>> _result = await dio.get(url); | |||
| var value = _result.data | |||
| .map((dynamic i) => CommonData.fromJson(i as Map<String, dynamic>)) | |||
| .toList(); | |||
| return value; | |||
| } | |||
| } | |||
| @@ -1,84 +1,137 @@ | |||
| import 'package:farm_tpf/custom_model/action_form/CommonData.dart'; | |||
| import 'package:farm_tpf/presentation/screens/actions/state_management_helper/change_dropdown_controller.dart'; | |||
| import 'package:farm_tpf/presentation/screens/resources/sc_common_data_helper.dart'; | |||
| import 'package:farm_tpf/utils/validators.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:get/get.dart'; | |||
| class DropdownSupplyWidget extends StatelessWidget { | |||
| class DropdownSupplyWidget extends StatefulWidget { | |||
| final String titleName; | |||
| final String condition; | |||
| final String hint; | |||
| final String value; | |||
| final Function onPressed; | |||
| final Function(CommonData) onPressed; | |||
| final String invalidMessage; | |||
| final String tag; | |||
| final String tbSupply; | |||
| DropdownSupplyWidget( | |||
| {@required this.hint, | |||
| {this.titleName, | |||
| @required this.hint, | |||
| @required this.tag, | |||
| this.value, | |||
| @required this.condition, | |||
| @required this.onPressed, | |||
| this.invalidMessage}); | |||
| this.invalidMessage, | |||
| @required this.tbSupply}); | |||
| @override | |||
| _DropdownSupplyWidgetState createState() => _DropdownSupplyWidgetState(); | |||
| } | |||
| class _DropdownSupplyWidgetState extends State<DropdownSupplyWidget> { | |||
| ChangeDropdownController controller; | |||
| @override | |||
| void initState() { | |||
| super.initState(); | |||
| controller = Get.put(ChangeDropdownController(), tag: widget.tag); | |||
| } | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return SizedBox( | |||
| width: double.infinity, | |||
| height: Validators.stringNotNullOrEmpty(invalidMessage) ? 85 : 65, | |||
| child: Column( | |||
| crossAxisAlignment: CrossAxisAlignment.start, | |||
| children: [ | |||
| Text( | |||
| hint ?? '', | |||
| style: TextStyle( | |||
| color: Validators.stringNotNullOrEmpty(invalidMessage) | |||
| ? Colors.red | |||
| : Colors.black54, | |||
| fontSize: 13.0), | |||
| ), | |||
| SizedBox( | |||
| return GetBuilder<ChangeDropdownController>( | |||
| tag: widget.tag, | |||
| builder: (data) { | |||
| return SizedBox( | |||
| width: double.infinity, | |||
| height: 44, | |||
| child: FlatButton( | |||
| padding: EdgeInsets.only( | |||
| top: 0.0, right: 0.0, bottom: 0.0, left: 0.0), | |||
| onPressed: onPressed, | |||
| child: 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: 0.5, | |||
| color: Validators.stringNotNullOrEmpty( | |||
| invalidMessage) | |||
| ? Colors.red | |||
| : Colors.black54)), | |||
| ), | |||
| child: Row( | |||
| children: [ | |||
| Expanded( | |||
| child: Text(value ?? hint, | |||
| style: TextStyle( | |||
| fontSize: 16.0, | |||
| color: Colors.black45))), | |||
| Icon( | |||
| Icons.arrow_drop_down, | |||
| color: Colors.grey, | |||
| ), | |||
| ], | |||
| )) | |||
| ], | |||
| )), | |||
| ), | |||
| Validators.stringNotNullOrEmpty(invalidMessage) | |||
| ? Text( | |||
| invalidMessage ?? '', | |||
| height: Validators.stringNotNullOrEmpty(widget.invalidMessage) | |||
| ? 85 | |||
| : 65, | |||
| child: Column( | |||
| crossAxisAlignment: CrossAxisAlignment.start, | |||
| children: [ | |||
| Text( | |||
| widget.hint ?? '', | |||
| style: TextStyle( | |||
| fontSize: 12.0, | |||
| color: Colors.red, | |||
| fontWeight: FontWeight.normal), | |||
| textAlign: TextAlign.left, | |||
| ) | |||
| : SizedBox(), | |||
| ], | |||
| ), | |||
| ); | |||
| color: | |||
| Validators.stringNotNullOrEmpty(widget.invalidMessage) | |||
| ? Colors.red | |||
| : Colors.black54, | |||
| fontSize: 13.0), | |||
| ), | |||
| SizedBox( | |||
| width: double.infinity, | |||
| height: 44, | |||
| child: FlatButton( | |||
| padding: EdgeInsets.only( | |||
| top: 0.0, right: 0.0, bottom: 0.0, left: 0.0), | |||
| onPressed: () { | |||
| Navigator.of(context) | |||
| .push(MaterialPageRoute( | |||
| builder: (_) => CommonDataHelperScreen( | |||
| titleName: widget.titleName ?? '', | |||
| tbSupply: widget.tbSupply ?? '', | |||
| condition: widget.condition ?? '', | |||
| selectedId: data?.selectedId ?? -1, | |||
| currentItems: [], | |||
| currentEditId: -1), | |||
| fullscreenDialog: false)) | |||
| .then((value) { | |||
| if (value != null) { | |||
| var result = value as CommonData; | |||
| controller?.change(result); | |||
| widget.onPressed(result); | |||
| } | |||
| }); | |||
| }, | |||
| child: 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: 0.5, | |||
| color: Validators.stringNotNullOrEmpty( | |||
| widget.invalidMessage) | |||
| ? Colors.red | |||
| : Colors.black54)), | |||
| ), | |||
| child: Row( | |||
| children: [ | |||
| Expanded( | |||
| child: Text( | |||
| data?.currentData?.name ?? | |||
| widget.hint, | |||
| style: TextStyle( | |||
| fontSize: 16.0, | |||
| color: Colors.black))), | |||
| Icon( | |||
| Icons.arrow_drop_down, | |||
| color: Colors.grey, | |||
| ), | |||
| ], | |||
| )) | |||
| ], | |||
| )), | |||
| ), | |||
| Validators.stringNotNullOrEmpty(widget.invalidMessage) | |||
| ? Text( | |||
| widget.invalidMessage ?? '', | |||
| style: TextStyle( | |||
| fontSize: 12.0, | |||
| color: Colors.red, | |||
| fontWeight: FontWeight.normal), | |||
| textAlign: TextAlign.left, | |||
| ) | |||
| : SizedBox(), | |||
| ], | |||
| ), | |||
| ); | |||
| }); | |||
| } | |||
| } | |||
| @@ -113,7 +113,7 @@ class _ActionScreenState extends State<ActionScreen> { | |||
| decoration: InputDecoration(labelText: field.description), | |||
| controller: textFieldControllers[field.id.toString()], | |||
| onSaved: (newValue) {}, | |||
| validator: field.isMandatory == 0 | |||
| validator: field.isMandatory | |||
| ? (String value) { | |||
| return Validators.validateNotNullOrEmpty( | |||
| value, 'Vui lòng nhập ${field.description}'); | |||
| @@ -125,7 +125,7 @@ class _ActionScreenState extends State<ActionScreen> { | |||
| hintValue: field.description, | |||
| textController: textFieldControllers[field.id.toString()], | |||
| onSaved: (newValue) {}, | |||
| validator: field.isMandatory == 1 | |||
| validator: field.isMandatory | |||
| ? (String value) { | |||
| return Validators.validNumberOrEmpty( | |||
| value, 'Vui lòng nhập ${field.description}'); | |||
| @@ -139,17 +139,20 @@ class _ActionScreenState extends State<ActionScreen> { | |||
| onSaved: (newValue) {}); | |||
| } else if (field.tbControlTypeName == 'dropdown') { | |||
| return DropdownSupplyWidget( | |||
| titleName: field.description ?? '', | |||
| tbSupply: field.tbActivityExtendTypeExternalTable ?? '', | |||
| tag: field.name, | |||
| value: field.description, | |||
| hint: | |||
| '${field.description} ${field.isMandatory == 1 ? '*' : ''}', | |||
| hint: '${field.description} ${field.isMandatory ? '*' : ''}', | |||
| condition: field.tbActivityExtendTypeCondition, | |||
| invalidMessage: '', | |||
| onPressed: () {}); | |||
| onPressed: (commonData) { | |||
| print(commonData.name); | |||
| }); | |||
| } else if (field.tbControlTypeName == 'date') { | |||
| return FieldDateWidget( | |||
| value: field.description, | |||
| hint: | |||
| '${field.description} ${field.isMandatory == 1 ? '*' : ''}', | |||
| hint: '${field.description} ${field.isMandatory ? '*' : ''}', | |||
| invalidMessage: '', | |||
| onPressed: () {}); | |||
| } else if (field.tbControlTypeName == 'radiobutton') { | |||
| @@ -0,0 +1,22 @@ | |||
| import 'package:get/get_state_manager/get_state_manager.dart'; | |||
| import 'package:farm_tpf/custom_model/action_form/CommonData.dart'; | |||
| class ChangeDropdownController extends GetxController { | |||
| CommonData currentData; | |||
| int selectedId; | |||
| String selectedName; | |||
| void initValue() { | |||
| currentData = CommonData(); | |||
| selectedId = -1; | |||
| selectedName = ''; | |||
| update(); | |||
| } | |||
| void change(CommonData data) { | |||
| currentData = data; | |||
| selectedId = data.id; | |||
| selectedName = data.name; | |||
| update(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,52 @@ | |||
| import 'dart:async'; | |||
| import 'package:bloc/bloc.dart'; | |||
| import 'package:equatable/equatable.dart'; | |||
| import 'package:farm_tpf/custom_model/action_form/CommonData.dart'; | |||
| import 'package:farm_tpf/data/repository/repository.dart'; | |||
| import 'package:meta/meta.dart'; | |||
| part 'common_data_event.dart'; | |||
| part 'common_data_state.dart'; | |||
| class CommonDataBloc extends Bloc<CommonDataEvent, CommonDataState> { | |||
| final Repository repository; | |||
| CommonDataBloc({@required this.repository}) : super(CommonDataInitial()); | |||
| @override | |||
| Stream<CommonDataState> mapEventToState( | |||
| CommonDataEvent event, | |||
| ) async* { | |||
| if (event is DataFetched) { | |||
| try { | |||
| final response = await repository.getCommonData( | |||
| tableSupply: event.tbSupply, condition: event.condition); | |||
| List<CommonData> commonDatas = response.map((data) { | |||
| if (data.id == event.selectedId) { | |||
| data.isSelected = true; | |||
| } | |||
| return data; | |||
| }).toList(); | |||
| yield CommonDataSuccess(items: commonDatas); | |||
| } catch (_) { | |||
| yield CommonDataFailure(); | |||
| } | |||
| } else if (event is OnRefresh) { | |||
| try { | |||
| final response = await repository.getCommonData( | |||
| tableSupply: event.tbSupply, condition: event.condition); | |||
| List<CommonData> commonDatas = response.map((data) { | |||
| if (data.id == event.selectedId) { | |||
| data.isSelected = true; | |||
| } | |||
| return data; | |||
| }).toList(); | |||
| yield CommonDataSuccess(items: commonDatas); | |||
| } catch (_) { | |||
| yield CommonDataFailure(); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,22 @@ | |||
| part of 'common_data_bloc.dart'; | |||
| abstract class CommonDataEvent extends Equatable { | |||
| const CommonDataEvent(); | |||
| @override | |||
| List<Object> get props => []; | |||
| } | |||
| class DataFetched extends CommonDataEvent { | |||
| final int selectedId; | |||
| final String tbSupply; | |||
| final String condition; | |||
| DataFetched({this.selectedId, this.tbSupply, this.condition}); | |||
| } | |||
| class OnRefresh extends CommonDataEvent { | |||
| final int selectedId; | |||
| final String tbSupply; | |||
| final String condition; | |||
| OnRefresh({this.selectedId, this.tbSupply, this.condition}); | |||
| } | |||
| @@ -0,0 +1,25 @@ | |||
| part of 'common_data_bloc.dart'; | |||
| abstract class CommonDataState extends Equatable { | |||
| const CommonDataState(); | |||
| @override | |||
| List<Object> get props => []; | |||
| } | |||
| class CommonDataInitial extends CommonDataState {} | |||
| class CommonDataFailure extends CommonDataState {} | |||
| class CommonDataSuccess<T> extends CommonDataState { | |||
| final List<T> items; | |||
| const CommonDataSuccess({this.items}); | |||
| CommonDataSuccess copyWith({List<T> items}) { | |||
| return CommonDataSuccess(items: items ?? this.items); | |||
| } | |||
| @override | |||
| List<Object> get props => [items]; | |||
| } | |||
| @@ -0,0 +1,206 @@ | |||
| import 'package:farm_tpf/custom_model/action_form/CommonData.dart'; | |||
| import 'package:farm_tpf/data/repository/repository.dart'; | |||
| import 'package:farm_tpf/presentation/custom_widgets/app_bar_widget.dart'; | |||
| import 'package:farm_tpf/presentation/custom_widgets/bottom_loader.dart'; | |||
| import 'package:farm_tpf/presentation/custom_widgets/loading_list_page.dart'; | |||
| import 'package:farm_tpf/presentation/custom_widgets/widget_utils.dart'; | |||
| import 'package:farm_tpf/presentation/screens/resources/bloc/bloc/common_data_bloc.dart'; | |||
| import 'package:farm_tpf/utils/const_string.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:flutter_bloc/flutter_bloc.dart'; | |||
| import 'package:get/get.dart'; | |||
| class CommonDataHelperScreen extends StatefulWidget { | |||
| final String tbSupply; | |||
| final String condition; | |||
| final int selectedId; | |||
| final String titleName; | |||
| final List<CommonData> currentItems; | |||
| final int currentEditId; | |||
| CommonDataHelperScreen( | |||
| {@required this.tbSupply, | |||
| this.condition, | |||
| @required this.selectedId, | |||
| @required this.titleName, | |||
| @required this.currentItems, | |||
| @required this.currentEditId}); | |||
| @override | |||
| _CommonDataHelperScreenState createState() => _CommonDataHelperScreenState(); | |||
| } | |||
| class _CommonDataHelperScreenState extends State<CommonDataHelperScreen> { | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return BlocProvider( | |||
| create: (context) => CommonDataBloc(repository: Repository()) | |||
| ..add(DataFetched( | |||
| tbSupply: widget.tbSupply, | |||
| condition: widget.condition, | |||
| selectedId: widget.selectedId)), | |||
| child: HoldInfinityWidget( | |||
| selectedId: widget.selectedId, | |||
| tbSupply: widget.tbSupply, | |||
| condition: widget.condition, | |||
| titleName: widget.titleName, | |||
| currentItems: widget.currentItems, | |||
| currentEditId: widget.currentEditId, | |||
| ), | |||
| ); | |||
| } | |||
| } | |||
| class HoldInfinityWidget extends StatelessWidget { | |||
| final int selectedId; | |||
| final String tbSupply; | |||
| final String condition; | |||
| final String titleName; | |||
| final List<CommonData> currentItems; | |||
| final int currentEditId; | |||
| HoldInfinityWidget( | |||
| {@required this.selectedId, | |||
| @required this.tbSupply, | |||
| this.condition, | |||
| @required this.titleName, | |||
| @required this.currentItems, | |||
| @required this.currentEditId}); | |||
| final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>(); | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return Scaffold( | |||
| backgroundColor: Colors.white, | |||
| key: _scaffoldKey, | |||
| appBar: AppBarWidget(), | |||
| body: Column( | |||
| crossAxisAlignment: CrossAxisAlignment.start, | |||
| children: <Widget>[ | |||
| Padding( | |||
| padding: const EdgeInsets.all(8.0), | |||
| child: Text( | |||
| '$titleName', | |||
| style: TextStyle(fontWeight: FontWeight.w500, fontSize: 22), | |||
| ), | |||
| ), | |||
| Expanded( | |||
| child: InfinityView( | |||
| selectedId: selectedId, | |||
| tbsupply: tbSupply, | |||
| condition: condition, | |||
| currentItems: currentItems, | |||
| currentEditId: currentEditId, | |||
| )) | |||
| ], | |||
| )); | |||
| } | |||
| } | |||
| class InfinityView extends StatefulWidget { | |||
| final int selectedId; | |||
| final String tbsupply; | |||
| final String condition; | |||
| final List<CommonData> currentItems; | |||
| final int currentEditId; | |||
| InfinityView( | |||
| {@required this.selectedId, | |||
| @required this.tbsupply, | |||
| this.condition, | |||
| @required this.currentItems, | |||
| @required this.currentEditId}); | |||
| @override | |||
| _InfinityViewState createState() => _InfinityViewState(); | |||
| } | |||
| class _InfinityViewState extends State<InfinityView> { | |||
| CommonDataBloc _commonDataBloc; | |||
| @override | |||
| void initState() { | |||
| _commonDataBloc = BlocProvider.of<CommonDataBloc>(context); | |||
| _commonDataBloc.add(DataFetched( | |||
| tbSupply: widget.tbsupply, | |||
| condition: widget.condition, | |||
| selectedId: widget.selectedId)); | |||
| super.initState(); | |||
| } | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return BlocBuilder<CommonDataBloc, CommonDataState>( | |||
| builder: (context, state) { | |||
| if (state is CommonDataFailure) { | |||
| return Center(child: Text(label_error_get_data)); | |||
| } | |||
| if (state is CommonDataSuccess) { | |||
| if (state.items.isEmpty) { | |||
| return Center(child: Text(label_list_empty)); | |||
| } | |||
| return RefreshIndicator( | |||
| child: ListView.builder( | |||
| itemBuilder: (BuildContext context, int index) { | |||
| return index >= state.items.length | |||
| ? BottomLoader() | |||
| : ItemInfinityWidget( | |||
| item: state.items[index], | |||
| currentItems: widget.currentItems, | |||
| currentEditId: widget.currentEditId, | |||
| ); | |||
| }, | |||
| itemCount: state.items.length), | |||
| onRefresh: () async { | |||
| _commonDataBloc.add(OnRefresh( | |||
| tbSupply: widget.tbsupply, | |||
| condition: widget.condition, | |||
| selectedId: widget.selectedId)); | |||
| }); | |||
| } | |||
| return Center( | |||
| child: LoadingListPage(), | |||
| ); | |||
| }, | |||
| ); | |||
| } | |||
| @override | |||
| void dispose() { | |||
| super.dispose(); | |||
| } | |||
| } | |||
| class ItemInfinityWidget extends StatelessWidget { | |||
| final CommonData item; | |||
| final List<CommonData> currentItems; | |||
| final int currentEditId; | |||
| const ItemInfinityWidget( | |||
| {Key key, | |||
| @required this.item, | |||
| @required this.currentItems, | |||
| @required this.currentEditId}) | |||
| : super(key: key); | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return GestureDetector( | |||
| child: Container( | |||
| decoration: BoxDecoration( | |||
| border: Border( | |||
| bottom: BorderSide(color: Colors.grey, width: 0.35))), | |||
| child: RadioListTile( | |||
| title: Text(item.name.toString()), | |||
| value: item, | |||
| groupValue: item?.isSelected == true ? item : null, | |||
| onChanged: (CommonData value) { | |||
| var editedId = (currentEditId > 0) ? currentEditId : -1; | |||
| if (currentItems.map((e) => e.id).contains(item.id) && | |||
| item.id != editedId) { | |||
| Utils.showSnackBarWarning( | |||
| message: | |||
| "Vật tư đã được thêm, vui lòng chọn vật tư khác"); | |||
| } else { | |||
| //close nackbar if open | |||
| if (Get.isSnackbarOpen) Get.back(); | |||
| Navigator.of(context).pop(value); | |||
| } | |||
| })), | |||
| onTap: () {}); | |||
| } | |||
| } | |||