video_widget.dart 6.2 KB

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