| android:value="2" /> | android:value="2" /> | ||||
| <provider | <provider | ||||
| android:name="androidx.core.content.FileProvider" | android:name="androidx.core.content.FileProvider" | ||||
| android:authorities="au.com.homecarenet.caregiver.fileprovider" | |||||
| android:authorities="vn.azteam.farmdemo.fileprovider" | |||||
| android:exported="false" | android:exported="false" | ||||
| android:grantUriPermissions="true"> | android:grantUriPermissions="true"> | ||||
| <meta-data | <meta-data |
| CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; | CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; | ||||
| CODE_SIGN_IDENTITY = "Apple Development"; | CODE_SIGN_IDENTITY = "Apple Development"; | ||||
| CODE_SIGN_STYLE = Automatic; | CODE_SIGN_STYLE = Automatic; | ||||
| CURRENT_PROJECT_VERSION = 21; | |||||
| CURRENT_PROJECT_VERSION = 22; | |||||
| DEVELOPMENT_TEAM = C3DTD2JH94; | DEVELOPMENT_TEAM = C3DTD2JH94; | ||||
| ENABLE_BITCODE = NO; | ENABLE_BITCODE = NO; | ||||
| FRAMEWORK_SEARCH_PATHS = ( | FRAMEWORK_SEARCH_PATHS = ( | ||||
| "$(inherited)", | "$(inherited)", | ||||
| "$(PROJECT_DIR)/Flutter", | "$(PROJECT_DIR)/Flutter", | ||||
| ); | ); | ||||
| MARKETING_VERSION = 1.1.5; | |||||
| MARKETING_VERSION = 1.1.6; | |||||
| PRODUCT_BUNDLE_IDENTIFIER = vn.azteam.farmdemo; | PRODUCT_BUNDLE_IDENTIFIER = vn.azteam.farmdemo; | ||||
| PRODUCT_NAME = "$(TARGET_NAME)"; | PRODUCT_NAME = "$(TARGET_NAME)"; | ||||
| PROVISIONING_PROFILE_SPECIFIER = ""; | PROVISIONING_PROFILE_SPECIFIER = ""; | ||||
| CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; | CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; | ||||
| CODE_SIGN_IDENTITY = "Apple Development"; | CODE_SIGN_IDENTITY = "Apple Development"; | ||||
| CODE_SIGN_STYLE = Automatic; | CODE_SIGN_STYLE = Automatic; | ||||
| CURRENT_PROJECT_VERSION = 21; | |||||
| CURRENT_PROJECT_VERSION = 22; | |||||
| DEVELOPMENT_TEAM = C3DTD2JH94; | DEVELOPMENT_TEAM = C3DTD2JH94; | ||||
| ENABLE_BITCODE = NO; | ENABLE_BITCODE = NO; | ||||
| FRAMEWORK_SEARCH_PATHS = ( | FRAMEWORK_SEARCH_PATHS = ( | ||||
| "$(inherited)", | "$(inherited)", | ||||
| "$(PROJECT_DIR)/Flutter", | "$(PROJECT_DIR)/Flutter", | ||||
| ); | ); | ||||
| MARKETING_VERSION = 1.1.5; | |||||
| MARKETING_VERSION = 1.1.6; | |||||
| PRODUCT_BUNDLE_IDENTIFIER = vn.azteam.farmdemo; | PRODUCT_BUNDLE_IDENTIFIER = vn.azteam.farmdemo; | ||||
| PRODUCT_NAME = "$(TARGET_NAME)"; | PRODUCT_NAME = "$(TARGET_NAME)"; | ||||
| PROVISIONING_PROFILE_SPECIFIER = ""; | PROVISIONING_PROFILE_SPECIFIER = ""; | ||||
| CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; | CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; | ||||
| CODE_SIGN_IDENTITY = "Apple Development"; | CODE_SIGN_IDENTITY = "Apple Development"; | ||||
| CODE_SIGN_STYLE = Automatic; | CODE_SIGN_STYLE = Automatic; | ||||
| CURRENT_PROJECT_VERSION = 21; | |||||
| CURRENT_PROJECT_VERSION = 22; | |||||
| DEVELOPMENT_TEAM = C3DTD2JH94; | DEVELOPMENT_TEAM = C3DTD2JH94; | ||||
| ENABLE_BITCODE = NO; | ENABLE_BITCODE = NO; | ||||
| FRAMEWORK_SEARCH_PATHS = ( | FRAMEWORK_SEARCH_PATHS = ( | ||||
| "$(inherited)", | "$(inherited)", | ||||
| "$(PROJECT_DIR)/Flutter", | "$(PROJECT_DIR)/Flutter", | ||||
| ); | ); | ||||
| MARKETING_VERSION = 1.1.5; | |||||
| MARKETING_VERSION = 1.1.6; | |||||
| PRODUCT_BUNDLE_IDENTIFIER = vn.azteam.farmdemo; | PRODUCT_BUNDLE_IDENTIFIER = vn.azteam.farmdemo; | ||||
| PRODUCT_NAME = "$(TARGET_NAME)"; | PRODUCT_NAME = "$(TARGET_NAME)"; | ||||
| PROVISIONING_PROFILE_SPECIFIER = ""; | PROVISIONING_PROFILE_SPECIFIER = ""; |
| import 'package:farm_tpf/presentation/screens/codes/models/stamp_filter_request.dart'; | import 'package:farm_tpf/presentation/screens/codes/models/stamp_filter_request.dart'; | ||||
| import 'package:farm_tpf/presentation/screens/codes/models/stamp_request.dart'; | import 'package:farm_tpf/presentation/screens/codes/models/stamp_request.dart'; | ||||
| import 'package:farm_tpf/presentation/screens/codes/models/stamp_timeline.dart'; | import 'package:farm_tpf/presentation/screens/codes/models/stamp_timeline.dart'; | ||||
| import 'package:farm_tpf/presentation/screens/task/models/employee.dart'; | |||||
| import 'package:farm_tpf/presentation/screens/task/models/task_request.dart'; | |||||
| import 'package:farm_tpf/utils/const_common.dart'; | import 'package:farm_tpf/utils/const_common.dart'; | ||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||
| import '../../presentation/screens/codes/models/stamp_type.dart'; | import '../../presentation/screens/codes/models/stamp_type.dart'; | ||||
| import '../../presentation/screens/login/models/request_user.dart'; | import '../../presentation/screens/login/models/request_user.dart'; | ||||
| import '../../presentation/screens/login/models/response_user.dart'; | import '../../presentation/screens/login/models/response_user.dart'; | ||||
| import '../../presentation/screens/task/models/task.dart'; | |||||
| import '../../presentation/screens/task/models/task_filter_request.dart'; | |||||
| import '../../presentation/screens/task/models/task_update_request.dart'; | |||||
| import '../api/app_exception.dart'; | import '../api/app_exception.dart'; | ||||
| class Repository { | class Repository { | ||||
| rethrow; | rethrow; | ||||
| } | } | ||||
| } | } | ||||
| // Task | |||||
| Future<List<Task>> tasks({ | |||||
| int page = 0, | |||||
| int size = 20, | |||||
| required TaskFilterRequest filter, | |||||
| }) async { | |||||
| try { | |||||
| // var url = '${ConstCommon.baseUrl}/api/tb-todo-lists/list?page=$page&size=$size&sort=createdDate,${filter.sort ?? 'asc'}'; | |||||
| var url = '${ConstCommon.baseUrl}/api/tb-todo-lists/list?page=$page&size=$size'; | |||||
| var res = await dio.post(url, data: { | |||||
| // 'status': filter.status, | |||||
| }); | |||||
| return (res.data as List).map((e) => Task.fromJson(e)).toList(); | |||||
| } catch (e) { | |||||
| rethrow; | |||||
| } | |||||
| } | |||||
| Future<void> createTask( | |||||
| Function(dynamic) onSuccess, | |||||
| Function(String) onError, { | |||||
| required RequestTask item, | |||||
| }) async { | |||||
| try { | |||||
| var url = '${ConstCommon.baseUrl}/api/tb-todo-lists'; | |||||
| await dio.post(url, data: item).then( | |||||
| (value) { | |||||
| onSuccess(value); | |||||
| }, | |||||
| ).catchError((e) { | |||||
| onError(AppException.handleError(e)); | |||||
| }); | |||||
| } catch (e) { | |||||
| onError(AppException.handleError(e)); | |||||
| } | |||||
| } | |||||
| Future<void> updateTask( | |||||
| Function(dynamic) onSuccess, | |||||
| Function(String) onError, { | |||||
| required RequestTaskUpdate item, | |||||
| }) async { | |||||
| try { | |||||
| var url = '${ConstCommon.baseUrl}/api/tb-todo-lists/'; | |||||
| await dio.put(url, data: item).then( | |||||
| (value) { | |||||
| onSuccess(value); | |||||
| }, | |||||
| ).catchError((e) { | |||||
| onError(AppException.handleError(e)); | |||||
| }); | |||||
| } catch (e) { | |||||
| onError(AppException.handleError(e)); | |||||
| } | |||||
| } | |||||
| Future<List<Employee>> getEmployees() async { | |||||
| try { | |||||
| var url = '${ConstCommon.baseUrl}/api/get-all-users-by-login-info'; | |||||
| var res = await dio.get( | |||||
| url, | |||||
| ); | |||||
| return (res.data as List).map((e) => Employee.fromJson(e)).toList(); | |||||
| } catch (e) { | |||||
| rethrow; | |||||
| } | |||||
| } | |||||
| } | } |
| // import 'data/repository/auth_repository.dart'; | // import 'data/repository/auth_repository.dart'; | ||||
| import 'data/repository/authentication_repository.dart'; | import 'data/repository/authentication_repository.dart'; | ||||
| import 'environment/app_config.dart'; | import 'environment/app_config.dart'; | ||||
| import 'presentation/screens/task/bloc/task_bloc.dart'; | |||||
| final GlobalKey<NavigatorState> globalNavigator = GlobalKey<NavigatorState>(); | final GlobalKey<NavigatorState> globalNavigator = GlobalKey<NavigatorState>(); | ||||
| Future<void> main() async { | Future<void> main() async { | ||||
| Repository(), | Repository(), | ||||
| ), | ), | ||||
| ), | ), | ||||
| BlocProvider( | |||||
| create: (_) => TaskBloc( | |||||
| Repository(), | |||||
| ), | |||||
| ), | |||||
| BlocProvider( | BlocProvider( | ||||
| create: (_) => DetailStampCubit(), | create: (_) => DetailStampCubit(), | ||||
| ), | ), |
| import 'package:flutter/material.dart'; | |||||
| import '../../../themes/app_dimension.dart'; | |||||
| import '../../../themes/app_colors.dart'; | |||||
| import '../../../themes/styles_text.dart'; | |||||
| class CheckboxWidget extends StatefulWidget { | |||||
| final String title; | |||||
| final bool isChecked; | |||||
| final Function(bool) onChange; | |||||
| final TextStyle? style; | |||||
| const CheckboxWidget({ | |||||
| Key? key, | |||||
| required this.title, | |||||
| required this.isChecked, | |||||
| required this.onChange, | |||||
| this.style, | |||||
| }) : super(key: key); | |||||
| @override | |||||
| _CheckboxWidgetState createState() => _CheckboxWidgetState(); | |||||
| } | |||||
| class _CheckboxWidgetState extends State<CheckboxWidget> { | |||||
| var checked = false; | |||||
| @override | |||||
| void initState() { | |||||
| super.initState(); | |||||
| checked = widget.isChecked; | |||||
| } | |||||
| @override | |||||
| Widget build(BuildContext context) { | |||||
| return InkWell( | |||||
| onTap: () { | |||||
| setState(() { | |||||
| checked = !checked; | |||||
| widget.onChange(checked); | |||||
| }); | |||||
| }, | |||||
| child: Row( | |||||
| children: [ | |||||
| checked | |||||
| ? Icon( | |||||
| Icons.check_box, | |||||
| color: AppColors.primary1, | |||||
| ) | |||||
| : Icon( | |||||
| Icons.check_box_outline_blank, | |||||
| color: AppColors.neutral1, | |||||
| ), | |||||
| SizedBox( | |||||
| width: 8.w, | |||||
| ), | |||||
| Text( | |||||
| '${widget.title}', | |||||
| style: widget.style ?? StylesText.body1, | |||||
| ), | |||||
| ], | |||||
| ), | |||||
| ); | |||||
| } | |||||
| } |
| widget.onPressed(widget.searchController.text); | widget.onPressed(widget.searchController.text); | ||||
| }, | }, | ||||
| ), | ), | ||||
| hintText: 'Tìm theo mã, tên lô', | |||||
| hintText: 'Tìm kiếm ...', | |||||
| hintStyle: TextStyle(color: Colors.grey[500])), | hintStyle: TextStyle(color: Colors.grey[500])), | ||||
| onSubmitted: (value) { | onSubmitted: (value) { | ||||
| widget.onPressed(widget.searchController.text); | widget.onPressed(widget.searchController.text); |
| import 'package:farm_tpf/presentation/screens/plot_detail/sc_plot_history.dart'; | import 'package:farm_tpf/presentation/screens/plot_detail/sc_plot_history.dart'; | ||||
| import 'package:farm_tpf/presentation/screens/plot_detail/sc_plot_parameter.dart'; | import 'package:farm_tpf/presentation/screens/plot_detail/sc_plot_parameter.dart'; | ||||
| import 'package:farm_tpf/presentation/screens/task/task_page.dart'; | |||||
| import 'package:farm_tpf/utils/const_color.dart'; | import 'package:farm_tpf/utils/const_color.dart'; | ||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||
| import 'package:get/get.dart'; | import 'package:get/get.dart'; | ||||
| Container( | Container( | ||||
| padding: EdgeInsets.only(top: 8, bottom: 8), | padding: EdgeInsets.only(top: 8, bottom: 8), | ||||
| child: Text( | child: Text( | ||||
| 'Chỉ số', | |||||
| 'Công việc', | |||||
| style: TextStyle(color: selectedTab.index == 0 ? AppColors.DEFAULT : Colors.black, fontSize: 12), | style: TextStyle(color: selectedTab.index == 0 ? AppColors.DEFAULT : Colors.black, fontSize: 12), | ||||
| ), | ), | ||||
| ), | ), | ||||
| body: TabBarView( | body: TabBarView( | ||||
| controller: tabbarController, | controller: tabbarController, | ||||
| children: [ | children: [ | ||||
| PlotParameterScreen( | |||||
| // PlotParameterScreen( | |||||
| // cropId: widget.cropId ?? -1, | |||||
| // ), | |||||
| TaskPage( | |||||
| cropId: widget.cropId ?? -1, | cropId: widget.cropId ?? -1, | ||||
| ), | ), | ||||
| PlotInformationScreen( | PlotInformationScreen( |
| import 'package:bloc/bloc.dart'; | |||||
| import 'package:equatable/equatable.dart'; | |||||
| import 'package:farm_tpf/presentation/screens/task/models/task_update_request.dart'; | |||||
| import 'package:flutter/foundation.dart'; | |||||
| import 'package:flutter/material.dart'; | |||||
| import '../../../../data/api/app_exception.dart'; | |||||
| import '../../../../data/repository/repository.dart'; | |||||
| import '../../../../models/item_dropdown.dart'; | |||||
| import '../../../../utils/const_common.dart'; | |||||
| import '../../../../utils/helpers.dart'; | |||||
| import '../../../../utils/utils.dart'; | |||||
| import '../../../custom_widgets/widget_utils.dart'; | |||||
| import '../models/task.dart'; | |||||
| import '../models/task_filter_request.dart'; | |||||
| part 'task_event.dart'; | |||||
| part 'task_state.dart'; | |||||
| class TaskBloc extends Bloc<TaskEvent, TaskState> { | |||||
| final Repository repository; | |||||
| int pageSize = 20; | |||||
| TaskBloc(this.repository) : super(TaskInitial()); | |||||
| var status = ValueNotifier( | |||||
| TaskStatus.values | |||||
| .map( | |||||
| (e) => ItemDropDown(key: describeEnum(e), value: Helpers.getTaskStatus(describeEnum(e))), | |||||
| ) | |||||
| .toList(), | |||||
| ); | |||||
| var selectedStatus = ValueNotifier( | |||||
| TaskStatus.values | |||||
| .map( | |||||
| (e) => ItemDropDown(key: describeEnum(e), value: Helpers.getTaskStatus(describeEnum(e))), | |||||
| ) | |||||
| .toList(), | |||||
| ); | |||||
| var sort = ValueNotifier(describeEnum(SortType.asc)); | |||||
| @override | |||||
| Stream<TaskState> mapEventToState( | |||||
| TaskEvent event, | |||||
| ) async* { | |||||
| if (event is DataFetched && !(state is TaskSuccess && ((state as TaskSuccess).hasReachedMax ?? false))) { | |||||
| try { | |||||
| if (state is TaskInitial) { | |||||
| yield (TaskLoading()); | |||||
| final response = await getListTask(0); | |||||
| yield TaskSuccess( | |||||
| items: response, | |||||
| page: 0, | |||||
| hasReachedMax: response.length < pageSize ? true : false, | |||||
| ); | |||||
| } | |||||
| if (state is TaskSuccess) { | |||||
| final currentState = state as TaskSuccess; | |||||
| if (currentState.hasReachedMax ?? false) { | |||||
| return; | |||||
| } | |||||
| int page = (currentState.page ?? 0) + 1; | |||||
| final response = await getListTask(page); | |||||
| yield response.isEmpty | |||||
| ? currentState.copyWith(hasReachedMax: true) | |||||
| : TaskSuccess( | |||||
| items: (currentState.items ?? []) + response, | |||||
| page: (currentState.page ?? 0) + 1, | |||||
| hasReachedMax: false, | |||||
| ); | |||||
| } | |||||
| } catch (e) { | |||||
| var errorString = AppException.handleError(e); | |||||
| yield (TaskFailure(errorString: errorString)); | |||||
| } | |||||
| } | |||||
| if (event is OnRefresh) { | |||||
| try { | |||||
| yield (TaskLoading()); | |||||
| final response = await getListTask(0); | |||||
| var items = <Task>[]; | |||||
| response.forEach((element) { | |||||
| items.add(Task.clone(element)); | |||||
| }); | |||||
| yield TaskSuccess( | |||||
| items: items, | |||||
| page: 0, | |||||
| hasReachedMax: items.length < pageSize ? true : false, | |||||
| ); | |||||
| } catch (e) { | |||||
| yield (TaskFailure(errorString: AppException.handleError(e))); | |||||
| } | |||||
| } else if (event is OnSearch) { | |||||
| try { | |||||
| yield (TaskLoading()); | |||||
| final response = await getListTask(0); | |||||
| yield TaskSuccess(items: response, page: 0, hasReachedMax: response.length < pageSize ? true : false); | |||||
| } catch (e) { | |||||
| yield (TaskFailure(errorString: AppException.handleError(e))); | |||||
| } | |||||
| } | |||||
| } | |||||
| Future<List<Task>> getListTask(int page) async { | |||||
| var filter = TaskFilterRequest() | |||||
| ..sort = sort.value | |||||
| ..status = selectedStatus.value.map((e) => e.key ?? '').toList(); | |||||
| return await repository.tasks(page: 0, filter: filter); | |||||
| } | |||||
| Future<void> updateStatusTask( | |||||
| RequestTaskUpdate task, { | |||||
| required Function onSuccess, | |||||
| }) async { | |||||
| print(task.toJson()); | |||||
| UtilWidget.showLoading(); | |||||
| await repository.updateTask( | |||||
| (success) { | |||||
| UtilWidget.hideDialog(); | |||||
| Utils.showSnackBarSuccess(); | |||||
| onSuccess(); | |||||
| }, | |||||
| (errorMessage) { | |||||
| UtilWidget.hideDialog(); | |||||
| Utils.showSnackBarError(); | |||||
| }, | |||||
| item: task, | |||||
| ); | |||||
| } | |||||
| } |
| part of 'task_bloc.dart'; | |||||
| abstract class TaskEvent extends Equatable { | |||||
| const TaskEvent(); | |||||
| @override | |||||
| List<Object> get props => []; | |||||
| } | |||||
| class DataFetched extends TaskEvent {} | |||||
| class OnRefresh extends TaskEvent {} | |||||
| class OnSearch extends TaskEvent { | |||||
| final String searchString; | |||||
| OnSearch({required this.searchString}); | |||||
| } |
| part of 'task_bloc.dart'; | |||||
| abstract class TaskState extends Equatable { | |||||
| const TaskState(); | |||||
| @override | |||||
| List<Object> get props => []; | |||||
| } | |||||
| class TaskInitial extends TaskState {} | |||||
| class TaskLoading extends TaskState {} | |||||
| class TaskFailure extends TaskState { | |||||
| final String errorString; | |||||
| TaskFailure({required this.errorString}); | |||||
| } | |||||
| class TaskSuccess<Task> extends TaskState { | |||||
| final List<Task>? items; | |||||
| final int? page; | |||||
| final bool? hasReachedMax; | |||||
| const TaskSuccess({this.items, this.page, this.hasReachedMax}); | |||||
| TaskSuccess copyWith({List<Task>? items, int? page, bool? hasReachedMax}) { | |||||
| return TaskSuccess( | |||||
| items: items ?? this.items, | |||||
| page: page ?? this.page, | |||||
| hasReachedMax: hasReachedMax ?? this.hasReachedMax, | |||||
| ); | |||||
| } | |||||
| } |
| import 'package:cached_network_image/cached_network_image.dart'; | |||||
| import 'package:farm_tpf/models/item_dropdown.dart'; | |||||
| import 'package:farm_tpf/presentation/screens/codes/models/stamp_type.dart'; | |||||
| import 'package:farm_tpf/presentation/screens/codes/widgets/item_column.dart'; | |||||
| import 'package:flutter/material.dart'; | |||||
| import 'package:flutter/scheduler.dart'; | |||||
| import 'package:flutter/services.dart'; | |||||
| import 'package:flutter/src/widgets/framework.dart'; | |||||
| import 'package:flutter/src/widgets/placeholder.dart'; | |||||
| import 'package:flutter_bloc/flutter_bloc.dart'; | |||||
| import 'package:get/get.dart'; | |||||
| import 'package:keyboard_dismisser/keyboard_dismisser.dart'; | |||||
| import '../../../utils/utils.dart'; | |||||
| import '../../custom_widgets/app_bar_widget.dart'; | |||||
| import '../../custom_widgets/button_widget.dart'; | |||||
| import '../../custom_widgets/date_picker/date_picker_widget.dart'; | |||||
| import '../../custom_widgets/dropdown/dropdown_bottom_sheet.dart'; | |||||
| import '../../custom_widgets/textfield/text_field_normal.dart'; | |||||
| import 'cubit/create_task_cubit.dart'; | |||||
| class CreateTaskPage extends StatefulWidget { | |||||
| final int cropId; | |||||
| const CreateTaskPage({ | |||||
| super.key, | |||||
| required this.cropId, | |||||
| }); | |||||
| @override | |||||
| State<CreateTaskPage> createState() => _CreateTaskPageState(); | |||||
| } | |||||
| class _CreateTaskPageState extends State<CreateTaskPage> { | |||||
| final bloc = CreateTaskCubit(); | |||||
| @override | |||||
| void initState() { | |||||
| super.initState(); | |||||
| bloc.preparedData(cropId: widget.cropId); | |||||
| } | |||||
| @override | |||||
| void dispose() { | |||||
| bloc.dispose(); | |||||
| super.dispose(); | |||||
| } | |||||
| @override | |||||
| Widget build(BuildContext context) { | |||||
| return Scaffold( | |||||
| appBar: AppBarWidget(), | |||||
| body: BlocListener<CreateTaskCubit, CreateTaskState>( | |||||
| bloc: bloc, | |||||
| listener: ((context, state) { | |||||
| if (state is CreateTaskLoading) { | |||||
| SchedulerBinding.instance.addPostFrameCallback((timeTask) { | |||||
| UtilWidget.showLoading(); | |||||
| }); | |||||
| } else if (state is CreateTaskFailure) { | |||||
| SchedulerBinding.instance.addPostFrameCallback((timeTask) { | |||||
| UtilWidget.hideLoading(); | |||||
| // UtilWidget.showToastError(state.errorMessage); | |||||
| }); | |||||
| } else if (state is CreateTaskPrepareDataSuccessful) { | |||||
| SchedulerBinding.instance.addPostFrameCallback((timeTask) { | |||||
| UtilWidget.hideLoading(); | |||||
| }); | |||||
| } | |||||
| }), | |||||
| child: KeyboardDismisser( | |||||
| child: Container( | |||||
| child: Form( | |||||
| key: bloc.formKey, | |||||
| child: Column( | |||||
| children: [ | |||||
| Expanded( | |||||
| child: _widgetBody(), | |||||
| ), | |||||
| Padding( | |||||
| padding: const EdgeInsets.all(8.0), | |||||
| child: ButtonWidget( | |||||
| title: 'Cập nhật', | |||||
| onPressed: () { | |||||
| bloc.onSubmit(widget.cropId); | |||||
| }, | |||||
| ), | |||||
| ), | |||||
| ], | |||||
| ), | |||||
| ), | |||||
| ), | |||||
| ), | |||||
| ), | |||||
| ); | |||||
| } | |||||
| Widget _widgetBody() { | |||||
| return Container( | |||||
| padding: const EdgeInsets.all(16), | |||||
| child: SingleChildScrollView( | |||||
| child: Column( | |||||
| crossAxisAlignment: CrossAxisAlignment.start, | |||||
| children: [ | |||||
| ItemColumnWidget( | |||||
| title: 'Giao việc cho', | |||||
| child: ValueListenableBuilder<String>( | |||||
| valueListenable: bloc.selectedPEmployee, | |||||
| builder: (context, selected, _) { | |||||
| return ValueListenableBuilder<List<ItemDropDown>>( | |||||
| valueListenable: bloc.employees, | |||||
| builder: (context, employees, _) { | |||||
| return DropdownBottomSheet( | |||||
| dataSources: employees, | |||||
| initValue: selected, | |||||
| onSelected: (val) { | |||||
| bloc.selectedPEmployee.value = val.key ?? ''; | |||||
| }, | |||||
| hint: 'Giao việc cho', | |||||
| ); | |||||
| }, | |||||
| ); | |||||
| }, | |||||
| ), | |||||
| ), | |||||
| const SizedBox( | |||||
| height: 8, | |||||
| ), | |||||
| ItemColumnWidget( | |||||
| title: 'Tiêu đề', | |||||
| child: TextFieldNormal( | |||||
| controller: bloc.titleNameCtl, | |||||
| maxLines: 1, | |||||
| hint: 'Tiêu đề', | |||||
| ), | |||||
| ), | |||||
| const SizedBox( | |||||
| height: 8, | |||||
| ), | |||||
| ItemColumnWidget( | |||||
| title: 'Mô tả công việc', | |||||
| child: TextFieldNormal( | |||||
| controller: bloc.detailCtl, | |||||
| maxLines: 3, | |||||
| hint: 'Mô tả công việc', | |||||
| ), | |||||
| ), | |||||
| const SizedBox( | |||||
| height: 8, | |||||
| ), | |||||
| const SizedBox(height: 8), | |||||
| ValueListenableBuilder<DateTime>( | |||||
| valueListenable: bloc.deadline, | |||||
| builder: (context, dexuat, _) { | |||||
| return ItemColumnWidget( | |||||
| title: 'Hạn chót', | |||||
| child: DatePickerWidget( | |||||
| initDateTime: dexuat, | |||||
| onUpdateDateTime: (selectedDate) { | |||||
| if (selectedDate != null) { | |||||
| bloc.deadline.value = selectedDate; | |||||
| } | |||||
| }, | |||||
| ), | |||||
| ); | |||||
| }, | |||||
| ), | |||||
| const SizedBox( | |||||
| height: 16, | |||||
| ), | |||||
| ], | |||||
| ), | |||||
| ), | |||||
| ); | |||||
| } | |||||
| } |
| import 'package:bloc/bloc.dart'; | |||||
| import 'package:equatable/equatable.dart'; | |||||
| import 'package:farm_tpf/presentation/screens/task/models/employee.dart'; | |||||
| import 'package:farm_tpf/utils/formatter.dart'; | |||||
| import 'package:flutter/material.dart'; | |||||
| import 'package:get/get.dart'; | |||||
| import '../../../../data/api/app_exception.dart'; | |||||
| import '../../../../data/repository/repository.dart'; | |||||
| import '../../../../models/item_dropdown.dart'; | |||||
| import '../../../../utils/utils.dart'; | |||||
| import '../../../custom_widgets/widget_utils.dart'; | |||||
| import '../models/task_request.dart'; | |||||
| part 'create_task_state.dart'; | |||||
| class CreateTaskCubit extends Cubit<CreateTaskState> { | |||||
| CreateTaskCubit() : super(CreateTaskInitial()); | |||||
| final repository = Repository(); | |||||
| final formKey = GlobalKey<FormState>(); | |||||
| final titleNameCtl = TextEditingController(); | |||||
| final detailCtl = TextEditingController(); | |||||
| var deadline = ValueNotifier(DateTime.now()); | |||||
| var employeeRaws = <Employee>[]; | |||||
| var employees = ValueNotifier(<ItemDropDown>[]); | |||||
| var selectedPEmployee = ValueNotifier(''); | |||||
| // var existedCreateTask = UpdateCreateTask(); | |||||
| void dispose() { | |||||
| titleNameCtl.dispose(); | |||||
| detailCtl.dispose(); | |||||
| } | |||||
| Future<void> preparedData({required int cropId}) async { | |||||
| try { | |||||
| await Future.delayed(const Duration(seconds: 0)); | |||||
| emit(CreateTaskLoading()); | |||||
| employeeRaws = await repository.getEmployees(); | |||||
| employees.value = employeeRaws | |||||
| .map( | |||||
| (e) => ItemDropDown(key: e.id?.toString(), value: e.name), | |||||
| ) | |||||
| .toList(); | |||||
| emit(CreateTaskPrepareDataSuccessful()); | |||||
| } catch (e) { | |||||
| emit(CreateTaskFailure(AppException.handleError(e))); | |||||
| } | |||||
| } | |||||
| Future<void> onSubmit(int cropId) async { | |||||
| if (formKey.currentState!.validate()) { | |||||
| if (selectedPEmployee.value.isEmpty) { | |||||
| Utils.showSnackBarWarning(message: 'Vui lòng chọn nhân viên'); | |||||
| return; | |||||
| } else if (titleNameCtl.text.trim().isEmpty) { | |||||
| Utils.showSnackBarWarning(message: 'Vui lòng nhập tiêu đề'); | |||||
| return; | |||||
| } else if (detailCtl.text.trim().isEmpty) { | |||||
| Utils.showSnackBarWarning(message: 'Vui lòng nhập nội dung'); | |||||
| return; | |||||
| } | |||||
| var requestTask = RequestTask(); | |||||
| requestTask | |||||
| ..cropId = cropId | |||||
| ..title = titleNameCtl.text | |||||
| ..detail = detailCtl.text | |||||
| ..userAssignedId = int.tryParse(selectedPEmployee.value) | |||||
| ..deadline = deadline.value.convertLocalDateTimeToStringUtcDateTime(); | |||||
| print(requestTask.toJson()); | |||||
| UtilWidget.showLoading(); | |||||
| await repository.createTask( | |||||
| (success) { | |||||
| UtilWidget.hideDialog(); | |||||
| Get.back(result: 'ok'); | |||||
| Utils.showSnackBarSuccess(); | |||||
| }, | |||||
| (errorMessage) { | |||||
| UtilWidget.hideDialog(); | |||||
| Utils.showSnackBarError(); | |||||
| }, | |||||
| item: requestTask, | |||||
| ); | |||||
| } | |||||
| } | |||||
| } |
| part of 'create_task_cubit.dart'; | |||||
| abstract class CreateTaskState extends Equatable { | |||||
| const CreateTaskState(); | |||||
| @override | |||||
| List<Object> get props => []; | |||||
| } | |||||
| class CreateTaskInitial extends CreateTaskState {} | |||||
| class CreateTaskLoading extends CreateTaskState {} | |||||
| class CreateTaskFailure extends CreateTaskState { | |||||
| final String errorMessage; | |||||
| CreateTaskFailure(this.errorMessage); | |||||
| } | |||||
| class CreateTaskPrepareDataSuccessful extends CreateTaskState { | |||||
| CreateTaskPrepareDataSuccessful(); | |||||
| } |
| class Employee { | |||||
| int? id; | |||||
| String? name; | |||||
| Employee({ | |||||
| this.id, | |||||
| this.name, | |||||
| }); | |||||
| Employee.fromJson(Map<String, dynamic> json) { | |||||
| id = json['id']; | |||||
| name = json['fullName']; | |||||
| } | |||||
| Map<String, dynamic> toJson() { | |||||
| final Map<String, dynamic> data = new Map<String, dynamic>(); | |||||
| data['id'] = this.id; | |||||
| data['fullName'] = this.name; | |||||
| return data; | |||||
| } | |||||
| } |
| class Task { | |||||
| int? id; | |||||
| String? title; | |||||
| String? description; | |||||
| String? dueDate; | |||||
| String? executeDate; | |||||
| Assigned? assigned; | |||||
| bool? isCompleted; | |||||
| Task({ | |||||
| this.id, | |||||
| this.title, | |||||
| this.description, | |||||
| this.dueDate, | |||||
| this.executeDate, | |||||
| this.assigned, | |||||
| this.isCompleted, | |||||
| }); | |||||
| Task.fromJson(Map<String, dynamic> json) { | |||||
| title = json['title']; | |||||
| id = json['id']; | |||||
| description = json['detail']; | |||||
| dueDate = json['deadline']; | |||||
| executeDate = json['executeDate']; | |||||
| assigned = json['assigned'] != null ? new Assigned.fromJson(json['assigned']) : null; | |||||
| isCompleted = json['completed']; | |||||
| } | |||||
| Map<String, dynamic> toJson() { | |||||
| final Map<String, dynamic> data = new Map<String, dynamic>(); | |||||
| data['id'] = this.id; | |||||
| data['title'] = this.title; | |||||
| data['detail'] = this.description; | |||||
| data['deadline'] = this.dueDate; | |||||
| data['executeDate'] = this.executeDate; | |||||
| if (this.assigned != null) { | |||||
| data['assigned'] = this.assigned?.toJson(); | |||||
| } | |||||
| data['completed'] = this.isCompleted; | |||||
| return data; | |||||
| } | |||||
| Task.clone(Task task) { | |||||
| this.id = task.id; | |||||
| this.title = task.title; | |||||
| this.description = task.description; | |||||
| this.dueDate = task.dueDate; | |||||
| this.executeDate = task.executeDate; | |||||
| this.assigned = task.assigned; | |||||
| this.isCompleted = task.isCompleted; | |||||
| } | |||||
| } | |||||
| class Assigned { | |||||
| int? id; | |||||
| bool? activated; | |||||
| Assigned({this.id, this.activated}); | |||||
| Assigned.fromJson(Map<String, dynamic> json) { | |||||
| id = json['id']; | |||||
| activated = json['activated']; | |||||
| } | |||||
| Map<String, dynamic> toJson() { | |||||
| final Map<String, dynamic> data = new Map<String, dynamic>(); | |||||
| data['id'] = this.id; | |||||
| data['activated'] = this.activated; | |||||
| return data; | |||||
| } | |||||
| } |
| class TaskFilterRequest { | |||||
| List<String>? status; | |||||
| String? sort; | |||||
| TaskFilterRequest({this.status, this.sort}); | |||||
| TaskFilterRequest.fromJson(Map<String, dynamic> json) { | |||||
| status = json['status'].cast<String>(); | |||||
| sort = json['sort']; | |||||
| } | |||||
| Map<String, dynamic> toJson() { | |||||
| final Map<String, dynamic> data = new Map<String, dynamic>(); | |||||
| data['status'] = this.status; | |||||
| data['sort'] = this.sort; | |||||
| return data; | |||||
| } | |||||
| } |
| class RequestTask { | |||||
| String? title; | |||||
| String? detail; | |||||
| String? deadline; | |||||
| int? userAssignedId; | |||||
| int? cropId; | |||||
| RequestTask({ | |||||
| this.title, | |||||
| this.detail, | |||||
| this.deadline, | |||||
| this.userAssignedId, | |||||
| this.cropId, | |||||
| }); | |||||
| RequestTask.fromJson(Map<String, dynamic> json) { | |||||
| title = json['title']; | |||||
| detail = json['detail']; | |||||
| deadline = json['deadline']; | |||||
| userAssignedId = json['user_assigned_id']; | |||||
| cropId = json['crop_id']; | |||||
| } | |||||
| Map<String, dynamic> toJson() { | |||||
| final Map<String, dynamic> data = new Map<String, dynamic>(); | |||||
| data['title'] = this.title; | |||||
| data['detail'] = this.detail; | |||||
| data['deadline'] = this.deadline; | |||||
| data['user_assigned_id'] = this.userAssignedId; | |||||
| data['crop_id'] = this.cropId; | |||||
| return data; | |||||
| } | |||||
| } |
| class RequestTaskUpdate { | |||||
| int? id; | |||||
| bool? completed; | |||||
| RequestTaskUpdate({this.id, this.completed}); | |||||
| RequestTaskUpdate.fromJson(Map<String, dynamic> json) { | |||||
| id = json['id']; | |||||
| completed = json['completed']; | |||||
| } | |||||
| Map<String, dynamic> toJson() { | |||||
| final Map<String, dynamic> data = new Map<String, dynamic>(); | |||||
| data['id'] = this.id; | |||||
| data['completed'] = this.completed; | |||||
| return data; | |||||
| } | |||||
| } |
| import 'package:farm_tpf/data/repository/repository.dart'; | |||||
| import 'package:farm_tpf/presentation/custom_widgets/button/button_2_icon.dart'; | |||||
| import 'package:farm_tpf/presentation/custom_widgets/button/second_button.dart'; | |||||
| import 'package:farm_tpf/presentation/screens/codes/code_detail_page.dart'; | |||||
| import 'package:farm_tpf/presentation/screens/codes/create_stamp_page.dart'; | |||||
| import 'package:farm_tpf/presentation/screens/codes/models/stamp.dart'; | |||||
| import 'package:farm_tpf/presentation/screens/codes/widgets/item_code.dart'; | |||||
| import 'package:farm_tpf/presentation/screens/task/models/task_update_request.dart'; | |||||
| import 'package:farm_tpf/themes/app_colors.dart'; | |||||
| import 'package:farm_tpf/utils/const_common.dart'; | |||||
| import 'package:flutter/cupertino.dart'; | |||||
| import 'package:flutter/foundation.dart'; | |||||
| import 'package:flutter/material.dart'; | |||||
| import 'package:flutter_bloc/flutter_bloc.dart'; | |||||
| import 'package:get/get.dart'; | |||||
| import '../../../models/item_dropdown.dart'; | |||||
| import '../../../themes/styles_text.dart'; | |||||
| import '../../../utils/const_string.dart'; | |||||
| import '../../../utils/helpers.dart'; | |||||
| import '../../custom_widgets/bottom_loader.dart'; | |||||
| import '../../custom_widgets/dropdown/multiple_select_bottom_sheet.dart'; | |||||
| import '../../custom_widgets/loading_list_page.dart'; | |||||
| import '../plot/widget_search.dart'; | |||||
| import 'bloc/task_bloc.dart'; | |||||
| import 'create_task_page.dart'; | |||||
| import 'widgets/task_item.dart'; | |||||
| class TaskPage extends StatefulWidget { | |||||
| final int cropId; | |||||
| const TaskPage({super.key, required this.cropId}); | |||||
| @override | |||||
| State<TaskPage> createState() => _TaskPageState(); | |||||
| } | |||||
| class _TaskPageState extends State<TaskPage> { | |||||
| final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>(); | |||||
| TaskBloc bloc = TaskBloc(Repository()); | |||||
| final _scrollController = ScrollController(); | |||||
| final _scrollThreshold = 250.0; | |||||
| @override | |||||
| void initState() { | |||||
| bloc.add(DataFetched()); | |||||
| _scrollController.addListener(() { | |||||
| final maxScroll = _scrollController.position.maxScrollExtent; | |||||
| final currentScroll = _scrollController.position.pixels; | |||||
| if (maxScroll - currentScroll < _scrollThreshold) { | |||||
| bloc.add(DataFetched()); | |||||
| } | |||||
| }); | |||||
| super.initState(); | |||||
| } | |||||
| @override | |||||
| void dispose() { | |||||
| _scrollController.dispose(); | |||||
| super.dispose(); | |||||
| } | |||||
| @override | |||||
| Widget build(BuildContext context) { | |||||
| return Scaffold( | |||||
| backgroundColor: Colors.white, | |||||
| key: _scaffoldKey, | |||||
| body: SafeArea( | |||||
| child: Column( | |||||
| crossAxisAlignment: CrossAxisAlignment.start, | |||||
| children: <Widget>[ | |||||
| Row( | |||||
| children: [ | |||||
| ValueListenableBuilder<String>( | |||||
| valueListenable: bloc.sort, | |||||
| builder: (context, sort, _) { | |||||
| return Button2Icon( | |||||
| leftIcon: (sort == describeEnum(SortType.asc)) ? CupertinoIcons.arrow_up : CupertinoIcons.arrow_down, | |||||
| title: 'Ngày tạo', | |||||
| onPressed: () { | |||||
| if (sort == describeEnum(SortType.asc)) { | |||||
| bloc.sort.value = describeEnum(SortType.desc); | |||||
| } else { | |||||
| bloc.sort.value = describeEnum(SortType.asc); | |||||
| } | |||||
| bloc.sort.notifyListeners(); | |||||
| bloc.add(OnRefresh()); | |||||
| }, | |||||
| ); | |||||
| }, | |||||
| ), | |||||
| ValueListenableBuilder<List<ItemDropDown>>( | |||||
| valueListenable: bloc.selectedStatus, | |||||
| builder: (context, selecteds, _) { | |||||
| return ValueListenableBuilder<List<ItemDropDown>>( | |||||
| valueListenable: bloc.status, | |||||
| builder: (context, status, _) { | |||||
| return MultipleSelectBottomSheet( | |||||
| dataSources: status, | |||||
| initValue: selecteds, | |||||
| onSelected: (val) { | |||||
| bloc.selectedStatus.value = val; | |||||
| Helpers.hideKeyboard(context); | |||||
| bloc.add(OnRefresh()); | |||||
| }, | |||||
| hint: 'Trạng thái', | |||||
| ); | |||||
| }, | |||||
| ); | |||||
| }, | |||||
| ), | |||||
| const Spacer(), | |||||
| SecondButton( | |||||
| onPressed: () { | |||||
| Get.to(() => CreateTaskPage( | |||||
| cropId: widget.cropId, | |||||
| ))?.then((value) { | |||||
| if (value != null) { | |||||
| bloc.add(OnRefresh()); | |||||
| } | |||||
| }); | |||||
| }, | |||||
| title: 'Thêm', | |||||
| leftIcon: CupertinoIcons.add, | |||||
| color: AppColors.primary1, | |||||
| textColor: Colors.white, | |||||
| borderColor: AppColors.primary1, | |||||
| ), | |||||
| ], | |||||
| ), | |||||
| Expanded( | |||||
| child: mainBody(), | |||||
| ), | |||||
| ], | |||||
| ), | |||||
| ), | |||||
| ); | |||||
| } | |||||
| Widget mainBody() { | |||||
| return BlocBuilder<TaskBloc, TaskState>( | |||||
| bloc: bloc, | |||||
| builder: (context, state) { | |||||
| if (state is TaskFailure) { | |||||
| return Center(child: Text(state.errorString)); | |||||
| } | |||||
| if (state is TaskSuccess) { | |||||
| if ((state.items ?? []).isEmpty) { | |||||
| return Center(child: Text(label_list_empty)); | |||||
| } | |||||
| return RefreshIndicator( | |||||
| child: ListView.builder( | |||||
| physics: AlwaysScrollableScrollPhysics(), | |||||
| itemBuilder: (BuildContext context, int index) { | |||||
| return index >= (state.items ?? []).length | |||||
| ? BottomLoader() | |||||
| : ItemTask( | |||||
| item: state.items?[index], | |||||
| onPressed: () { | |||||
| // Get.to( | |||||
| // () => TaskDetailPage( | |||||
| // stampId: state.items?[index].id, | |||||
| // stampTask: state.items?[index].code, | |||||
| // ), | |||||
| // ); | |||||
| }, | |||||
| onChangedStatus: (status) { | |||||
| bloc.updateStatusTask( | |||||
| RequestTaskUpdate( | |||||
| id: state.items?[index].id, | |||||
| completed: status, | |||||
| ), | |||||
| onSuccess: () { | |||||
| bloc.add(OnRefresh()); | |||||
| }, | |||||
| ); | |||||
| }, | |||||
| ); | |||||
| }, | |||||
| itemCount: (state.hasReachedMax ?? false) ? (state.items ?? []).length : (state.items ?? []).length + 1, | |||||
| controller: _scrollController, | |||||
| ), | |||||
| onRefresh: () async { | |||||
| bloc.add(OnRefresh()); | |||||
| }); | |||||
| } | |||||
| return Center( | |||||
| child: LoadingListPage(), | |||||
| ); | |||||
| }, | |||||
| ); | |||||
| } | |||||
| } |
| // ignore_for_file: public_member_api_docs, sort_constructors_first | |||||
| import 'package:farm_tpf/presentation/custom_widgets/checkbox/checkbox_widget.dart'; | |||||
| import 'package:farm_tpf/utils/helpers.dart'; | |||||
| import 'package:flutter/material.dart'; | |||||
| import 'package:farm_tpf/presentation/screens/codes/models/stamp.dart'; | |||||
| import '../../../../themes/app_colors.dart'; | |||||
| import '../../../../themes/styles_text.dart'; | |||||
| import 'package:farm_tpf/utils/formatter.dart'; | |||||
| import '../models/task.dart'; | |||||
| class ItemTask extends StatelessWidget { | |||||
| final Task item; | |||||
| final Function onPressed; | |||||
| final Function(bool) onChangedStatus; | |||||
| ItemTask({ | |||||
| Key? key, | |||||
| required this.item, | |||||
| required this.onPressed, | |||||
| required this.onChangedStatus, | |||||
| }) : super(key: key); | |||||
| @override | |||||
| Widget build(BuildContext context) { | |||||
| var dueDate = item.dueDate?.format_DDMMYY().toString() ?? ''; | |||||
| var completedDate = item.executeDate?.format_DDMMYY().toString() ?? ''; | |||||
| var backgroundColor = Colors.white; | |||||
| if (item.isCompleted ?? false) { | |||||
| backgroundColor = AppColors.primary1.withOpacity(0.3); | |||||
| } else { | |||||
| var dueDateCompare = item.dueDate?.convertStringServerDateTimeToLocalDateTime() ?? DateTime.now(); | |||||
| if (Helpers.isAfterToday(dueDateCompare)) { | |||||
| backgroundColor = Colors.white; | |||||
| } else { | |||||
| backgroundColor = AppColors.semantic6; | |||||
| } | |||||
| } | |||||
| return GestureDetector( | |||||
| onTap: () { | |||||
| onPressed(); | |||||
| }, | |||||
| child: Container( | |||||
| padding: EdgeInsets.all(8), | |||||
| margin: EdgeInsets.symmetric(vertical: 4, horizontal: 8), | |||||
| decoration: BoxDecoration( | |||||
| borderRadius: BorderRadius.circular(10), | |||||
| border: Border.all( | |||||
| color: Colors.grey.shade200, | |||||
| width: 1, | |||||
| ), | |||||
| color: backgroundColor, | |||||
| ), | |||||
| child: Row( | |||||
| children: [ | |||||
| Expanded( | |||||
| child: Column( | |||||
| crossAxisAlignment: CrossAxisAlignment.start, | |||||
| children: [ | |||||
| Text( | |||||
| '${item.title ?? ''}', | |||||
| style: StylesText.body4, | |||||
| ), | |||||
| const SizedBox( | |||||
| height: 4, | |||||
| ), | |||||
| Text( | |||||
| 'Hạn chót: $dueDate', | |||||
| style: StylesText.body6, | |||||
| ), | |||||
| const SizedBox( | |||||
| height: 4, | |||||
| ), | |||||
| Text( | |||||
| 'Hoàn thành: $completedDate', | |||||
| style: StylesText.body6, | |||||
| ), | |||||
| ], | |||||
| ), | |||||
| ), | |||||
| CheckboxWidget( | |||||
| title: '', | |||||
| isChecked: item.isCompleted ?? false, | |||||
| onChange: (val) { | |||||
| onChangedStatus(val); | |||||
| }, | |||||
| ) | |||||
| ], | |||||
| ), | |||||
| ), | |||||
| ); | |||||
| } | |||||
| } |
| asc, | asc, | ||||
| desc, | desc, | ||||
| } | } | ||||
| enum TaskStatus { | |||||
| completed, | |||||
| no_completed, | |||||
| } |
| } | } | ||||
| return ''; | return ''; | ||||
| } | } | ||||
| static String getTaskStatus(String status) { | |||||
| if (status.toLowerCase() == describeEnum(TaskStatus.completed)) { | |||||
| return 'Hoàn thành'; | |||||
| } else if (status.toLowerCase() == describeEnum(TaskStatus.no_completed)) { | |||||
| return 'Chưa hoàn thành'; | |||||
| } | |||||
| return ''; | |||||
| } | |||||
| static bool isAfterToday(DateTime compareDate) { | |||||
| var now = DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day); | |||||
| var date = DateTime(compareDate.year, compareDate.month, compareDate.day); | |||||
| return date.isAfter(now); | |||||
| } | |||||
| } | } |
| description: A new Flutter project. | description: A new Flutter project. | ||||
| publish_to: 'none' | publish_to: 'none' | ||||
| version: 1.1.6+21 | |||||
| version: 1.1.7+23 | |||||
| environment: | environment: | ||||
| sdk: ">=2.17.0 <=3.0.0" | sdk: ">=2.17.0 <=3.0.0" |