video_widget.dart 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. import 'dart:io' as io;
  2. import 'package:chewie/chewie.dart';
  3. import 'package:flutter/cupertino.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:logging/logging.dart';
  6. import 'package:photos/core/constants.dart';
  7. import 'package:photos/models/file.dart';
  8. import 'package:photos/ui/viewer/file/thumbnail_widget.dart';
  9. import 'package:photos/ui/viewer/file/video_controls.dart';
  10. import 'package:photos/utils/file_util.dart';
  11. import 'package:photos/utils/toast_util.dart';
  12. import 'package:video_player/video_player.dart';
  13. import 'package:visibility_detector/visibility_detector.dart';
  14. class VideoWidget extends StatefulWidget {
  15. final File file;
  16. final bool autoPlay;
  17. final String tagPrefix;
  18. final Function(bool) playbackCallback;
  19. const VideoWidget(
  20. this.file, {
  21. this.autoPlay = false,
  22. this.tagPrefix,
  23. this.playbackCallback,
  24. Key key,
  25. }) : super(key: key);
  26. @override
  27. State<VideoWidget> createState() => _VideoWidgetState();
  28. }
  29. class _VideoWidgetState extends State<VideoWidget> {
  30. final _logger = Logger("VideoWidget");
  31. VideoPlayerController _videoPlayerController;
  32. ChewieController _chewieController;
  33. double _progress;
  34. bool _isPlaying;
  35. @override
  36. void initState() {
  37. super.initState();
  38. if (widget.file.isRemoteFile()) {
  39. _loadNetworkVideo();
  40. } else if (widget.file.isSharedMediaToAppSandbox()) {
  41. final localFile = io.File(getSharedMediaFilePath(widget.file));
  42. if (localFile.existsSync()) {
  43. _logger.fine("loading from app cache");
  44. _setVideoPlayerController(file: localFile);
  45. } else if (widget.file.uploadedFileID != null) {
  46. _loadNetworkVideo();
  47. }
  48. } else {
  49. widget.file.getAsset().then((asset) async {
  50. if (asset == null || !(await asset.exists)) {
  51. if (widget.file.uploadedFileID != null) {
  52. _loadNetworkVideo();
  53. }
  54. } else {
  55. asset.getMediaUrl().then((url) {
  56. _setVideoPlayerController(url: url);
  57. });
  58. }
  59. });
  60. }
  61. }
  62. void _loadNetworkVideo() {
  63. getFileFromServer(
  64. widget.file,
  65. progressCallback: (count, total) {
  66. if (mounted) {
  67. setState(() {
  68. _progress = count / total;
  69. if (_progress == 1) {
  70. showShortToast(context, "Decrypting video...");
  71. }
  72. });
  73. }
  74. },
  75. ).then((file) {
  76. if (file != null) {
  77. _setVideoPlayerController(file: file);
  78. }
  79. });
  80. }
  81. @override
  82. void dispose() {
  83. if (_videoPlayerController != null) {
  84. _videoPlayerController.dispose();
  85. }
  86. if (_chewieController != null) {
  87. _chewieController.dispose();
  88. }
  89. super.dispose();
  90. }
  91. VideoPlayerController _setVideoPlayerController({String url, io.File file}) {
  92. VideoPlayerController videoPlayerController;
  93. if (url != null) {
  94. videoPlayerController = VideoPlayerController.network(url);
  95. } else {
  96. videoPlayerController = VideoPlayerController.file(file);
  97. }
  98. return _videoPlayerController = videoPlayerController
  99. ..initialize().whenComplete(() {
  100. if (mounted) {
  101. setState(() {});
  102. }
  103. });
  104. }
  105. @override
  106. Widget build(BuildContext context) {
  107. final content = _videoPlayerController != null &&
  108. _videoPlayerController.value.isInitialized
  109. ? _getVideoPlayer()
  110. : _getLoadingWidget();
  111. final contentWithDetector = GestureDetector(
  112. child: content,
  113. onVerticalDragUpdate: (d) => {
  114. if (d.delta.dy > kDragSensitivity) {Navigator.of(context).pop()}
  115. },
  116. );
  117. return VisibilityDetector(
  118. key: Key(widget.file.tag()),
  119. onVisibilityChanged: (info) {
  120. if (info.visibleFraction < 1) {
  121. if (mounted && _chewieController != null) {
  122. _chewieController.pause();
  123. }
  124. }
  125. },
  126. child: Hero(
  127. tag: widget.tagPrefix + widget.file.tag(),
  128. child: contentWithDetector,
  129. ),
  130. );
  131. }
  132. Widget _getLoadingWidget() {
  133. return Stack(
  134. children: [
  135. _getThumbnail(),
  136. Container(
  137. color: Colors.black12,
  138. constraints: const BoxConstraints.expand(),
  139. ),
  140. Center(
  141. child: SizedBox.fromSize(
  142. size: const Size.square(30),
  143. child: _progress == null || _progress == 1
  144. ? const CupertinoActivityIndicator(
  145. color: Colors.white,
  146. )
  147. : CircularProgressIndicator(
  148. backgroundColor: Colors.black,
  149. value: _progress,
  150. valueColor: const AlwaysStoppedAnimation<Color>(
  151. Color.fromRGBO(45, 194, 98, 1.0),
  152. ),
  153. ),
  154. ),
  155. ),
  156. ],
  157. );
  158. }
  159. Widget _getThumbnail() {
  160. return Container(
  161. color: Colors.black,
  162. constraints: const BoxConstraints.expand(),
  163. child: ThumbnailWidget(
  164. widget.file,
  165. fit: BoxFit.contain,
  166. ),
  167. );
  168. }
  169. Widget _getVideoPlayer() {
  170. _videoPlayerController.addListener(() {
  171. if (_isPlaying != _videoPlayerController.value.isPlaying) {
  172. _isPlaying = _videoPlayerController.value.isPlaying;
  173. if (widget.playbackCallback != null) {
  174. widget.playbackCallback(_isPlaying);
  175. }
  176. }
  177. });
  178. _chewieController = ChewieController(
  179. videoPlayerController: _videoPlayerController,
  180. aspectRatio: _videoPlayerController.value.aspectRatio,
  181. autoPlay: widget.autoPlay,
  182. autoInitialize: true,
  183. looping: true,
  184. allowMuting: true,
  185. allowFullScreen: false,
  186. customControls: const VideoControls(),
  187. );
  188. return Container(
  189. color: Colors.black,
  190. child: Chewie(controller: _chewieController),
  191. );
  192. }
  193. }