video_widget.dart 6.0 KB

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