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
11KB

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