| @@ -0,0 +1,12 @@ | |||
| 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]; | |||
| } | |||
| @@ -0,0 +1,68 @@ | |||
| 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(), | |||
| ], | |||
| ), | |||
| ), | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,68 @@ | |||
| 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), | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,131 @@ | |||
| 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, | |||
| ), | |||
| ) | |||
| ], | |||
| ), | |||
| ), | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,184 @@ | |||
| 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, | |||
| ); | |||
| }, | |||
| ), | |||
| ) | |||
| ], | |||
| ), | |||
| ), | |||
| ); | |||
| }, | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,175 @@ | |||
| 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, | |||
| ) | |||
| ], | |||
| ), | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,98 @@ | |||
| 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, | |||
| ), | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,145 @@ | |||
| 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, | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,67 @@ | |||
| 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, | |||
| // ); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,22 @@ | |||
| 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(); | |||
| } | |||
| @@ -0,0 +1,49 @@ | |||
| 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, | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,155 @@ | |||
| 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, | |||
| ), | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| ], | |||
| ), | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,29 @@ | |||
| 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, | |||
| ], | |||
| ), | |||
| ); | |||
| } | |||
| } | |||
| @@ -80,12 +80,13 @@ class _InfinityViewState extends State<InfinityView> { | |||
| height: 8, | |||
| ), | |||
| 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(), | |||
| Expanded(child: BlocBuilder<PlotBloc, PlotState>( | |||
| builder: (context, state) { | |||
| @@ -1,30 +1,24 @@ | |||
| import 'dart:io'; | |||
| import 'package:badges/badges.dart' as badge; | |||
| import 'package:dio/dio.dart'; | |||
| import 'package:farm_tpf/custom_model/NotificationObjectDTO.dart'; | |||
| import 'package:farm_tpf/data/repository/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/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/notification/sc_notification.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_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/const_color.dart'; | |||
| import 'package:farm_tpf/utils/const_common.dart'; | |||
| import 'package:farm_tpf/utils/const_icons.dart'; | |||
| import 'package:farm_tpf/utils/pref.dart'; | |||
| import 'package:firebase_messaging/firebase_messaging.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:flutter_svg/flutter_svg.dart'; | |||
| import 'package:get/get.dart'; | |||
| import '../../../main.dart'; | |||
| class TabbarScreen extends StatefulWidget { | |||
| static Route route() { | |||
| return MaterialPageRoute<void>(builder: (_) => TabbarScreen()); | |||
| @@ -214,7 +208,7 @@ class _TabbarScreenState extends State<TabbarScreen> { | |||
| return ControlDeviceScreen(); | |||
| break; | |||
| case TabBarIndex.qr: | |||
| return Container(); | |||
| return CodePage(); | |||
| break; | |||
| case TabBarIndex.notification: | |||
| return NotificationScreen(); | |||
| @@ -277,19 +271,20 @@ class _TabbarScreenState extends State<TabbarScreen> { | |||
| )), | |||
| onTap: () { | |||
| //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); | |||
| }, | |||
| ); | |||
| }), | |||