|
|
|
@@ -0,0 +1,360 @@ |
|
|
|
import 'dart:io'; |
|
|
|
import 'dart:async'; |
|
|
|
|
|
|
|
import 'package:farm_tpf/utils/const_color.dart'; |
|
|
|
import 'package:farm_tpf/utils/const_string.dart'; |
|
|
|
import 'package:flutter/cupertino.dart'; |
|
|
|
import 'package:flutter/material.dart'; |
|
|
|
import 'package:image_picker/image_picker.dart'; |
|
|
|
import 'package:video_player/video_player.dart'; |
|
|
|
|
|
|
|
class WidgetMediaHelper extends StatefulWidget { |
|
|
|
@override |
|
|
|
_WidgetMediaHelperState createState() => _WidgetMediaHelperState(); |
|
|
|
} |
|
|
|
|
|
|
|
class _WidgetMediaHelperState extends State<WidgetMediaHelper> { |
|
|
|
PickedFile _imageFile; |
|
|
|
dynamic _pickImageError; |
|
|
|
bool isVideo = false; |
|
|
|
VideoPlayerController _controller; |
|
|
|
VideoPlayerController _toBeDisposed; |
|
|
|
String _retrieveDataError; |
|
|
|
|
|
|
|
final ImagePicker _picker = ImagePicker(); |
|
|
|
|
|
|
|
@override |
|
|
|
Widget build(BuildContext context) { |
|
|
|
return Container( |
|
|
|
child: Column( |
|
|
|
children: <Widget>[ |
|
|
|
_actionButton(), |
|
|
|
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()), |
|
|
|
], |
|
|
|
)); |
|
|
|
} |
|
|
|
|
|
|
|
Widget viewResult() { |
|
|
|
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, |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
Widget multipleChoice() { |
|
|
|
return CupertinoAlertDialog( |
|
|
|
title: Text(label_title_select_media), |
|
|
|
actions: <Widget>[ |
|
|
|
CupertinoDialogAction( |
|
|
|
child: const Text(label_select_image_from_library), |
|
|
|
onPressed: () { |
|
|
|
Navigator.pop(context, 'Discard'); |
|
|
|
isVideo = false; |
|
|
|
_onImageButtonPressed(ImageSource.gallery, context: context); |
|
|
|
}), |
|
|
|
CupertinoDialogAction( |
|
|
|
child: const Text(label_take_photo), |
|
|
|
onPressed: () { |
|
|
|
Navigator.pop(context, 'Discard'); |
|
|
|
isVideo = false; |
|
|
|
_onImageButtonPressed(ImageSource.camera, context: context); |
|
|
|
}), |
|
|
|
CupertinoDialogAction( |
|
|
|
child: const Text(label_select_video_from_library), |
|
|
|
onPressed: () { |
|
|
|
Navigator.pop(context, 'Discard'); |
|
|
|
isVideo = true; |
|
|
|
_onImageButtonPressed(ImageSource.gallery); |
|
|
|
}), |
|
|
|
CupertinoDialogAction( |
|
|
|
child: const Text(label_record_video), |
|
|
|
onPressed: () { |
|
|
|
Navigator.pop(context, 'Discard'); |
|
|
|
isVideo = true; |
|
|
|
_onImageButtonPressed(ImageSource.camera); |
|
|
|
}), |
|
|
|
CupertinoDialogAction( |
|
|
|
child: const Text(label_cancel), |
|
|
|
textStyle: TextStyle(fontWeight: FontWeight.bold), |
|
|
|
isDefaultAction: true, |
|
|
|
onPressed: () { |
|
|
|
Navigator.pop(context, 'Cancel'); |
|
|
|
}), |
|
|
|
], |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
Widget _actionButton() { |
|
|
|
return SizedBox( |
|
|
|
width: double.infinity, |
|
|
|
height: 55, |
|
|
|
child: FlatButton( |
|
|
|
onPressed: () { |
|
|
|
showDialog( |
|
|
|
context: context, |
|
|
|
barrierDismissible: true, |
|
|
|
builder: (context) => Opacity( |
|
|
|
child: multipleChoice(), |
|
|
|
opacity: 1, |
|
|
|
)); |
|
|
|
}, |
|
|
|
color: COLOR_CONST.DEFAULT, |
|
|
|
shape: RoundedRectangleBorder( |
|
|
|
borderRadius: new BorderRadius.circular(7.0), |
|
|
|
), |
|
|
|
child: Row( |
|
|
|
children: <Widget>[ |
|
|
|
Icon(Icons.image), |
|
|
|
Text( |
|
|
|
button_add_media, |
|
|
|
style: TextStyle( |
|
|
|
fontWeight: FontWeight.bold, color: COLOR_CONST.WHITE), |
|
|
|
) |
|
|
|
], |
|
|
|
), |
|
|
|
), |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
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(); |
|
|
|
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), width: 100, height: 100); |
|
|
|
} 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; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
Text _getRetrieveErrorWidget() { |
|
|
|
if (_retrieveDataError != null) { |
|
|
|
final Text result = Text(_retrieveDataError); |
|
|
|
_retrieveDataError = null; |
|
|
|
return result; |
|
|
|
} |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
Future<void> _displayPickImageDialog( |
|
|
|
BuildContext context, OnPickImageCallback onPick) async { |
|
|
|
onPick(null, null, null); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
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 Container( |
|
|
|
width: 100, |
|
|
|
height: 100, |
|
|
|
child: AspectRatio( |
|
|
|
aspectRatio: controller.value?.aspectRatio, |
|
|
|
child: VideoPlayer(controller), |
|
|
|
), |
|
|
|
); |
|
|
|
} else { |
|
|
|
return Container(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |