video_widget.dart 5.7 KB

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