You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

361 lines
9.9KB

  1. import 'dart:io';
  2. import 'dart:async';
  3. import 'package:farm_tpf/utils/const_color.dart';
  4. import 'package:farm_tpf/utils/const_string.dart';
  5. import 'package:flutter/cupertino.dart';
  6. import 'package:flutter/material.dart';
  7. import 'package:image_picker/image_picker.dart';
  8. import 'package:video_player/video_player.dart';
  9. class WidgetMediaHelper extends StatefulWidget {
  10. @override
  11. _WidgetMediaHelperState createState() => _WidgetMediaHelperState();
  12. }
  13. class _WidgetMediaHelperState extends State<WidgetMediaHelper> {
  14. PickedFile _imageFile;
  15. dynamic _pickImageError;
  16. bool isVideo = false;
  17. VideoPlayerController _controller;
  18. VideoPlayerController _toBeDisposed;
  19. String _retrieveDataError;
  20. final ImagePicker _picker = ImagePicker();
  21. @override
  22. Widget build(BuildContext context) {
  23. return Container(
  24. child: Column(
  25. children: <Widget>[
  26. _actionButton(),
  27. 1 == 1
  28. ? FutureBuilder<void>(
  29. future: retrieveLostData(),
  30. builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
  31. switch (snapshot.connectionState) {
  32. case ConnectionState.none:
  33. case ConnectionState.waiting:
  34. return const Text(
  35. 'You have not yet picked an image.',
  36. textAlign: TextAlign.center,
  37. );
  38. case ConnectionState.done:
  39. return isVideo ? _previewVideo() : _previewImage();
  40. default:
  41. if (snapshot.hasError) {
  42. return Text(
  43. 'Pick image/video error: ${snapshot.error}}',
  44. textAlign: TextAlign.center,
  45. );
  46. } else {
  47. return const Text(
  48. 'You have not yet picked an image.',
  49. textAlign: TextAlign.center,
  50. );
  51. }
  52. }
  53. },
  54. )
  55. : (isVideo ? _previewVideo() : _previewImage()),
  56. ],
  57. ));
  58. }
  59. Widget viewResult() {
  60. FutureBuilder<void>(
  61. future: retrieveLostData(),
  62. builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
  63. switch (snapshot.connectionState) {
  64. case ConnectionState.none:
  65. case ConnectionState.waiting:
  66. return const Text(
  67. 'You have not yet picked an image.',
  68. textAlign: TextAlign.center,
  69. );
  70. case ConnectionState.done:
  71. return isVideo ? _previewVideo() : _previewImage();
  72. default:
  73. if (snapshot.hasError) {
  74. return Text(
  75. 'Pick image/video error: ${snapshot.error}}',
  76. textAlign: TextAlign.center,
  77. );
  78. } else {
  79. return const Text(
  80. 'You have not yet picked an image.',
  81. textAlign: TextAlign.center,
  82. );
  83. }
  84. }
  85. },
  86. );
  87. }
  88. Widget multipleChoice() {
  89. return CupertinoAlertDialog(
  90. title: Text(label_title_select_media),
  91. actions: <Widget>[
  92. CupertinoDialogAction(
  93. child: const Text(label_select_image_from_library),
  94. onPressed: () {
  95. Navigator.pop(context, 'Discard');
  96. isVideo = false;
  97. _onImageButtonPressed(ImageSource.gallery, context: context);
  98. }),
  99. CupertinoDialogAction(
  100. child: const Text(label_take_photo),
  101. onPressed: () {
  102. Navigator.pop(context, 'Discard');
  103. isVideo = false;
  104. _onImageButtonPressed(ImageSource.camera, context: context);
  105. }),
  106. CupertinoDialogAction(
  107. child: const Text(label_select_video_from_library),
  108. onPressed: () {
  109. Navigator.pop(context, 'Discard');
  110. isVideo = true;
  111. _onImageButtonPressed(ImageSource.gallery);
  112. }),
  113. CupertinoDialogAction(
  114. child: const Text(label_record_video),
  115. onPressed: () {
  116. Navigator.pop(context, 'Discard');
  117. isVideo = true;
  118. _onImageButtonPressed(ImageSource.camera);
  119. }),
  120. CupertinoDialogAction(
  121. child: const Text(label_cancel),
  122. textStyle: TextStyle(fontWeight: FontWeight.bold),
  123. isDefaultAction: true,
  124. onPressed: () {
  125. Navigator.pop(context, 'Cancel');
  126. }),
  127. ],
  128. );
  129. }
  130. Widget _actionButton() {
  131. return SizedBox(
  132. width: double.infinity,
  133. height: 55,
  134. child: FlatButton(
  135. onPressed: () {
  136. showDialog(
  137. context: context,
  138. barrierDismissible: true,
  139. builder: (context) => Opacity(
  140. child: multipleChoice(),
  141. opacity: 1,
  142. ));
  143. },
  144. color: COLOR_CONST.DEFAULT,
  145. shape: RoundedRectangleBorder(
  146. borderRadius: new BorderRadius.circular(7.0),
  147. ),
  148. child: Row(
  149. children: <Widget>[
  150. Icon(Icons.image),
  151. Text(
  152. button_add_media,
  153. style: TextStyle(
  154. fontWeight: FontWeight.bold, color: COLOR_CONST.WHITE),
  155. )
  156. ],
  157. ),
  158. ),
  159. );
  160. }
  161. Future<void> _playVideo(PickedFile file) async {
  162. if (file != null && mounted) {
  163. await _disposeVideoController();
  164. _controller = VideoPlayerController.file(File(file.path));
  165. await _controller.setVolume(1.0);
  166. await _controller.initialize();
  167. await _controller.setLooping(true);
  168. await _controller.play();
  169. setState(() {});
  170. }
  171. }
  172. void _onImageButtonPressed(ImageSource source, {BuildContext context}) async {
  173. if (_controller != null) {
  174. await _controller.setVolume(0.0);
  175. }
  176. if (isVideo) {
  177. final PickedFile file = await _picker.getVideo(
  178. source: source, maxDuration: const Duration(seconds: 10));
  179. await _playVideo(file);
  180. } else {
  181. await _displayPickImageDialog(context,
  182. (double maxWidth, double maxHeight, int quality) async {
  183. try {
  184. final pickedFile = await _picker.getImage(
  185. source: source,
  186. maxWidth: maxWidth,
  187. maxHeight: maxHeight,
  188. imageQuality: quality,
  189. );
  190. setState(() {
  191. _imageFile = pickedFile;
  192. });
  193. } catch (e) {
  194. setState(() {
  195. _pickImageError = e;
  196. });
  197. }
  198. });
  199. }
  200. }
  201. @override
  202. void deactivate() {
  203. if (_controller != null) {
  204. _controller.setVolume(0.0);
  205. _controller.pause();
  206. }
  207. super.deactivate();
  208. }
  209. @override
  210. void dispose() {
  211. _disposeVideoController();
  212. super.dispose();
  213. }
  214. Future<void> _disposeVideoController() async {
  215. if (_toBeDisposed != null) {
  216. await _toBeDisposed.dispose();
  217. }
  218. _toBeDisposed = _controller;
  219. _controller = null;
  220. }
  221. Widget _previewVideo() {
  222. final Text retrieveError = _getRetrieveErrorWidget();
  223. if (retrieveError != null) {
  224. return retrieveError;
  225. }
  226. if (_controller == null) {
  227. return const Text(
  228. 'You have not yet picked a video',
  229. textAlign: TextAlign.center,
  230. );
  231. }
  232. return Padding(
  233. padding: const EdgeInsets.all(10.0),
  234. child: AspectRatioVideo(_controller),
  235. );
  236. }
  237. Widget _previewImage() {
  238. final Text retrieveError = _getRetrieveErrorWidget();
  239. if (retrieveError != null) {
  240. return retrieveError;
  241. }
  242. if (_imageFile != null) {
  243. return Image.file(File(_imageFile.path), width: 100, height: 100);
  244. } else if (_pickImageError != null) {
  245. return Text(
  246. 'Pick image error: $_pickImageError',
  247. textAlign: TextAlign.center,
  248. );
  249. } else {
  250. return const Text(
  251. 'You have not yet picked an image.',
  252. textAlign: TextAlign.center,
  253. );
  254. }
  255. }
  256. Future<void> retrieveLostData() async {
  257. final LostData response = await _picker.getLostData();
  258. if (response.isEmpty) {
  259. return;
  260. }
  261. if (response.file != null) {
  262. if (response.type == RetrieveType.video) {
  263. isVideo = true;
  264. await _playVideo(response.file);
  265. } else {
  266. isVideo = false;
  267. setState(() {
  268. _imageFile = response.file;
  269. });
  270. }
  271. } else {
  272. _retrieveDataError = response.exception.code;
  273. }
  274. }
  275. Text _getRetrieveErrorWidget() {
  276. if (_retrieveDataError != null) {
  277. final Text result = Text(_retrieveDataError);
  278. _retrieveDataError = null;
  279. return result;
  280. }
  281. return null;
  282. }
  283. Future<void> _displayPickImageDialog(
  284. BuildContext context, OnPickImageCallback onPick) async {
  285. onPick(null, null, null);
  286. }
  287. }
  288. typedef void OnPickImageCallback(
  289. double maxWidth, double maxHeight, int quality);
  290. class AspectRatioVideo extends StatefulWidget {
  291. AspectRatioVideo(this.controller);
  292. final VideoPlayerController controller;
  293. @override
  294. AspectRatioVideoState createState() => AspectRatioVideoState();
  295. }
  296. class AspectRatioVideoState extends State<AspectRatioVideo> {
  297. VideoPlayerController get controller => widget.controller;
  298. bool initialized = false;
  299. void _onVideoControllerUpdate() {
  300. if (!mounted) {
  301. return;
  302. }
  303. if (initialized != controller.value.initialized) {
  304. initialized = controller.value.initialized;
  305. setState(() {});
  306. }
  307. }
  308. @override
  309. void initState() {
  310. super.initState();
  311. controller.addListener(_onVideoControllerUpdate);
  312. }
  313. @override
  314. void dispose() {
  315. controller.removeListener(_onVideoControllerUpdate);
  316. super.dispose();
  317. }
  318. @override
  319. Widget build(BuildContext context) {
  320. if (initialized) {
  321. return Container(
  322. width: 100,
  323. height: 100,
  324. child: AspectRatio(
  325. aspectRatio: controller.value?.aspectRatio,
  326. child: VideoPlayer(controller),
  327. ),
  328. );
  329. } else {
  330. return Container();
  331. }
  332. }
  333. }