main.dart 12 KB

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