| @@ -1 +1 @@ | |||
| eb141365fb69a4e0decadd2584d53516 | |||
| 0d1f90510e22958a585802bb59f9c570 | |||
| @@ -0,0 +1,22 @@ | |||
| { | |||
| "id": 3, | |||
| "activityId": 6, | |||
| "cropId": 1, | |||
| "executeDate": "2020-09-19T14:21:39Z", | |||
| "description": "", | |||
| "executeBy": "SinhLe", | |||
| "media": "Nursery/9027b1d4-b904-4141-ab2e-7ad7768402b4.mp4;Nursery/9b48173a-7750-4def-aa3f-00b7f2a0e511.jpg;", | |||
| "seedName": "Rau muống", | |||
| "substratesId": 1, | |||
| "seedLength": 1, | |||
| "quantity": 2, | |||
| "seedIncubationTime": 1, | |||
| "nurseryDetail": [ | |||
| { | |||
| "id": 5, | |||
| "workerName": "7", | |||
| "trayNumber": "8", | |||
| "tbNurseryId": 3 | |||
| } | |||
| ] | |||
| } | |||
| @@ -1,30 +1,47 @@ | |||
| import 'NurseryDetail.dart'; | |||
| class Nursery { | |||
| int id; | |||
| int activityId; | |||
| int cropId; | |||
| String executeDate; | |||
| String description; | |||
| String substrates; | |||
| num quantity; | |||
| String executeBy; | |||
| String media; | |||
| String seedName; | |||
| num substratesId; | |||
| num seedLength; | |||
| num quantity; | |||
| num seedIncubationTime; | |||
| List<NurseryDetail> nurseryDetail; | |||
| Nursery( | |||
| {this.cropId, | |||
| {this.id, | |||
| this.activityId, | |||
| this.cropId, | |||
| this.executeDate, | |||
| this.description, | |||
| this.substrates, | |||
| this.quantity, | |||
| this.executeBy, | |||
| this.media, | |||
| this.seedName, | |||
| this.substratesId, | |||
| this.seedLength, | |||
| this.quantity, | |||
| this.seedIncubationTime, | |||
| this.nurseryDetail}); | |||
| Nursery.fromJson(Map<String, dynamic> json) { | |||
| id = json['id']; | |||
| activityId = json['activityId']; | |||
| cropId = json['cropId']; | |||
| executeDate = json['executeDate']; | |||
| description = json['description']; | |||
| substrates = json['substrates']; | |||
| quantity = json['quantity']; | |||
| executeBy = json['executeBy']; | |||
| media = json['media']; | |||
| seedName = json['seedName']; | |||
| substratesId = json['substratesId']; | |||
| seedLength = json['seedLength']; | |||
| quantity = json['quantity']; | |||
| seedIncubationTime = json['seedIncubationTime']; | |||
| if (json['nurseryDetail'] != null) { | |||
| nurseryDetail = new List<NurseryDetail>(); | |||
| @@ -36,12 +53,17 @@ class Nursery { | |||
| Map<String, dynamic> toJson() { | |||
| final Map<String, dynamic> data = new Map<String, dynamic>(); | |||
| data['id'] = this.id; | |||
| data['activityId'] = this.activityId; | |||
| data['cropId'] = this.cropId; | |||
| data['executeDate'] = this.executeDate; | |||
| data['description'] = this.description; | |||
| data['substrates'] = this.substrates; | |||
| data['quantity'] = this.quantity; | |||
| data['executeBy'] = this.executeBy; | |||
| data['media'] = this.media; | |||
| data['seedName'] = this.seedName; | |||
| data['substratesId'] = this.substratesId; | |||
| data['seedLength'] = this.seedLength; | |||
| data['quantity'] = this.quantity; | |||
| data['seedIncubationTime'] = this.seedIncubationTime; | |||
| if (this.nurseryDetail != null) { | |||
| data['nurseryDetail'] = | |||
| @@ -50,32 +72,3 @@ class Nursery { | |||
| return data; | |||
| } | |||
| } | |||
| class NurseryDetail { | |||
| int id; | |||
| String workerName; | |||
| String trayNumber; | |||
| bool isExpanded = false; | |||
| NurseryDetail({this.workerName, this.trayNumber}); | |||
| NurseryDetail.clone(NurseryDetail nurseryDetail) { | |||
| this.id = nurseryDetail.id; | |||
| this.workerName = nurseryDetail.workerName; | |||
| this.trayNumber = nurseryDetail.trayNumber; | |||
| this.isExpanded = nurseryDetail.isExpanded; | |||
| } | |||
| NurseryDetail.fromJson(Map<String, dynamic> json) { | |||
| id = json['id']; | |||
| workerName = json['workerName']; | |||
| trayNumber = json['trayNumber']; | |||
| } | |||
| Map<String, dynamic> toJson() { | |||
| final Map<String, dynamic> data = new Map<String, dynamic>(); | |||
| data['id'] = this.id; | |||
| data['workerName'] = this.workerName; | |||
| data['trayNumber'] = this.trayNumber; | |||
| return data; | |||
| } | |||
| } | |||
| @@ -0,0 +1,31 @@ | |||
| class NurseryDetail { | |||
| int id; | |||
| String workerName; | |||
| String trayNumber; | |||
| int tbNurseryId; | |||
| NurseryDetail({this.id, this.workerName, this.trayNumber, this.tbNurseryId}); | |||
| NurseryDetail.clone(NurseryDetail nurseryDetail) { | |||
| this.id = nurseryDetail.id; | |||
| this.workerName = nurseryDetail.workerName; | |||
| this.trayNumber = nurseryDetail.trayNumber; | |||
| this.tbNurseryId = nurseryDetail.tbNurseryId; | |||
| } | |||
| NurseryDetail.fromJson(Map<String, dynamic> json) { | |||
| id = json['id']; | |||
| workerName = json['workerName']; | |||
| trayNumber = json['trayNumber']; | |||
| tbNurseryId = json['tbNurseryId']; | |||
| } | |||
| Map<String, dynamic> toJson() { | |||
| final Map<String, dynamic> data = new Map<String, dynamic>(); | |||
| data['id'] = this.id; | |||
| data['workerName'] = this.workerName; | |||
| data['trayNumber'] = this.trayNumber; | |||
| data['tbNurseryId'] = this.tbNurseryId; | |||
| return data; | |||
| } | |||
| } | |||
| @@ -3,6 +3,8 @@ import 'dart:async'; | |||
| import 'package:bloc/bloc.dart'; | |||
| import 'package:equatable/equatable.dart'; | |||
| import 'package:farm_tpf/custom_model/Nursery.dart'; | |||
| import 'package:farm_tpf/custom_model/Nursery.dart'; | |||
| import 'package:farm_tpf/custom_model/NurseryDetail.dart'; | |||
| import 'package:meta/meta.dart'; | |||
| part 'expansion_list_event.dart'; | |||
| @@ -19,23 +21,6 @@ class ExpansionListBloc extends Bloc<ExpansionListEvent, ExpansionListState> { | |||
| List<NurseryDetail> items = new List<NurseryDetail>(); | |||
| for (int i = 0; i < event.items.length; i++) { | |||
| var current = event.items[i]; | |||
| if (i == 0) { | |||
| current.isExpanded = true; | |||
| } else { | |||
| current.isExpanded = false; | |||
| } | |||
| items.add(NurseryDetail.clone(current)); | |||
| } | |||
| yield ExpansionListSuccess(items: items); | |||
| } else if (event is Collapse) { | |||
| List<NurseryDetail> items = new List<NurseryDetail>(); | |||
| for (int i = 0; i < event.items.length; i++) { | |||
| var current = event.items[i]; | |||
| if (i == event.index) { | |||
| current.isExpanded = !current.isExpanded; | |||
| } else { | |||
| current.isExpanded = false; | |||
| } | |||
| items.add(NurseryDetail.clone(current)); | |||
| } | |||
| yield ExpansionListSuccess(items: items); | |||
| @@ -24,9 +24,3 @@ class DeleteItem extends ExpansionListEvent { | |||
| final List<NurseryDetail> items; | |||
| DeleteItem({@required this.index, @required this.items}); | |||
| } | |||
| class Collapse extends ExpansionListEvent { | |||
| final int index; | |||
| final List<NurseryDetail> items; | |||
| Collapse({@required this.index, @required this.items}); | |||
| } | |||
| @@ -1,10 +1,9 @@ | |||
| import 'dart:convert'; | |||
| import 'package:farm_tpf/custom_model/Nursery.dart'; | |||
| import 'package:farm_tpf/data/api/app_exception.dart'; | |||
| import 'package:farm_tpf/custom_model/NurseryDetail.dart'; | |||
| import 'package:farm_tpf/data/repository/repository.dart'; | |||
| import 'package:farm_tpf/models/index.dart'; | |||
| import 'package:farm_tpf/presentation/custom_widgets/widget_media_helper.dart'; | |||
| import 'package:farm_tpf/presentation/custom_widgets/widget_media_picker.dart'; | |||
| import 'package:farm_tpf/presentation/screens/actions/nursery/bloc/expansion_list_bloc.dart'; | |||
| import 'package:farm_tpf/presentation/screens/resources/sc_resource_helper.dart'; | |||
| @@ -62,8 +61,6 @@ class _EditActionNurseryState extends State<EditActionNurseryScreen> { | |||
| super.initState(); | |||
| changeSupply.initValue(); | |||
| changeFileController.initValue(); | |||
| filePaths.add( | |||
| "/Users/macbook/Library/Developer/CoreSimulator/Devices/CC52586C-39AA-489B-A74D-83107AA9F7C1/data/Containers/Data/Application/F377B9A4-939D-42B2-9628-350C4A0BFD50/tmp/image_picker_801FCF0F-577F-4AFA-AC58-1C716A1C561C-30707-000015F0E14256F8.jpg"); | |||
| _nursery.nurseryDetail = new List<NurseryDetail>(); | |||
| flutterToast = FlutterToast(context); | |||
| //UPDATE | |||
| @@ -150,7 +147,7 @@ class _EditActionNurseryState extends State<EditActionNurseryScreen> { | |||
| .then((value) { | |||
| if (value != null) { | |||
| var result = value as Supply; | |||
| _nursery.substrates = result.name; | |||
| _nursery.substratesId = result.id; | |||
| changeSupply.change(result); | |||
| print("Home: $value"); | |||
| } | |||
| @@ -1,360 +0,0 @@ | |||
| import 'dart:io'; | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:image_picker/image_picker.dart'; | |||
| import 'package:video_player/video_player.dart'; | |||
| class ActionNurseryScreen extends StatefulWidget { | |||
| @override | |||
| _ActionNurseryScreenState createState() => _ActionNurseryScreenState(); | |||
| } | |||
| class _ActionNurseryScreenState extends State<ActionNurseryScreen> { | |||
| PickedFile _imageFile; | |||
| dynamic _pickImageError; | |||
| bool isVideo = false; | |||
| VideoPlayerController _controller; | |||
| VideoPlayerController _toBeDisposed; | |||
| String _retrieveDataError; | |||
| final ImagePicker _picker = ImagePicker(); | |||
| final TextEditingController maxWidthController = TextEditingController(); | |||
| final TextEditingController maxHeightController = TextEditingController(); | |||
| final TextEditingController qualityController = TextEditingController(); | |||
| Future<void> _playVideo(PickedFile file) async { | |||
| if (file != null && mounted) { | |||
| await _disposeVideoController(); | |||
| _controller = VideoPlayerController.file(File(file.path)); | |||
| await _controller.setVolume(1.0); | |||
| await _controller.initialize(); | |||
| await _controller.setLooping(true); | |||
| await _controller.play(); | |||
| setState(() {}); | |||
| } | |||
| } | |||
| void _onImageButtonPressed(ImageSource source, {BuildContext context}) async { | |||
| if (_controller != null) { | |||
| await _controller.setVolume(0.0); | |||
| } | |||
| if (isVideo) { | |||
| final PickedFile file = await _picker.getVideo( | |||
| source: source, maxDuration: const Duration(seconds: 10)); | |||
| await _playVideo(file); | |||
| } else { | |||
| await _displayPickImageDialog(context, | |||
| (double maxWidth, double maxHeight, int quality) async { | |||
| try { | |||
| final pickedFile = await _picker.getImage( | |||
| source: source, | |||
| maxWidth: maxWidth, | |||
| maxHeight: maxHeight, | |||
| imageQuality: quality, | |||
| ); | |||
| setState(() { | |||
| _imageFile = pickedFile; | |||
| }); | |||
| } catch (e) { | |||
| setState(() { | |||
| _pickImageError = e; | |||
| }); | |||
| } | |||
| }); | |||
| } | |||
| } | |||
| @override | |||
| void deactivate() { | |||
| if (_controller != null) { | |||
| _controller.setVolume(0.0); | |||
| _controller.pause(); | |||
| } | |||
| super.deactivate(); | |||
| } | |||
| @override | |||
| void dispose() { | |||
| _disposeVideoController(); | |||
| maxWidthController.dispose(); | |||
| maxHeightController.dispose(); | |||
| qualityController.dispose(); | |||
| super.dispose(); | |||
| } | |||
| Future<void> _disposeVideoController() async { | |||
| if (_toBeDisposed != null) { | |||
| await _toBeDisposed.dispose(); | |||
| } | |||
| _toBeDisposed = _controller; | |||
| _controller = null; | |||
| } | |||
| Widget _previewVideo() { | |||
| final Text retrieveError = _getRetrieveErrorWidget(); | |||
| if (retrieveError != null) { | |||
| return retrieveError; | |||
| } | |||
| if (_controller == null) { | |||
| return const Text( | |||
| 'You have not yet picked a video', | |||
| textAlign: TextAlign.center, | |||
| ); | |||
| } | |||
| return Padding( | |||
| padding: const EdgeInsets.all(10.0), | |||
| child: AspectRatioVideo(_controller), | |||
| ); | |||
| } | |||
| Widget _previewImage() { | |||
| final Text retrieveError = _getRetrieveErrorWidget(); | |||
| if (retrieveError != null) { | |||
| return retrieveError; | |||
| } | |||
| if (_imageFile != null) { | |||
| return Image.file(File(_imageFile.path)); | |||
| } else if (_pickImageError != null) { | |||
| return Text( | |||
| 'Pick image error: $_pickImageError', | |||
| textAlign: TextAlign.center, | |||
| ); | |||
| } else { | |||
| return const Text( | |||
| 'You have not yet picked an image.', | |||
| textAlign: TextAlign.center, | |||
| ); | |||
| } | |||
| } | |||
| Future<void> retrieveLostData() async { | |||
| final LostData response = await _picker.getLostData(); | |||
| if (response.isEmpty) { | |||
| return; | |||
| } | |||
| if (response.file != null) { | |||
| if (response.type == RetrieveType.video) { | |||
| isVideo = true; | |||
| await _playVideo(response.file); | |||
| } else { | |||
| isVideo = false; | |||
| setState(() { | |||
| _imageFile = response.file; | |||
| }); | |||
| } | |||
| } else { | |||
| _retrieveDataError = response.exception.code; | |||
| } | |||
| } | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return Scaffold( | |||
| appBar: AppBar( | |||
| title: Text("Test"), | |||
| ), | |||
| body: Center( | |||
| //TODO : check default platform is android | |||
| child: 1 == 1 | |||
| ? FutureBuilder<void>( | |||
| future: retrieveLostData(), | |||
| builder: (BuildContext context, AsyncSnapshot<void> snapshot) { | |||
| switch (snapshot.connectionState) { | |||
| case ConnectionState.none: | |||
| case ConnectionState.waiting: | |||
| return const Text( | |||
| 'You have not yet picked an image.', | |||
| textAlign: TextAlign.center, | |||
| ); | |||
| case ConnectionState.done: | |||
| return isVideo ? _previewVideo() : _previewImage(); | |||
| default: | |||
| if (snapshot.hasError) { | |||
| return Text( | |||
| 'Pick image/video error: ${snapshot.error}}', | |||
| textAlign: TextAlign.center, | |||
| ); | |||
| } else { | |||
| return const Text( | |||
| 'You have not yet picked an image.', | |||
| textAlign: TextAlign.center, | |||
| ); | |||
| } | |||
| } | |||
| }, | |||
| ) | |||
| : (isVideo ? _previewVideo() : _previewImage()), | |||
| ), | |||
| floatingActionButton: Column( | |||
| mainAxisAlignment: MainAxisAlignment.end, | |||
| children: <Widget>[ | |||
| FloatingActionButton( | |||
| onPressed: () { | |||
| isVideo = false; | |||
| _onImageButtonPressed(ImageSource.gallery, context: context); | |||
| }, | |||
| heroTag: 'image0', | |||
| tooltip: 'Pick Image from gallery', | |||
| child: const Icon(Icons.photo_library), | |||
| ), | |||
| Padding( | |||
| padding: const EdgeInsets.only(top: 16.0), | |||
| child: FloatingActionButton( | |||
| onPressed: () { | |||
| isVideo = false; | |||
| _onImageButtonPressed(ImageSource.camera, context: context); | |||
| }, | |||
| heroTag: 'image1', | |||
| tooltip: 'Take a Photo', | |||
| child: const Icon(Icons.camera_alt), | |||
| ), | |||
| ), | |||
| Padding( | |||
| padding: const EdgeInsets.only(top: 16.0), | |||
| child: FloatingActionButton( | |||
| backgroundColor: Colors.red, | |||
| onPressed: () { | |||
| isVideo = true; | |||
| _onImageButtonPressed(ImageSource.gallery); | |||
| }, | |||
| heroTag: 'video0', | |||
| tooltip: 'Pick Video from gallery', | |||
| child: const Icon(Icons.video_library), | |||
| ), | |||
| ), | |||
| Padding( | |||
| padding: const EdgeInsets.only(top: 16.0), | |||
| child: FloatingActionButton( | |||
| backgroundColor: Colors.red, | |||
| onPressed: () { | |||
| isVideo = true; | |||
| _onImageButtonPressed(ImageSource.camera); | |||
| }, | |||
| heroTag: 'video1', | |||
| tooltip: 'Take a Video', | |||
| child: const Icon(Icons.videocam), | |||
| ), | |||
| ), | |||
| ], | |||
| ), | |||
| ); | |||
| } | |||
| Text _getRetrieveErrorWidget() { | |||
| if (_retrieveDataError != null) { | |||
| final Text result = Text(_retrieveDataError); | |||
| _retrieveDataError = null; | |||
| return result; | |||
| } | |||
| return null; | |||
| } | |||
| Future<void> _displayPickImageDialog( | |||
| BuildContext context, OnPickImageCallback onPick) async { | |||
| return showDialog( | |||
| context: context, | |||
| builder: (context) { | |||
| return AlertDialog( | |||
| title: Text('Add optional parameters'), | |||
| content: Column( | |||
| children: <Widget>[ | |||
| TextField( | |||
| controller: maxWidthController, | |||
| keyboardType: TextInputType.numberWithOptions(decimal: true), | |||
| decoration: | |||
| InputDecoration(hintText: "Enter maxWidth if desired"), | |||
| ), | |||
| TextField( | |||
| controller: maxHeightController, | |||
| keyboardType: TextInputType.numberWithOptions(decimal: true), | |||
| decoration: | |||
| InputDecoration(hintText: "Enter maxHeight if desired"), | |||
| ), | |||
| TextField( | |||
| controller: qualityController, | |||
| keyboardType: TextInputType.number, | |||
| decoration: | |||
| InputDecoration(hintText: "Enter quality if desired"), | |||
| ), | |||
| ], | |||
| ), | |||
| actions: <Widget>[ | |||
| FlatButton( | |||
| child: const Text('CANCEL'), | |||
| onPressed: () { | |||
| Navigator.of(context).pop(); | |||
| }, | |||
| ), | |||
| FlatButton( | |||
| child: const Text('PICK'), | |||
| onPressed: () { | |||
| double width = maxWidthController.text.isNotEmpty | |||
| ? double.parse(maxWidthController.text) | |||
| : null; | |||
| double height = maxHeightController.text.isNotEmpty | |||
| ? double.parse(maxHeightController.text) | |||
| : null; | |||
| int quality = qualityController.text.isNotEmpty | |||
| ? int.parse(qualityController.text) | |||
| : null; | |||
| onPick(width, height, quality); | |||
| Navigator.of(context).pop(); | |||
| }), | |||
| ], | |||
| ); | |||
| }); | |||
| } | |||
| } | |||
| typedef void OnPickImageCallback( | |||
| double maxWidth, double maxHeight, int quality); | |||
| class AspectRatioVideo extends StatefulWidget { | |||
| AspectRatioVideo(this.controller); | |||
| final VideoPlayerController controller; | |||
| @override | |||
| AspectRatioVideoState createState() => AspectRatioVideoState(); | |||
| } | |||
| class AspectRatioVideoState extends State<AspectRatioVideo> { | |||
| VideoPlayerController get controller => widget.controller; | |||
| bool initialized = false; | |||
| void _onVideoControllerUpdate() { | |||
| if (!mounted) { | |||
| return; | |||
| } | |||
| if (initialized != controller.value.initialized) { | |||
| initialized = controller.value.initialized; | |||
| setState(() {}); | |||
| } | |||
| } | |||
| @override | |||
| void initState() { | |||
| super.initState(); | |||
| controller.addListener(_onVideoControllerUpdate); | |||
| } | |||
| @override | |||
| void dispose() { | |||
| controller.removeListener(_onVideoControllerUpdate); | |||
| super.dispose(); | |||
| } | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| if (initialized) { | |||
| return Center( | |||
| child: AspectRatio( | |||
| aspectRatio: controller.value?.aspectRatio, | |||
| child: VideoPlayer(controller), | |||
| ), | |||
| ); | |||
| } else { | |||
| return Container(); | |||
| } | |||
| } | |||
| } | |||
| @@ -3,7 +3,6 @@ import 'dart:io'; | |||
| import 'package:dio/dio.dart'; | |||
| import 'package:farm_tpf/data/repository/user_repository.dart'; | |||
| import 'package:farm_tpf/main.dart'; | |||
| import 'package:farm_tpf/presentation/screens/actions/nursery/sc_nursery.dart'; | |||
| import 'package:farm_tpf/presentation/screens/actions/plant/sc_plant.dart'; | |||
| import 'package:farm_tpf/presentation/screens/plot/sc_plot.dart'; | |||
| import 'package:farm_tpf/presentation/screens/plot_detail/sc_plot_detail.dart'; | |||
| @@ -88,12 +87,6 @@ class _HomePageState extends State<HomePage> { | |||
| mainAxisSize: MainAxisSize.min, | |||
| children: <Widget>[ | |||
| Text("logged in."), | |||
| MaterialButton( | |||
| child: Text("Nursery action"), | |||
| onPressed: () { | |||
| Navigator.of(context).push( | |||
| MaterialPageRoute(builder: (_) => ActionNurseryScreen())); | |||
| }), | |||
| MaterialButton( | |||
| child: Text("Plant action"), | |||
| onPressed: () { | |||
| @@ -2,7 +2,7 @@ import 'dart:async'; | |||
| import 'package:bloc/bloc.dart'; | |||
| import 'package:equatable/equatable.dart'; | |||
| import 'package:farm_tpf/custom_model/Nursery.dart'; | |||
| import 'package:farm_tpf/custom_model/NurseryDetail.dart'; | |||
| import 'package:farm_tpf/utils/const_common.dart'; | |||
| import 'package:meta/meta.dart'; | |||