video_controls.dart 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. import 'dart:async';
  2. import 'package:chewie/chewie.dart';
  3. import 'package:chewie/src/material_progress_bar.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:photos/utils/date_time_util.dart';
  6. import 'package:video_player/video_player.dart';
  7. class VideoControls extends StatefulWidget {
  8. const VideoControls({Key key}) : super(key: key);
  9. @override
  10. State<StatefulWidget> createState() {
  11. return _VideoControlsState();
  12. }
  13. }
  14. class _VideoControlsState extends State<VideoControls> {
  15. VideoPlayerValue _latestValue;
  16. bool _hideStuff = true;
  17. Timer _hideTimer;
  18. Timer _initTimer;
  19. Timer _showAfterExpandCollapseTimer;
  20. bool _dragging = false;
  21. bool _displayTapped = false;
  22. final barHeight = 48.0;
  23. final marginSize = 5.0;
  24. VideoPlayerController controller;
  25. ChewieController chewieController;
  26. @override
  27. Widget build(BuildContext context) {
  28. if (_latestValue.hasError) {
  29. return chewieController.errorBuilder != null
  30. ? chewieController.errorBuilder(
  31. context,
  32. chewieController.videoPlayerController.value.errorDescription,
  33. )
  34. : Center(
  35. child: Icon(
  36. Icons.error,
  37. color: Colors.white,
  38. size: 42,
  39. ),
  40. );
  41. }
  42. return MouseRegion(
  43. onHover: (_) {
  44. _cancelAndRestartTimer();
  45. },
  46. child: GestureDetector(
  47. onTap: () => _cancelAndRestartTimer(),
  48. child: AbsorbPointer(
  49. absorbing: _hideStuff,
  50. child: Column(
  51. children: <Widget>[
  52. _latestValue != null &&
  53. !_latestValue.isPlaying &&
  54. _latestValue.duration == null ||
  55. _latestValue.isBuffering
  56. ? const Expanded(
  57. child: const Center(
  58. child: const CircularProgressIndicator(),
  59. ),
  60. )
  61. : _buildHitArea(),
  62. _buildBottomBar(context),
  63. ],
  64. ),
  65. ),
  66. ),
  67. );
  68. }
  69. @override
  70. void dispose() {
  71. _dispose();
  72. super.dispose();
  73. }
  74. void _dispose() {
  75. controller.removeListener(_updateState);
  76. _hideTimer?.cancel();
  77. _initTimer?.cancel();
  78. _showAfterExpandCollapseTimer?.cancel();
  79. }
  80. @override
  81. void didChangeDependencies() {
  82. final _oldController = chewieController;
  83. chewieController = ChewieController.of(context);
  84. controller = chewieController.videoPlayerController;
  85. if (_oldController != chewieController) {
  86. _dispose();
  87. _initialize();
  88. }
  89. super.didChangeDependencies();
  90. }
  91. AnimatedOpacity _buildBottomBar(
  92. BuildContext context,
  93. ) {
  94. final iconColor = Theme.of(context).textTheme.button.color;
  95. return AnimatedOpacity(
  96. opacity: _hideStuff ? 0.0 : 1.0,
  97. duration: Duration(milliseconds: 300),
  98. child: Container(
  99. height: barHeight,
  100. color: Colors.transparent,
  101. child: Row(
  102. children: <Widget>[
  103. _buildCurrentPosition(iconColor),
  104. chewieController.isLive ? const SizedBox() : _buildProgressBar(),
  105. _buildTotalDuration(iconColor),
  106. ],
  107. ),
  108. ),
  109. );
  110. }
  111. Expanded _buildHitArea() {
  112. return Expanded(
  113. child: GestureDetector(
  114. onTap: () {
  115. if (_latestValue != null) {
  116. if (_displayTapped) {
  117. setState(() {
  118. _hideStuff = true;
  119. });
  120. } else
  121. _cancelAndRestartTimer();
  122. } else {
  123. _playPause();
  124. setState(() {
  125. _hideStuff = true;
  126. });
  127. }
  128. },
  129. child: Container(
  130. color: Colors.transparent,
  131. child: Center(
  132. child: AnimatedOpacity(
  133. opacity:
  134. _latestValue != null && !_hideStuff && !_dragging ? 1.0 : 0.0,
  135. duration: Duration(milliseconds: 300),
  136. child: GestureDetector(
  137. onTap: _playPause,
  138. child: Padding(
  139. padding: EdgeInsets.all(12.0),
  140. child: Icon(
  141. _latestValue.isPlaying ? Icons.pause : Icons.play_arrow,
  142. size: 64.0),
  143. ),
  144. ),
  145. ),
  146. ),
  147. ),
  148. ),
  149. );
  150. }
  151. Widget _buildCurrentPosition(Color iconColor) {
  152. final position = _latestValue != null && _latestValue.position != null
  153. ? _latestValue.position
  154. : Duration.zero;
  155. return Container(
  156. margin: EdgeInsets.only(left: 20.0, right: 16.0),
  157. child: Text(
  158. '${formatDuration(position)}',
  159. style: TextStyle(
  160. fontSize: 12.0,
  161. ),
  162. ),
  163. );
  164. }
  165. Widget _buildTotalDuration(Color iconColor) {
  166. final duration = _latestValue != null && _latestValue.duration != null
  167. ? _latestValue.duration
  168. : Duration.zero;
  169. return Padding(
  170. padding: EdgeInsets.only(right: 20.0),
  171. child: Text(
  172. '${formatDuration(duration)}',
  173. style: TextStyle(
  174. fontSize: 12.0,
  175. ),
  176. ),
  177. );
  178. }
  179. void _cancelAndRestartTimer() {
  180. _hideTimer?.cancel();
  181. _startHideTimer();
  182. setState(() {
  183. _hideStuff = false;
  184. _displayTapped = true;
  185. });
  186. }
  187. Future<Null> _initialize() async {
  188. controller.addListener(_updateState);
  189. _updateState();
  190. if ((controller.value != null && controller.value.isPlaying) ||
  191. chewieController.autoPlay) {
  192. _startHideTimer();
  193. }
  194. if (chewieController.showControlsOnInitialize) {
  195. _initTimer = Timer(Duration(milliseconds: 200), () {
  196. setState(() {
  197. _hideStuff = false;
  198. });
  199. });
  200. }
  201. }
  202. void _playPause() {
  203. bool isFinished = _latestValue.position >= _latestValue.duration;
  204. setState(() {
  205. if (controller.value.isPlaying) {
  206. _hideStuff = false;
  207. _hideTimer?.cancel();
  208. controller.pause();
  209. } else {
  210. _cancelAndRestartTimer();
  211. if (!controller.value.isInitialized) {
  212. controller.initialize().then((_) {
  213. controller.play();
  214. });
  215. } else {
  216. if (isFinished) {
  217. controller.seekTo(Duration(seconds: 0));
  218. }
  219. controller.play();
  220. }
  221. }
  222. });
  223. }
  224. void _startHideTimer() {
  225. _hideTimer = Timer(const Duration(seconds: 2), () {
  226. setState(() {
  227. _hideStuff = true;
  228. });
  229. });
  230. }
  231. void _updateState() {
  232. setState(() {
  233. _latestValue = controller.value;
  234. });
  235. }
  236. Widget _buildProgressBar() {
  237. return Expanded(
  238. child: Padding(
  239. padding: EdgeInsets.only(right: 16.0),
  240. child: MaterialVideoProgressBar(
  241. controller,
  242. onDragStart: () {
  243. setState(() {
  244. _dragging = true;
  245. });
  246. _hideTimer?.cancel();
  247. },
  248. onDragEnd: () {
  249. setState(() {
  250. _dragging = false;
  251. });
  252. _startHideTimer();
  253. },
  254. colors: chewieController.materialProgressColors ??
  255. ChewieProgressColors(
  256. playedColor: Theme.of(context).accentColor,
  257. handleColor: Colors.white,
  258. bufferedColor: Colors.white,
  259. backgroundColor: Theme.of(context).disabledColor),
  260. ),
  261. ),
  262. );
  263. }
  264. }