video_widget.dart 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  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. setState(() {});
  74. });
  75. }
  76. }
  77. void _loadNetworkVideo() {
  78. getFileFromServer(
  79. widget.file,
  80. progressCallback: (count, total) {
  81. if (mounted) {
  82. setState(() {
  83. _progress = count / (widget.file.fileSize ?? total);
  84. if (_progress == 1) {
  85. showShortToast(context, "Decrypting video...");
  86. }
  87. });
  88. }
  89. },
  90. ).then((file) {
  91. if (file != null) {
  92. _setVideoPlayerController(file: file);
  93. }
  94. });
  95. }
  96. @override
  97. void dispose() {
  98. if (_videoPlayerController != null) {
  99. _videoPlayerController.dispose();
  100. }
  101. if (_chewieController != null) {
  102. _chewieController.dispose();
  103. }
  104. super.dispose();
  105. }
  106. VideoPlayerController _setVideoPlayerController({String url, io.File file}) {
  107. VideoPlayerController videoPlayerController;
  108. if (url != null) {
  109. videoPlayerController = VideoPlayerController.network(url);
  110. } else {
  111. videoPlayerController = VideoPlayerController.file(file);
  112. }
  113. return _videoPlayerController = videoPlayerController
  114. ..initialize().whenComplete(() {
  115. if (mounted) {
  116. setState(() {});
  117. }
  118. });
  119. }
  120. @override
  121. Widget build(BuildContext context) {
  122. final content = _videoPlayerController != null &&
  123. _videoPlayerController.value.isInitialized
  124. ? _getVideoPlayer()
  125. : _getLoadingWidget();
  126. final contentWithDetector = GestureDetector(
  127. child: content,
  128. onVerticalDragUpdate: (d) => {
  129. if (d.delta.dy > dragSensitivity) {Navigator.of(context).pop()}
  130. },
  131. );
  132. return VisibilityDetector(
  133. key: Key(widget.file.tag),
  134. onVisibilityChanged: (info) {
  135. if (info.visibleFraction < 1) {
  136. if (mounted && _chewieController != null) {
  137. _chewieController.pause();
  138. }
  139. }
  140. },
  141. child: Hero(
  142. tag: widget.tagPrefix + widget.file.tag,
  143. child: contentWithDetector,
  144. ),
  145. );
  146. }
  147. Widget _getLoadingWidget() {
  148. return Stack(
  149. children: [
  150. _getThumbnail(),
  151. Container(
  152. color: Colors.black12,
  153. constraints: const BoxConstraints.expand(),
  154. ),
  155. Center(
  156. child: SizedBox.fromSize(
  157. size: const Size.square(30),
  158. child: _progress == null || _progress == 1
  159. ? const CupertinoActivityIndicator(
  160. color: Colors.white,
  161. )
  162. : CircularProgressIndicator(
  163. backgroundColor: Colors.black,
  164. value: _progress,
  165. valueColor: const AlwaysStoppedAnimation<Color>(
  166. Color.fromRGBO(45, 194, 98, 1.0),
  167. ),
  168. ),
  169. ),
  170. ),
  171. ],
  172. );
  173. }
  174. Widget _getThumbnail() {
  175. return Container(
  176. color: Colors.black,
  177. constraints: const BoxConstraints.expand(),
  178. child: ThumbnailWidget(
  179. widget.file,
  180. fit: BoxFit.contain,
  181. ),
  182. );
  183. }
  184. Widget _getVideoPlayer() {
  185. _videoPlayerController.addListener(() {
  186. if (_isPlaying != _videoPlayerController.value.isPlaying) {
  187. _isPlaying = _videoPlayerController.value.isPlaying;
  188. if (widget.playbackCallback != null) {
  189. widget.playbackCallback(_isPlaying);
  190. }
  191. }
  192. });
  193. _chewieController = ChewieController(
  194. videoPlayerController: _videoPlayerController,
  195. aspectRatio: _videoPlayerController.value.aspectRatio,
  196. autoPlay: widget.autoPlay,
  197. autoInitialize: true,
  198. looping: true,
  199. allowMuting: true,
  200. allowFullScreen: false,
  201. customControls: const VideoControls(),
  202. );
  203. return Container(
  204. color: Colors.black,
  205. child: Chewie(controller: _chewieController),
  206. );
  207. }
  208. }