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.

447 lines
12KB

  1. import 'dart:io';
  2. import 'dart:async';
  3. import 'package:farm_tpf/presentation/custom_widgets/hoz_list_view.dart';
  4. import 'package:farm_tpf/presentation/custom_widgets/shimmer_image.dart';
  5. import 'package:farm_tpf/utils/const_color.dart';
  6. import 'package:farm_tpf/utils/const_string.dart';
  7. import 'package:flutter/cupertino.dart';
  8. import 'package:flutter/material.dart';
  9. import 'package:image_picker/image_picker.dart';
  10. import 'package:video_player/video_player.dart';
  11. class WidgetMediaHelper extends StatefulWidget {
  12. @override
  13. _WidgetMediaHelperState createState() => _WidgetMediaHelperState();
  14. }
  15. class _WidgetMediaHelperState extends State<WidgetMediaHelper> {
  16. PickedFile _imageFile;
  17. dynamic _pickImageError;
  18. bool isVideo = false;
  19. VideoPlayerController _controller;
  20. VideoPlayerController _toBeDisposed;
  21. String _retrieveDataError;
  22. final ImagePicker _picker = ImagePicker();
  23. List<ItemMediaVM> items = [
  24. ItemMediaVM('http://lorempixel.com/640/480', true),
  25. ItemMediaVM(
  26. 'https://s3.amazonaws.com/uifaces/faces/twitter/rickdt/128.jpg', true),
  27. ItemMediaVM('http://lorempixel.com/640/480/food', true),
  28. ItemMediaVM('http://lorempixel.com/640/480/animals', true),
  29. ItemMediaVM('http://lorempixel.com/640/480/fashion', true),
  30. ];
  31. @override
  32. Widget build(BuildContext context) {
  33. return Container(
  34. padding: EdgeInsets.all(8),
  35. child: Column(
  36. children: <Widget>[
  37. _actionButton(),
  38. SizedBox(
  39. height: 4.0,
  40. ),
  41. _buildListPoster(),
  42. 1 == 1
  43. ? FutureBuilder<void>(
  44. future: retrieveLostData(),
  45. builder:
  46. (BuildContext context, AsyncSnapshot<void> snapshot) {
  47. switch (snapshot.connectionState) {
  48. case ConnectionState.none:
  49. case ConnectionState.waiting:
  50. return const Text(
  51. 'You have not yet picked an image.',
  52. textAlign: TextAlign.center,
  53. );
  54. case ConnectionState.done:
  55. return isVideo ? _previewVideo() : _previewImage();
  56. default:
  57. if (snapshot.hasError) {
  58. return Text(
  59. 'Pick image/video error: ${snapshot.error}}',
  60. textAlign: TextAlign.center,
  61. );
  62. } else {
  63. return const Text(
  64. 'You have not yet picked an image.',
  65. textAlign: TextAlign.center,
  66. );
  67. }
  68. }
  69. },
  70. )
  71. : (isVideo ? _previewVideo() : _previewImage()),
  72. ],
  73. ));
  74. }
  75. _buildListPoster() {
  76. return WrapContentHozListView(
  77. itemBuilder: (context, index) {
  78. var item = items[index];
  79. return _WidgetItemMedia(item);
  80. },
  81. separatorBuilder: (context, index) {
  82. return SizedBox(width: 14);
  83. },
  84. list: items,
  85. );
  86. }
  87. Widget viewResult() {
  88. FutureBuilder<void>(
  89. future: retrieveLostData(),
  90. builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
  91. switch (snapshot.connectionState) {
  92. case ConnectionState.none:
  93. case ConnectionState.waiting:
  94. return const Text(
  95. 'You have not yet picked an image.',
  96. textAlign: TextAlign.center,
  97. );
  98. case ConnectionState.done:
  99. return isVideo ? _previewVideo() : _previewImage();
  100. default:
  101. if (snapshot.hasError) {
  102. return Text(
  103. 'Pick image/video error: ${snapshot.error}}',
  104. textAlign: TextAlign.center,
  105. );
  106. } else {
  107. return const Text(
  108. 'You have not yet picked an image.',
  109. textAlign: TextAlign.center,
  110. );
  111. }
  112. }
  113. },
  114. );
  115. }
  116. Widget multipleChoice() {
  117. return CupertinoAlertDialog(
  118. title: Text(label_title_select_media),
  119. actions: <Widget>[
  120. CupertinoDialogAction(
  121. child: const Text(label_select_image_from_library),
  122. onPressed: () {
  123. Navigator.pop(context, 'Discard');
  124. isVideo = false;
  125. _onImageButtonPressed(ImageSource.gallery, context: context);
  126. }),
  127. CupertinoDialogAction(
  128. child: const Text(label_take_photo),
  129. onPressed: () {
  130. Navigator.pop(context, 'Discard');
  131. isVideo = false;
  132. _onImageButtonPressed(ImageSource.camera, context: context);
  133. }),
  134. CupertinoDialogAction(
  135. child: const Text(label_select_video_from_library),
  136. onPressed: () {
  137. Navigator.pop(context, 'Discard');
  138. isVideo = true;
  139. _onImageButtonPressed(ImageSource.gallery);
  140. }),
  141. CupertinoDialogAction(
  142. child: const Text(label_record_video),
  143. onPressed: () {
  144. Navigator.pop(context, 'Discard');
  145. isVideo = true;
  146. _onImageButtonPressed(ImageSource.camera);
  147. }),
  148. CupertinoDialogAction(
  149. child: const Text(label_cancel),
  150. textStyle: TextStyle(fontWeight: FontWeight.bold),
  151. isDefaultAction: true,
  152. onPressed: () {
  153. Navigator.pop(context, 'Cancel');
  154. }),
  155. ],
  156. );
  157. }
  158. Widget _actionButton() {
  159. return SizedBox(
  160. width: double.infinity,
  161. height: 44,
  162. child: FlatButton(
  163. onPressed: () {
  164. showDialog(
  165. context: context,
  166. barrierDismissible: true,
  167. builder: (context) => Opacity(
  168. child: multipleChoice(),
  169. opacity: 1,
  170. ));
  171. },
  172. color: COLOR_CONST.DEFAULT,
  173. shape: RoundedRectangleBorder(
  174. borderRadius: new BorderRadius.circular(7.0),
  175. ),
  176. child: Center(
  177. child: Row(
  178. mainAxisAlignment: MainAxisAlignment.center,
  179. children: <Widget>[
  180. Icon(Icons.image, color: Colors.white),
  181. SizedBox(
  182. width: 8.0,
  183. ),
  184. Text(
  185. button_add_media,
  186. style: TextStyle(
  187. fontWeight: FontWeight.bold, color: COLOR_CONST.WHITE),
  188. )
  189. ],
  190. ),
  191. )),
  192. );
  193. }
  194. Future<void> _playVideo(PickedFile file) async {
  195. if (file != null && mounted) {
  196. await _disposeVideoController();
  197. _controller = VideoPlayerController.file(File(file.path));
  198. await _controller.setVolume(1.0);
  199. await _controller.initialize();
  200. await _controller.setLooping(true);
  201. await _controller.play();
  202. setState(() {});
  203. }
  204. }
  205. void _onImageButtonPressed(ImageSource source, {BuildContext context}) async {
  206. if (_controller != null) {
  207. await _controller.setVolume(0.0);
  208. }
  209. if (isVideo) {
  210. final PickedFile file = await _picker.getVideo(
  211. source: source, maxDuration: const Duration(seconds: 10));
  212. await _playVideo(file);
  213. } else {
  214. await _displayPickImageDialog(context,
  215. (double maxWidth, double maxHeight, int quality) async {
  216. try {
  217. final pickedFile = await _picker.getImage(
  218. source: source,
  219. maxWidth: maxWidth,
  220. maxHeight: maxHeight,
  221. imageQuality: quality,
  222. );
  223. setState(() {
  224. _imageFile = pickedFile;
  225. });
  226. } catch (e) {
  227. setState(() {
  228. _pickImageError = e;
  229. });
  230. }
  231. });
  232. }
  233. }
  234. @override
  235. void deactivate() {
  236. if (_controller != null) {
  237. _controller.setVolume(0.0);
  238. _controller.pause();
  239. }
  240. super.deactivate();
  241. }
  242. @override
  243. void dispose() {
  244. _disposeVideoController();
  245. super.dispose();
  246. }
  247. Future<void> _disposeVideoController() async {
  248. if (_toBeDisposed != null) {
  249. await _toBeDisposed.dispose();
  250. }
  251. _toBeDisposed = _controller;
  252. _controller = null;
  253. }
  254. Widget _previewVideo() {
  255. final Text retrieveError = _getRetrieveErrorWidget();
  256. if (retrieveError != null) {
  257. return retrieveError;
  258. }
  259. if (_controller == null) {
  260. return const Text(
  261. 'You have not yet picked a video',
  262. textAlign: TextAlign.center,
  263. );
  264. }
  265. return Padding(
  266. padding: const EdgeInsets.all(10.0),
  267. child: AspectRatioVideo(_controller),
  268. );
  269. }
  270. Widget _previewImage() {
  271. final Text retrieveError = _getRetrieveErrorWidget();
  272. if (retrieveError != null) {
  273. return retrieveError;
  274. }
  275. if (_imageFile != null) {
  276. return Image.file(File(_imageFile.path), width: 100, height: 100);
  277. } else if (_pickImageError != null) {
  278. return Text(
  279. 'Pick image error: $_pickImageError',
  280. textAlign: TextAlign.center,
  281. );
  282. } else {
  283. return const Text(
  284. 'You have not yet picked an image.',
  285. textAlign: TextAlign.center,
  286. );
  287. }
  288. }
  289. Future<void> retrieveLostData() async {
  290. final LostData response = await _picker.getLostData();
  291. if (response.isEmpty) {
  292. return;
  293. }
  294. if (response.file != null) {
  295. if (response.type == RetrieveType.video) {
  296. isVideo = true;
  297. await _playVideo(response.file);
  298. } else {
  299. isVideo = false;
  300. setState(() {
  301. _imageFile = response.file;
  302. });
  303. }
  304. } else {
  305. _retrieveDataError = response.exception.code;
  306. }
  307. }
  308. Text _getRetrieveErrorWidget() {
  309. if (_retrieveDataError != null) {
  310. final Text result = Text(_retrieveDataError);
  311. _retrieveDataError = null;
  312. return result;
  313. }
  314. return null;
  315. }
  316. Future<void> _displayPickImageDialog(
  317. BuildContext context, OnPickImageCallback onPick) async {
  318. onPick(null, null, null);
  319. }
  320. }
  321. typedef void OnPickImageCallback(
  322. double maxWidth, double maxHeight, int quality);
  323. class AspectRatioVideo extends StatefulWidget {
  324. AspectRatioVideo(this.controller);
  325. final VideoPlayerController controller;
  326. @override
  327. AspectRatioVideoState createState() => AspectRatioVideoState();
  328. }
  329. class AspectRatioVideoState extends State<AspectRatioVideo> {
  330. VideoPlayerController get controller => widget.controller;
  331. bool initialized = false;
  332. void _onVideoControllerUpdate() {
  333. if (!mounted) {
  334. return;
  335. }
  336. if (initialized != controller.value.initialized) {
  337. initialized = controller.value.initialized;
  338. setState(() {});
  339. }
  340. }
  341. @override
  342. void initState() {
  343. super.initState();
  344. controller.addListener(_onVideoControllerUpdate);
  345. }
  346. @override
  347. void dispose() {
  348. controller.removeListener(_onVideoControllerUpdate);
  349. super.dispose();
  350. }
  351. @override
  352. Widget build(BuildContext context) {
  353. if (initialized) {
  354. return Container(
  355. width: 100,
  356. height: 100,
  357. child: AspectRatio(
  358. aspectRatio: controller.value?.aspectRatio,
  359. child: VideoPlayer(controller),
  360. ),
  361. );
  362. } else {
  363. return Container();
  364. }
  365. }
  366. }
  367. class _WidgetItemMedia extends StatelessWidget {
  368. final ItemMediaVM item;
  369. _WidgetItemMedia(this.item);
  370. BuildContext _context;
  371. @override
  372. Widget build(BuildContext context) {
  373. _context = context;
  374. return GestureDetector(
  375. onTap: () {
  376. print("Show preview image or video");
  377. },
  378. child: Stack(
  379. alignment: Alignment.bottomCenter,
  380. overflow: Overflow.visible,
  381. children: <Widget>[
  382. Positioned(
  383. child: ClipRRect(
  384. borderRadius: BorderRadius.circular(8),
  385. child: ShimmerImage(
  386. item.photo,
  387. width: 93,
  388. height: 124,
  389. fit: BoxFit.cover,
  390. ),
  391. )),
  392. Positioned(
  393. top: -5,
  394. right: -5,
  395. child: IconButton(
  396. icon: Icon(
  397. Icons.cancel,
  398. color: Colors.redAccent,
  399. ),
  400. onPressed: () {
  401. print("On tap delete media");
  402. }),
  403. )
  404. ],
  405. ));
  406. }
  407. }
  408. class ItemMediaVM {
  409. String photo;
  410. bool isVideo;
  411. ItemMediaVM(this.photo, this.isVideo);
  412. }