| import 'package:equatable/equatable.dart'; | |||||
| class ItemDropDown extends Equatable { | |||||
| String? key; | |||||
| String? value; | |||||
| dynamic data; | |||||
| ItemDropDown({this.key, this.value, this.data}); | |||||
| @override | |||||
| List<Object?> get props => [key, value, data]; | |||||
| } |
| import 'package:flutter/material.dart'; | |||||
| import '../../../themes/styles_text.dart'; | |||||
| class Button2Icon extends StatelessWidget { | |||||
| final IconData leftIcon; | |||||
| final IconData? rightIcon; | |||||
| final Widget? rightWidget; | |||||
| final String title; | |||||
| final Function onPressed; | |||||
| const Button2Icon({ | |||||
| super.key, | |||||
| required this.leftIcon, | |||||
| this.rightIcon, | |||||
| required this.title, | |||||
| required this.onPressed, | |||||
| this.rightWidget, | |||||
| }); | |||||
| @override | |||||
| Widget build(BuildContext context) { | |||||
| return GestureDetector( | |||||
| onTap: () { | |||||
| onPressed(); | |||||
| }, | |||||
| child: Container( | |||||
| padding: EdgeInsets.all(8), | |||||
| margin: EdgeInsets.symmetric(horizontal: 8), | |||||
| decoration: BoxDecoration( | |||||
| borderRadius: BorderRadius.circular(10), | |||||
| border: Border.all( | |||||
| color: Colors.grey.shade200, | |||||
| width: 1, | |||||
| ), | |||||
| ), | |||||
| child: Row( | |||||
| mainAxisSize: MainAxisSize.min, | |||||
| children: [ | |||||
| Icon( | |||||
| leftIcon, | |||||
| size: 16, | |||||
| color: Colors.grey.shade500, | |||||
| ), | |||||
| const SizedBox( | |||||
| width: 4, | |||||
| ), | |||||
| Text( | |||||
| title, | |||||
| style: StylesText.body5, | |||||
| ), | |||||
| const SizedBox( | |||||
| width: 4, | |||||
| ), | |||||
| rightIcon != null | |||||
| ? Icon( | |||||
| rightIcon, | |||||
| size: 16, | |||||
| color: Colors.grey.shade500, | |||||
| ) | |||||
| : const SizedBox.shrink(), | |||||
| rightWidget ?? const SizedBox.shrink(), | |||||
| ], | |||||
| ), | |||||
| ), | |||||
| ); | |||||
| } | |||||
| } |
| import 'package:flutter/material.dart'; | |||||
| import '../../../themes/styles_text.dart'; | |||||
| class SecondButton extends StatelessWidget { | |||||
| final IconData? leftIcon; | |||||
| final Function onPressed; | |||||
| final String title; | |||||
| final Color? color; | |||||
| final Color? textColor; | |||||
| final Color? borderColor; | |||||
| final double? width; | |||||
| final double? height; | |||||
| const SecondButton({ | |||||
| super.key, | |||||
| required this.onPressed, | |||||
| this.leftIcon, | |||||
| required this.title, | |||||
| this.color, | |||||
| this.textColor, | |||||
| this.borderColor, | |||||
| this.width, | |||||
| this.height, | |||||
| }); | |||||
| @override | |||||
| Widget build(BuildContext context) { | |||||
| return GestureDetector( | |||||
| onTap: () { | |||||
| onPressed(); | |||||
| }, | |||||
| child: Container( | |||||
| width: width, | |||||
| height: height, | |||||
| padding: EdgeInsets.all(7), | |||||
| margin: EdgeInsets.symmetric(horizontal: 8), | |||||
| decoration: BoxDecoration( | |||||
| borderRadius: BorderRadius.circular(10), | |||||
| border: Border.all( | |||||
| color: borderColor ?? Colors.white, | |||||
| width: 2, | |||||
| ), | |||||
| color: color ?? Colors.white, | |||||
| ), | |||||
| child: Row( | |||||
| mainAxisSize: MainAxisSize.min, | |||||
| mainAxisAlignment: MainAxisAlignment.center, | |||||
| children: [ | |||||
| leftIcon != null | |||||
| ? Icon( | |||||
| leftIcon, | |||||
| size: 16, | |||||
| color: textColor ?? Colors.grey.shade500, | |||||
| ) | |||||
| : const SizedBox.shrink(), | |||||
| const SizedBox( | |||||
| width: 4, | |||||
| ), | |||||
| Text( | |||||
| title, | |||||
| style: StylesText.body5.copyWith(color: textColor ?? Colors.white), | |||||
| ), | |||||
| ], | |||||
| ), | |||||
| ), | |||||
| ); | |||||
| } | |||||
| } |
| import 'package:flutter/material.dart'; | |||||
| import 'package:flutter_datetime_picker/flutter_datetime_picker.dart'; | |||||
| import 'package:flutter_screenutil/flutter_screenutil.dart'; | |||||
| import 'package:flutter_svg/svg.dart'; | |||||
| import 'package:intl/intl.dart'; | |||||
| import '../../../themes/app_colors.dart'; | |||||
| import '../../../themes/styles_text.dart'; | |||||
| import '../../../utils/app_images.dart'; | |||||
| class DatePickerWidget extends StatefulWidget { | |||||
| final bool isEnable; | |||||
| final DateTime initDateTime; | |||||
| final DateTime? minDate; | |||||
| final bool isMinDate; | |||||
| SvgPicture? icon; | |||||
| final bool isAcceptSelectPassDate; | |||||
| final bool isShowTime; | |||||
| final Function(DateTime? selectedDateTimeLocal) onUpdateDateTime; | |||||
| DatePickerWidget({ | |||||
| required this.initDateTime, | |||||
| required this.onUpdateDateTime, | |||||
| this.minDate, | |||||
| this.isMinDate = false, | |||||
| this.isEnable = true, | |||||
| this.icon, | |||||
| this.isAcceptSelectPassDate = true, | |||||
| this.isShowTime = false, | |||||
| }); | |||||
| @override | |||||
| _DatePickerWidgetState createState() => _DatePickerWidgetState(); | |||||
| } | |||||
| class _DatePickerWidgetState extends State<DatePickerWidget> { | |||||
| late DateTime selectedDate; | |||||
| @override | |||||
| void initState() { | |||||
| super.initState(); | |||||
| } | |||||
| @override | |||||
| Widget build(BuildContext context) { | |||||
| selectedDate = widget.initDateTime; | |||||
| print('--- min date ----'); | |||||
| print(widget.isMinDate ? DateTime.parse(widget.minDate.toString()) : DateTime(DateTime.now().year - 5)); | |||||
| return InkWell( | |||||
| onTap: widget.isEnable | |||||
| ? () { | |||||
| widget.isShowTime | |||||
| ? DatePicker.showDateTimePicker( | |||||
| context, | |||||
| showTitleActions: true, | |||||
| minTime: DateTime(DateTime.now().year - 5), | |||||
| maxTime: DateTime(DateTime.now().year + 5), | |||||
| onChanged: (date) {}, | |||||
| onConfirm: (date) { | |||||
| if (date != null) { | |||||
| if (widget.isAcceptSelectPassDate) { | |||||
| } else { | |||||
| var now = DateTime.now(); | |||||
| if (date.isBefore( | |||||
| DateTime(now.year, now.month, now.day, 00, 00, 00), | |||||
| )) { | |||||
| return; | |||||
| } | |||||
| } | |||||
| widget.onUpdateDateTime(date); | |||||
| setState(() { | |||||
| selectedDate = date; | |||||
| }); | |||||
| } | |||||
| }, | |||||
| currentTime: selectedDate, | |||||
| locale: LocaleType.vi, | |||||
| ) | |||||
| : showDatePicker( | |||||
| context: context, | |||||
| initialDate: selectedDate, | |||||
| firstDate: DateTime(DateTime.now().year - 5), | |||||
| lastDate: DateTime(DateTime.now().year + 5), | |||||
| ).then((date) { | |||||
| if (date != null) { | |||||
| if (widget.isAcceptSelectPassDate) { | |||||
| } else { | |||||
| var now = DateTime.now(); | |||||
| if (date.isBefore( | |||||
| DateTime(now.year, now.month, now.day, 00, 00, 00), | |||||
| )) { | |||||
| return; | |||||
| } | |||||
| } | |||||
| widget.onUpdateDateTime(date); | |||||
| setState(() { | |||||
| selectedDate = date; | |||||
| }); | |||||
| } | |||||
| }); | |||||
| } | |||||
| : null, | |||||
| child: Container( | |||||
| height: 48.h, | |||||
| decoration: BoxDecoration( | |||||
| borderRadius: BorderRadius.circular(12), | |||||
| border: Border.all( | |||||
| color: AppColors.neutral4, | |||||
| width: 1, | |||||
| ), | |||||
| color: widget.isEnable ? Colors.white : Colors.transparent, | |||||
| ), | |||||
| padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), | |||||
| child: Row( | |||||
| children: [ | |||||
| Expanded( | |||||
| child: Text( | |||||
| widget.isShowTime ? DateFormat('dd/MM/yyyy HH:mm').format(selectedDate) : DateFormat('dd/MM/yyyy').format(selectedDate), | |||||
| style: StylesText.body6, | |||||
| textAlign: TextAlign.start, | |||||
| ), | |||||
| ), | |||||
| SizedBox( | |||||
| child: widget.icon ?? | |||||
| SvgPicture.asset( | |||||
| AssetSVG.icCalendar, | |||||
| ), | |||||
| ) | |||||
| ], | |||||
| ), | |||||
| ), | |||||
| ); | |||||
| } | |||||
| } |
| import 'package:flutter/material.dart'; | |||||
| import 'package:flutter_svg/flutter_svg.dart'; | |||||
| import 'package:get/get.dart'; | |||||
| import '../../../themes/app_dimension.dart'; | |||||
| import '../../../models/item_dropdown.dart'; | |||||
| import '../../../themes/app_colors.dart'; | |||||
| import '../../../themes/styles_text.dart'; | |||||
| import '../../../utils/app_images.dart'; | |||||
| import '../../../utils/validators.dart'; | |||||
| class DropdownBottomSheet extends StatefulWidget { | |||||
| final String? initValue; | |||||
| final List<ItemDropDown> dataSources; | |||||
| final Function(ItemDropDown) onSelected; | |||||
| final Color? borderColor; | |||||
| final String hint; | |||||
| final double? height; | |||||
| final String? errorText; | |||||
| final bool isDisable; | |||||
| final EdgeInsetsGeometry? contentPadding; | |||||
| final Color? disabledColor; | |||||
| const DropdownBottomSheet({ | |||||
| Key? key, | |||||
| required this.dataSources, | |||||
| required this.onSelected, | |||||
| required this.hint, | |||||
| this.initValue, | |||||
| this.borderColor, | |||||
| this.height, | |||||
| this.errorText, | |||||
| this.isDisable = false, | |||||
| this.contentPadding, | |||||
| this.disabledColor, | |||||
| }) : super(key: key); | |||||
| @override | |||||
| _DropdownBottomSheetState createState() => _DropdownBottomSheetState(); | |||||
| } | |||||
| class _DropdownBottomSheetState extends State<DropdownBottomSheet> { | |||||
| var dataSources = ValueNotifier(<ItemDropDown>[]); | |||||
| var selectValue = ValueNotifier(ItemDropDown()); | |||||
| int _selectedIndex = 0; | |||||
| FixedExtentScrollController? scrollController; | |||||
| final _searchCtl = TextEditingController(); | |||||
| @override | |||||
| void initState() { | |||||
| super.initState(); | |||||
| } | |||||
| @override | |||||
| void dispose() { | |||||
| _searchCtl.dispose(); | |||||
| super.dispose(); | |||||
| } | |||||
| @override | |||||
| Widget build(BuildContext context) { | |||||
| _selectedIndex = widget.dataSources.indexWhere((element) { | |||||
| return element.key == widget.initValue; | |||||
| }); | |||||
| if (_selectedIndex >= 0) { | |||||
| selectValue.value = widget.dataSources[_selectedIndex]; | |||||
| } else { | |||||
| selectValue.value = ItemDropDown(key: '', value: ''); | |||||
| } | |||||
| return InkWell( | |||||
| onTap: () { | |||||
| FocusScope.of(context).requestFocus(FocusNode()); | |||||
| widget.isDisable ? null : _modalBottomSheetMenu(); | |||||
| }, | |||||
| child: Column( | |||||
| crossAxisAlignment: CrossAxisAlignment.start, | |||||
| children: [ | |||||
| Container( | |||||
| padding: widget.contentPadding ?? | |||||
| EdgeInsets.symmetric( | |||||
| horizontal: 12.w, | |||||
| vertical: 16.h, | |||||
| ), | |||||
| decoration: BoxDecoration( | |||||
| color: widget.isDisable ? (widget.disabledColor ?? AppColors.background1) : AppColors.white, | |||||
| borderRadius: BorderRadius.circular(12), | |||||
| border: Border.all( | |||||
| color: AppColors.neutral4, | |||||
| width: 1, | |||||
| ), | |||||
| ), | |||||
| child: Row( | |||||
| children: [ | |||||
| Expanded( | |||||
| child: ValueListenableBuilder<ItemDropDown>( | |||||
| valueListenable: selectValue, | |||||
| builder: (context, selected, _) { | |||||
| if (Validators.stringNotNullOrEmpty(selected.key)) { | |||||
| return Text( | |||||
| selected.value ?? '', | |||||
| style: StylesText.body6, | |||||
| maxLines: 1, | |||||
| ); | |||||
| } else { | |||||
| return Text( | |||||
| widget.hint, | |||||
| style: StylesText.body6.copyWith( | |||||
| color: AppColors.neutral3, | |||||
| ), | |||||
| ); | |||||
| } | |||||
| }, | |||||
| ), | |||||
| ), | |||||
| SvgPicture.asset(AssetSVG.icArrowDown), | |||||
| ], | |||||
| ), | |||||
| ), | |||||
| if (widget.errorText != null) ...[ | |||||
| SizedBox( | |||||
| height: 4.h, | |||||
| ), | |||||
| Text( | |||||
| widget.errorText ?? '', | |||||
| style: StylesText.caption1.copyWith( | |||||
| color: AppColors.semantic2, | |||||
| ), | |||||
| ) | |||||
| ], | |||||
| ], | |||||
| ), | |||||
| ); | |||||
| } | |||||
| void _modalBottomSheetMenu() { | |||||
| _searchCtl.clear(); | |||||
| dataSources.value = List.of(widget.dataSources); | |||||
| showDialog( | |||||
| context: context, | |||||
| builder: (context) { | |||||
| return AlertDialog( | |||||
| contentPadding: const EdgeInsets.all(8), | |||||
| content: Container( | |||||
| width: double.maxFinite, | |||||
| child: Column( | |||||
| mainAxisSize: MainAxisSize.min, | |||||
| children: <Widget>[ | |||||
| Expanded( | |||||
| child: ValueListenableBuilder<List<ItemDropDown>>( | |||||
| valueListenable: dataSources, | |||||
| builder: (context, sources, _) { | |||||
| return ListView.builder( | |||||
| shrinkWrap: true, | |||||
| itemBuilder: ((context, index) { | |||||
| var item = sources[index]; | |||||
| return InkWell( | |||||
| onTap: () {}, | |||||
| child: RadioListTile<ItemDropDown>( | |||||
| value: item, | |||||
| title: Text( | |||||
| item.value ?? '', | |||||
| style: StylesText.body6, | |||||
| ), | |||||
| groupValue: item.key == selectValue.value.key ? item : null, | |||||
| onChanged: (val) { | |||||
| Get.back(); | |||||
| widget.onSelected(val!); | |||||
| }, | |||||
| ), | |||||
| ); | |||||
| }), | |||||
| itemCount: sources.length, | |||||
| ); | |||||
| }, | |||||
| ), | |||||
| ) | |||||
| ], | |||||
| ), | |||||
| ), | |||||
| ); | |||||
| }, | |||||
| ); | |||||
| } | |||||
| } |
| import 'package:farm_tpf/presentation/custom_widgets/app_bar_widget.dart'; | |||||
| import 'package:farm_tpf/presentation/custom_widgets/button/second_button.dart'; | |||||
| import 'package:farm_tpf/presentation/screens/codes/code_update_timeline_page.dart'; | |||||
| import 'package:farm_tpf/presentation/screens/codes/widgets/item_code_timeline.dart'; | |||||
| import 'package:flutter/material.dart'; | |||||
| import 'package:get/get.dart'; | |||||
| import '../../../themes/styles_text.dart'; | |||||
| class CodeDetailPage extends StatefulWidget { | |||||
| const CodeDetailPage({super.key}); | |||||
| @override | |||||
| State<CodeDetailPage> createState() => _CodeDetailPageState(); | |||||
| } | |||||
| class _CodeDetailPageState extends State<CodeDetailPage> { | |||||
| @override | |||||
| Widget build(BuildContext context) { | |||||
| return Scaffold( | |||||
| appBar: AppBarWidget( | |||||
| action: IconButton( | |||||
| onPressed: () { | |||||
| Get.to(() => CodeUpdateTimelinePage()); | |||||
| }, | |||||
| icon: Icon( | |||||
| Icons.edit, | |||||
| color: Colors.blue, | |||||
| ), | |||||
| ), | |||||
| ), | |||||
| body: Padding( | |||||
| padding: const EdgeInsets.all(8.0), | |||||
| child: Column( | |||||
| children: [ | |||||
| Expanded( | |||||
| child: SingleChildScrollView( | |||||
| child: Column( | |||||
| children: [ | |||||
| _itemCodeDetail( | |||||
| title: 'Tên sản phẩm', | |||||
| detail: 'Cà rốt', | |||||
| titleStyle: StylesText.body4, | |||||
| detailStyle: StylesText.body4.copyWith( | |||||
| color: Colors.blue, | |||||
| ), | |||||
| ), | |||||
| _itemCodeDetail(title: 'Mô tả', detail: 'detail'), | |||||
| _itemCodeDetail(title: 'Số lượng tem', detail: 'detail'), | |||||
| _itemCodeDetail(title: 'Trạng thái', detail: 'detail'), | |||||
| _itemCodeDetail(title: 'Hạn sử dụng', detail: 'detail'), | |||||
| _itemCodeDetail(title: 'Mẫu tem', detail: 'detail'), | |||||
| Text( | |||||
| 'Timeline hoạt động', | |||||
| style: StylesText.body1, | |||||
| ), | |||||
| _timelineWidget(), | |||||
| ], | |||||
| ), | |||||
| ), | |||||
| ), | |||||
| const SizedBox( | |||||
| height: 8, | |||||
| ), | |||||
| // Container( | |||||
| // width: 100, | |||||
| // height: 100, | |||||
| // color: Colors.red, | |||||
| // ), | |||||
| _actionButtonWidget(), | |||||
| ], | |||||
| ), | |||||
| ), | |||||
| ); | |||||
| } | |||||
| Widget _timelineWidget() { | |||||
| return ListView.builder( | |||||
| itemBuilder: (context, index) { | |||||
| return ItemCodeTimeline( | |||||
| onPressed: () {}, | |||||
| ); | |||||
| }, | |||||
| itemCount: 20, | |||||
| physics: NeverScrollableScrollPhysics(), | |||||
| shrinkWrap: true, | |||||
| ); | |||||
| } | |||||
| Widget _actionButtonWidget() { | |||||
| return Wrap( | |||||
| spacing: 8, | |||||
| children: [ | |||||
| Row( | |||||
| children: [ | |||||
| Expanded( | |||||
| child: SecondButton( | |||||
| onPressed: () {}, | |||||
| title: 'Kích hoạt toàn bộ', | |||||
| borderColor: Colors.blue, | |||||
| textColor: Colors.blue, | |||||
| width: double.infinity, | |||||
| height: 40, | |||||
| ), | |||||
| ), | |||||
| Expanded( | |||||
| child: SecondButton( | |||||
| onPressed: () {}, | |||||
| title: 'Huỷ toàn bộ', | |||||
| borderColor: Colors.red, | |||||
| textColor: Colors.white, | |||||
| color: Colors.red, | |||||
| width: double.infinity, | |||||
| height: 40, | |||||
| ), | |||||
| ), | |||||
| ], | |||||
| ), | |||||
| const SizedBox( | |||||
| height: 8, | |||||
| ), | |||||
| Row( | |||||
| children: [ | |||||
| Expanded( | |||||
| child: SecondButton( | |||||
| onPressed: () {}, | |||||
| title: 'Cập nhật hoạt động', | |||||
| borderColor: Colors.green, | |||||
| textColor: Colors.green, | |||||
| width: double.infinity, | |||||
| height: 40, | |||||
| ), | |||||
| ), | |||||
| Expanded( | |||||
| child: SecondButton( | |||||
| onPressed: () {}, | |||||
| title: 'In / Xuất file', | |||||
| borderColor: Colors.cyan, | |||||
| textColor: Colors.white, | |||||
| color: Colors.cyan, | |||||
| width: double.infinity, | |||||
| height: 40, | |||||
| ), | |||||
| ), | |||||
| ], | |||||
| ), | |||||
| ], | |||||
| ); | |||||
| } | |||||
| Widget _itemCodeDetail({ | |||||
| required String title, | |||||
| required String detail, | |||||
| TextStyle? titleStyle, | |||||
| TextStyle? detailStyle, | |||||
| }) { | |||||
| return Padding( | |||||
| padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), | |||||
| child: Row( | |||||
| children: [ | |||||
| Expanded( | |||||
| child: Text( | |||||
| title, | |||||
| style: titleStyle ?? StylesText.body6, | |||||
| ), | |||||
| ), | |||||
| Text( | |||||
| detail, | |||||
| style: detailStyle ?? StylesText.body6, | |||||
| ) | |||||
| ], | |||||
| ), | |||||
| ); | |||||
| } | |||||
| } |
| 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/widgets/item_code.dart'; | |||||
| import 'package:farm_tpf/themes/app_colors.dart'; | |||||
| import 'package:flutter/cupertino.dart'; | |||||
| import 'package:flutter/material.dart'; | |||||
| import 'package:get/get.dart'; | |||||
| import '../../../themes/styles_text.dart'; | |||||
| import '../plot/widget_search.dart'; | |||||
| class CodePage extends StatefulWidget { | |||||
| const CodePage({super.key}); | |||||
| @override | |||||
| State<CodePage> createState() => _CodePageState(); | |||||
| } | |||||
| class _CodePageState extends State<CodePage> { | |||||
| final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>(); | |||||
| @override | |||||
| Widget build(BuildContext context) { | |||||
| return Scaffold( | |||||
| backgroundColor: Colors.white, | |||||
| key: _scaffoldKey, | |||||
| body: SafeArea( | |||||
| child: Column( | |||||
| crossAxisAlignment: CrossAxisAlignment.start, | |||||
| children: <Widget>[ | |||||
| SizedBox( | |||||
| height: 8, | |||||
| ), | |||||
| Container( | |||||
| padding: EdgeInsets.all(8), | |||||
| color: Colors.white, | |||||
| child: Text( | |||||
| 'Danh sách tem', | |||||
| style: TextStyle(fontWeight: FontWeight.w500, fontSize: 22), | |||||
| ), | |||||
| ), | |||||
| WidgetSearch(), | |||||
| Row( | |||||
| children: [ | |||||
| Button2Icon( | |||||
| leftIcon: CupertinoIcons.arrow_up_arrow_down, | |||||
| rightIcon: CupertinoIcons.chevron_down, | |||||
| title: 'Sort', | |||||
| onPressed: () {}, | |||||
| ), | |||||
| Button2Icon( | |||||
| leftIcon: CupertinoIcons.slider_horizontal_3, | |||||
| title: 'Filter', | |||||
| rightWidget: Container( | |||||
| width: 16, | |||||
| height: 16, | |||||
| decoration: BoxDecoration( | |||||
| borderRadius: BorderRadius.circular(12), | |||||
| color: Colors.blue, | |||||
| ), | |||||
| child: Center( | |||||
| child: Text( | |||||
| '1', | |||||
| style: StylesText.caption6.copyWith(color: Colors.white), | |||||
| ), | |||||
| ), | |||||
| ), | |||||
| onPressed: () {}, | |||||
| ), | |||||
| const Spacer(), | |||||
| SecondButton( | |||||
| onPressed: () {}, | |||||
| title: 'Tạo tem', | |||||
| leftIcon: CupertinoIcons.add, | |||||
| color: AppColors.primary1, | |||||
| textColor: Colors.white, | |||||
| borderColor: AppColors.primary1, | |||||
| ), | |||||
| ], | |||||
| ), | |||||
| Expanded( | |||||
| child: ListView.builder( | |||||
| itemBuilder: ((context, index) { | |||||
| return ItemCode( | |||||
| onPressed: () { | |||||
| Get.to(() => CodeDetailPage()); | |||||
| }, | |||||
| ); | |||||
| }), | |||||
| itemCount: 100, | |||||
| ), | |||||
| ), | |||||
| ], | |||||
| ), | |||||
| ), | |||||
| ); | |||||
| } | |||||
| } |
| import 'package:farm_tpf/presentation/custom_widgets/app_bar_widget.dart'; | |||||
| import 'package:farm_tpf/presentation/custom_widgets/button_widget.dart'; | |||||
| import 'package:farm_tpf/presentation/screens/codes/cubit/code_update_timeline_cubit.dart'; | |||||
| import 'package:flutter/material.dart'; | |||||
| import 'package:flutter/scheduler.dart'; | |||||
| import 'package:flutter_bloc/flutter_bloc.dart'; | |||||
| import 'package:keyboard_dismisser/keyboard_dismisser.dart'; | |||||
| import '../../../utils/utils.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 'widgets/item_column.dart'; | |||||
| class CodeUpdateTimelinePage extends StatefulWidget { | |||||
| const CodeUpdateTimelinePage({super.key}); | |||||
| @override | |||||
| State<CodeUpdateTimelinePage> createState() => _CodeUpdateTimelinePageState(); | |||||
| } | |||||
| class _CodeUpdateTimelinePageState extends State<CodeUpdateTimelinePage> { | |||||
| final bloc = CodeUpdateTimelineCubit(); | |||||
| @override | |||||
| void initState() { | |||||
| super.initState(); | |||||
| bloc.preparedData(); | |||||
| } | |||||
| @override | |||||
| void dispose() { | |||||
| bloc.dispose(); | |||||
| super.dispose(); | |||||
| } | |||||
| @override | |||||
| Widget build(BuildContext context) { | |||||
| return Scaffold( | |||||
| appBar: AppBarWidget(), | |||||
| body: BlocListener<CodeUpdateTimelineCubit, CodeUpdateTimelineState>( | |||||
| bloc: bloc, | |||||
| listener: ((context, state) { | |||||
| if (state is CodeUpdateTimelineLoading) { | |||||
| SchedulerBinding.instance.addPostFrameCallback((timeStamp) { | |||||
| UtilWidget.showLoading(); | |||||
| }); | |||||
| } else if (state is CodeUpdateTimelineFailure) { | |||||
| SchedulerBinding.instance.addPostFrameCallback((timeStamp) { | |||||
| UtilWidget.hideLoading(); | |||||
| // UtilWidget.showToastError(state.errorMessage); | |||||
| }); | |||||
| } else if (state is CodeUpdateTimelinePrepareDataSuccessful) { | |||||
| SchedulerBinding.instance.addPostFrameCallback((timeStamp) { | |||||
| 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: () {}, | |||||
| ), | |||||
| ), | |||||
| ], | |||||
| ), | |||||
| ), | |||||
| ), | |||||
| ), | |||||
| ), | |||||
| ); | |||||
| } | |||||
| Widget _widgetBody() { | |||||
| return Container( | |||||
| padding: const EdgeInsets.all(16), | |||||
| child: SingleChildScrollView( | |||||
| child: Column( | |||||
| crossAxisAlignment: CrossAxisAlignment.start, | |||||
| children: [ | |||||
| ValueListenableBuilder<DateTime>( | |||||
| valueListenable: bloc.actionDate, | |||||
| builder: (context, actionDate, _) { | |||||
| return ItemColumnWidget( | |||||
| title: 'Ngày nhập', | |||||
| child: DatePickerWidget( | |||||
| initDateTime: actionDate, | |||||
| onUpdateDateTime: (selectedDate) { | |||||
| if (selectedDate != null) { | |||||
| bloc.actionDate.value = selectedDate; | |||||
| } | |||||
| }, | |||||
| ), | |||||
| ); | |||||
| }, | |||||
| ), | |||||
| SizedBox( | |||||
| height: 8, | |||||
| ), | |||||
| ItemColumnWidget( | |||||
| title: 'Hoạt động', | |||||
| child: ValueListenableBuilder<String>( | |||||
| valueListenable: bloc.selectedActionType, | |||||
| builder: (context, selected, _) { | |||||
| return DropdownBottomSheet( | |||||
| dataSources: bloc.actionTypes, | |||||
| initValue: selected, | |||||
| onSelected: (val) { | |||||
| bloc.selectedActionType.value = val.key ?? ''; | |||||
| }, | |||||
| hint: 'Hoạt động', | |||||
| ); | |||||
| }, | |||||
| ), | |||||
| ), | |||||
| const SizedBox( | |||||
| height: 8, | |||||
| ), | |||||
| ItemColumnWidget( | |||||
| title: 'Mô tả', | |||||
| child: TextFieldNormal( | |||||
| controller: bloc.descriptionCtl, | |||||
| maxLines: 2, | |||||
| hint: 'Mô tả', | |||||
| ), | |||||
| ), | |||||
| const SizedBox( | |||||
| height: 16, | |||||
| ), | |||||
| ], | |||||
| ), | |||||
| ), | |||||
| ); | |||||
| } | |||||
| } |
| import 'package:bloc/bloc.dart'; | |||||
| import 'package:equatable/equatable.dart'; | |||||
| import 'package:flutter/material.dart'; | |||||
| import '../../../../data/api/app_exception.dart'; | |||||
| import '../../../../data/repository/repository.dart'; | |||||
| import '../../../../models/item_dropdown.dart'; | |||||
| part 'code_update_timeline_state.dart'; | |||||
| class CodeUpdateTimelineCubit extends Cubit<CodeUpdateTimelineState> { | |||||
| CodeUpdateTimelineCubit() : super(CodeUpdateTimelineInitial()); | |||||
| final repository = Repository(); | |||||
| final formKey = GlobalKey<FormState>(); | |||||
| var actionDate = ValueNotifier(DateTime.now()); | |||||
| final descriptionCtl = TextEditingController(); | |||||
| var actionTypes = [ | |||||
| ItemDropDown(key: 'hoat dong 1', value: 'hoat dong 1'), | |||||
| ItemDropDown(key: 'hoat dong 2', value: 'hoat dong 2'), | |||||
| ]; | |||||
| var selectedActionType = ValueNotifier(''); | |||||
| // var existedCodeUpdateTimeline = UpdateCodeUpdateTimeline(); | |||||
| void dispose() { | |||||
| descriptionCtl.dispose(); | |||||
| } | |||||
| Future<void> preparedData() async { | |||||
| try { | |||||
| await Future.delayed(const Duration(seconds: 0)); | |||||
| emit(CodeUpdateTimelineLoading()); | |||||
| emit(CodeUpdateTimelinePrepareDataSuccessful()); | |||||
| } catch (e) { | |||||
| emit(CodeUpdateTimelineFailure(AppException.handleError(e))); | |||||
| } | |||||
| } | |||||
| Future<void> onSubmit() async { | |||||
| if (formKey.currentState!.validate()) { | |||||
| // var gioiTinh = gioiTinhs.firstWhere( | |||||
| // (element) => selectedGioiTinh.value == element.key, | |||||
| // orElse: () => ItemDropDown(), | |||||
| // ); | |||||
| // existedCodeUpdateTimeline | |||||
| // ..gioiTinhBe = gioiTinh.value | |||||
| // ..ghiChu = noteCtl.text | |||||
| // ..ngayTiemUngKhiThan = ungKhiThanDate.value.convertLocalDateTimeToServer(); | |||||
| // UtilWidget.showLoading(); | |||||
| // await repository.updateCodeUpdateTimeline( | |||||
| // (success) { | |||||
| // UtilWidget.hideDialog(); | |||||
| // UtilWidget.showToastSuccess('Thêm thành công'); | |||||
| // Get.back(); | |||||
| // }, | |||||
| // (errorMessage) { | |||||
| // UtilWidget.hideDialog(); | |||||
| // UtilWidget.showToastError(errorMessage); | |||||
| // }, | |||||
| // item: existedCodeUpdateTimeline, | |||||
| // ); | |||||
| } | |||||
| } | |||||
| } |
| part of 'code_update_timeline_cubit.dart'; | |||||
| abstract class CodeUpdateTimelineState extends Equatable { | |||||
| const CodeUpdateTimelineState(); | |||||
| @override | |||||
| List<Object> get props => []; | |||||
| } | |||||
| class CodeUpdateTimelineInitial extends CodeUpdateTimelineState {} | |||||
| class CodeUpdateTimelineLoading extends CodeUpdateTimelineState {} | |||||
| class CodeUpdateTimelineFailure extends CodeUpdateTimelineState { | |||||
| final String errorMessage; | |||||
| CodeUpdateTimelineFailure(this.errorMessage); | |||||
| } | |||||
| class CodeUpdateTimelinePrepareDataSuccessful extends CodeUpdateTimelineState { | |||||
| CodeUpdateTimelinePrepareDataSuccessful(); | |||||
| } |
| import 'package:flutter/material.dart'; | |||||
| import '../../../../themes/app_colors.dart'; | |||||
| import '../../../../themes/styles_text.dart'; | |||||
| class ItemCode extends StatelessWidget { | |||||
| final Function onPressed; | |||||
| const ItemCode({ | |||||
| super.key, | |||||
| required this.onPressed, | |||||
| }); | |||||
| @override | |||||
| Widget build(BuildContext context) { | |||||
| 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: AppColors.primary1.withOpacity(0.3), | |||||
| ), | |||||
| child: Column( | |||||
| crossAxisAlignment: CrossAxisAlignment.start, | |||||
| children: [ | |||||
| Text( | |||||
| 'Cà chua đợt 1 5/2023', | |||||
| style: StylesText.body1, | |||||
| ), | |||||
| const SizedBox( | |||||
| height: 8, | |||||
| ), | |||||
| Text( | |||||
| '06/05/2023 - 1500 tem - mới', | |||||
| style: StylesText.body6, | |||||
| ), | |||||
| ], | |||||
| ), | |||||
| ), | |||||
| ); | |||||
| } | |||||
| } |
| import 'package:flutter/material.dart'; | |||||
| import '../../../../themes/app_colors.dart'; | |||||
| import '../../../../themes/styles_text.dart'; | |||||
| class ItemCodeTimeline extends StatelessWidget { | |||||
| final Function onPressed; | |||||
| const ItemCodeTimeline({ | |||||
| super.key, | |||||
| required this.onPressed, | |||||
| }); | |||||
| @override | |||||
| Widget build(BuildContext context) { | |||||
| return Container( | |||||
| child: Stack( | |||||
| children: [ | |||||
| Row( | |||||
| children: [ | |||||
| const SizedBox( | |||||
| width: 16, | |||||
| height: 20, | |||||
| ), | |||||
| Expanded(child: _widgetTimeLine()), | |||||
| ], | |||||
| ), | |||||
| Positioned( | |||||
| top: 0, | |||||
| left: 8, | |||||
| child: Container( | |||||
| color: Colors.white, | |||||
| width: 16, | |||||
| height: 24, | |||||
| child: Column( | |||||
| children: [ | |||||
| Container( | |||||
| width: 16, | |||||
| height: 16, | |||||
| decoration: BoxDecoration( | |||||
| borderRadius: BorderRadius.circular( | |||||
| 16, | |||||
| ), | |||||
| color: Colors.green, | |||||
| ), | |||||
| ), | |||||
| ], | |||||
| ), | |||||
| ), | |||||
| ), | |||||
| ], | |||||
| ), | |||||
| ); | |||||
| } | |||||
| Widget _widgetTimeLine() { | |||||
| return Container( | |||||
| width: double.infinity, | |||||
| padding: const EdgeInsets.only( | |||||
| left: 12, | |||||
| right: 8, | |||||
| ), | |||||
| margin: const EdgeInsets.only( | |||||
| bottom: 8, | |||||
| ), | |||||
| decoration: BoxDecoration( | |||||
| border: Border( | |||||
| left: BorderSide(color: Colors.blue), | |||||
| ), | |||||
| ), | |||||
| child: Column( | |||||
| children: [ | |||||
| Row( | |||||
| mainAxisAlignment: MainAxisAlignment.start, | |||||
| children: [ | |||||
| Text( | |||||
| 'ss - ', | |||||
| style: StylesText.body5, | |||||
| ), | |||||
| Text( | |||||
| '20/22/2202', | |||||
| style: StylesText.caption3.copyWith(), | |||||
| ) | |||||
| ], | |||||
| ), | |||||
| const SizedBox( | |||||
| height: 8, | |||||
| ), | |||||
| _widgetItemInfoCompleted( | |||||
| title: 'title', | |||||
| actionDate: '2/2/2023', | |||||
| nguoiThucHiens: 'nguoi thuc hien', | |||||
| ), | |||||
| ], | |||||
| ), | |||||
| ); | |||||
| } | |||||
| Widget _widgetItemInfoCompleted({ | |||||
| required String title, | |||||
| required String actionDate, | |||||
| String? nguoiThucHiens, | |||||
| }) { | |||||
| return Container( | |||||
| padding: const EdgeInsets.all(8), | |||||
| decoration: BoxDecoration( | |||||
| borderRadius: BorderRadius.circular(8), | |||||
| color: AppColors.background1, | |||||
| ), | |||||
| child: Column( | |||||
| mainAxisAlignment: MainAxisAlignment.start, | |||||
| children: [ | |||||
| Row( | |||||
| children: [ | |||||
| Expanded( | |||||
| child: Text( | |||||
| title, | |||||
| style: StylesText.caption2.copyWith( | |||||
| color: AppColors.neutral1, | |||||
| ), | |||||
| ), | |||||
| ), | |||||
| Text( | |||||
| actionDate, | |||||
| style: StylesText.caption3.copyWith( | |||||
| color: AppColors.neutral1, | |||||
| ), | |||||
| ), | |||||
| ], | |||||
| ), | |||||
| Padding( | |||||
| padding: const EdgeInsets.symmetric(vertical: 8.0), | |||||
| child: Row( | |||||
| children: [ | |||||
| Expanded( | |||||
| child: Text( | |||||
| 'Người thực hiện', | |||||
| style: StylesText.caption2.copyWith( | |||||
| color: AppColors.neutral1, | |||||
| ), | |||||
| ), | |||||
| ), | |||||
| Text( | |||||
| nguoiThucHiens ?? '', | |||||
| style: StylesText.caption3.copyWith( | |||||
| color: AppColors.neutral1, | |||||
| ), | |||||
| ), | |||||
| ], | |||||
| ), | |||||
| ), | |||||
| ], | |||||
| ), | |||||
| ); | |||||
| } | |||||
| } |
| import 'package:flutter/material.dart'; | |||||
| import '../../../../themes/styles_text.dart'; | |||||
| class ItemColumnWidget extends StatelessWidget { | |||||
| final String title; | |||||
| double? textSize12; | |||||
| final Widget child; | |||||
| ItemColumnWidget({Key? key, required this.title, required this.child, this.textSize12}) : super(key: key); | |||||
| @override | |||||
| Widget build(BuildContext context) { | |||||
| return Container( | |||||
| child: Column( | |||||
| crossAxisAlignment: CrossAxisAlignment.start, | |||||
| children: [ | |||||
| Text( | |||||
| title, | |||||
| style: textSize12 != null ? StylesText.body6.copyWith(fontSize: textSize12) : StylesText.body6, | |||||
| ), | |||||
| const SizedBox( | |||||
| height: 5, | |||||
| ), | |||||
| child, | |||||
| ], | |||||
| ), | |||||
| ); | |||||
| } | |||||
| } |
| height: 8, | height: 8, | ||||
| ), | ), | ||||
| Container( | Container( | ||||
| padding: EdgeInsets.all(8), | |||||
| color: Colors.white, | |||||
| child: Text( | |||||
| 'Danh sách lô trồng', | |||||
| style: TextStyle(fontWeight: FontWeight.w500, fontSize: 22), | |||||
| )), | |||||
| padding: EdgeInsets.all(8), | |||||
| color: Colors.white, | |||||
| child: Text( | |||||
| 'Danh sách lô trồng', | |||||
| style: TextStyle(fontWeight: FontWeight.w500, fontSize: 22), | |||||
| ), | |||||
| ), | |||||
| WidgetSearch(), | WidgetSearch(), | ||||
| Expanded(child: BlocBuilder<PlotBloc, PlotState>( | Expanded(child: BlocBuilder<PlotBloc, PlotState>( | ||||
| builder: (context, state) { | builder: (context, state) { |
| import 'dart:io'; | |||||
| import 'package:badges/badges.dart' as badge; | |||||
| import 'package:dio/dio.dart'; | import 'package:dio/dio.dart'; | ||||
| import 'package:farm_tpf/custom_model/NotificationObjectDTO.dart'; | import 'package:farm_tpf/custom_model/NotificationObjectDTO.dart'; | ||||
| import 'package:farm_tpf/data/repository/repository.dart'; | import 'package:farm_tpf/data/repository/repository.dart'; | ||||
| import 'package:farm_tpf/data/repository/user_repository.dart'; | import 'package:farm_tpf/data/repository/user_repository.dart'; | ||||
| import 'package:farm_tpf/presentation/custom_widgets/widget_utils.dart'; | import 'package:farm_tpf/presentation/custom_widgets/widget_utils.dart'; | ||||
| import 'package:farm_tpf/presentation/screens/account/sc_account.dart'; | import 'package:farm_tpf/presentation/screens/account/sc_account.dart'; | ||||
| import 'package:farm_tpf/presentation/screens/codes/code_page.dart'; | |||||
| import 'package:farm_tpf/presentation/screens/control_device/sc_control_device.dart'; | import 'package:farm_tpf/presentation/screens/control_device/sc_control_device.dart'; | ||||
| import 'package:farm_tpf/presentation/screens/notification/sc_notification.dart'; | import 'package:farm_tpf/presentation/screens/notification/sc_notification.dart'; | ||||
| import 'package:farm_tpf/presentation/screens/notification/update_count_noti_bloc.dart'; | import 'package:farm_tpf/presentation/screens/notification/update_count_noti_bloc.dart'; | ||||
| import 'package:farm_tpf/presentation/screens/plot/sc_plot.dart'; | import 'package:farm_tpf/presentation/screens/plot/sc_plot.dart'; | ||||
| import 'package:farm_tpf/presentation/screens/plot_detail/sc_plot_detail.dart'; | import 'package:farm_tpf/presentation/screens/plot_detail/sc_plot_detail.dart'; | ||||
| import 'package:farm_tpf/presentation/screens/qr_scan/qr_scan_page.dart'; | |||||
| import 'package:farm_tpf/utils/NotificationsBloc.dart'; | import 'package:farm_tpf/utils/NotificationsBloc.dart'; | ||||
| import 'package:farm_tpf/utils/const_color.dart'; | import 'package:farm_tpf/utils/const_color.dart'; | ||||
| import 'package:farm_tpf/utils/const_common.dart'; | import 'package:farm_tpf/utils/const_common.dart'; | ||||
| import 'package:farm_tpf/utils/const_icons.dart'; | import 'package:farm_tpf/utils/const_icons.dart'; | ||||
| import 'package:farm_tpf/utils/pref.dart'; | import 'package:farm_tpf/utils/pref.dart'; | ||||
| import 'package:firebase_messaging/firebase_messaging.dart'; | |||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_svg/flutter_svg.dart'; | import 'package:flutter_svg/flutter_svg.dart'; | ||||
| import 'package:get/get.dart'; | import 'package:get/get.dart'; | ||||
| import '../../../main.dart'; | |||||
| class TabbarScreen extends StatefulWidget { | class TabbarScreen extends StatefulWidget { | ||||
| static Route route() { | static Route route() { | ||||
| return MaterialPageRoute<void>(builder: (_) => TabbarScreen()); | return MaterialPageRoute<void>(builder: (_) => TabbarScreen()); | ||||
| return ControlDeviceScreen(); | return ControlDeviceScreen(); | ||||
| break; | break; | ||||
| case TabBarIndex.qr: | case TabBarIndex.qr: | ||||
| return Container(); | |||||
| return CodePage(); | |||||
| break; | break; | ||||
| case TabBarIndex.notification: | case TabBarIndex.notification: | ||||
| return NotificationScreen(); | return NotificationScreen(); | ||||
| )), | )), | ||||
| onTap: () { | onTap: () { | ||||
| //Open scan qr code when tap icon in tabbar | //Open scan qr code when tap icon in tabbar | ||||
| if (index == 2) { | |||||
| changeTabbar.changeIndex(changeTabbar.index ?? TabBarIndex.plot); | |||||
| // scan(context); | |||||
| Get.to( | |||||
| QrCodeScannerScreen(), | |||||
| ).then((value) { | |||||
| if (value != null) { | |||||
| _showAlertCheckCropCode(value); | |||||
| } | |||||
| }); | |||||
| } else { | |||||
| changeTabbar.changeIndex(itemsTabbar[index].index); | |||||
| } | |||||
| // if (index == 2) { | |||||
| // changeTabbar.changeIndex(changeTabbar.index ?? TabBarIndex.plot); | |||||
| // // scan(context); | |||||
| // Get.to( | |||||
| // QrCodeScannerScreen(), | |||||
| // ).then((value) { | |||||
| // if (value != null) { | |||||
| // _showAlertCheckCropCode(value); | |||||
| // } | |||||
| // }); | |||||
| // } else { | |||||
| // changeTabbar.changeIndex(itemsTabbar[index].index); | |||||
| // } | |||||
| changeTabbar.changeIndex(itemsTabbar[index].index); | |||||
| }, | }, | ||||
| ); | ); | ||||
| }), | }), |