material_controls.dart 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. import 'dart:async';
  2. import 'package:chewie/src/chewie_progress_colors.dart';
  3. import 'package:chewie/src/material_progress_bar.dart';
  4. import 'package:chewie/src/utils.dart';
  5. import 'package:flutter/foundation.dart';
  6. import 'package:flutter/material.dart';
  7. import 'package:video_player/video_player.dart';
  8. class MaterialControls extends StatefulWidget {
  9. final VideoPlayerController controller;
  10. final bool fullScreen;
  11. final Future<dynamic> Function() onExpandCollapse;
  12. final ChewieProgressColors progressColors;
  13. final bool autoPlay;
  14. MaterialControls({
  15. @required this.controller,
  16. @required this.fullScreen,
  17. @required this.onExpandCollapse,
  18. @required this.progressColors,
  19. @required this.autoPlay,
  20. });
  21. @override
  22. State<StatefulWidget> createState() {
  23. return new _MaterialControlsState();
  24. }
  25. }
  26. class _MaterialControlsState extends State<MaterialControls> {
  27. VideoPlayerValue _latestValue;
  28. double _latestVolume;
  29. bool _hideStuff = true;
  30. Timer _hideTimer;
  31. Timer _showTimer;
  32. Timer _showAfterExpandCollapseTimer;
  33. bool _dragging = false;
  34. final barHeight = 48.0;
  35. final marginSize = 5.0;
  36. @override
  37. Widget build(BuildContext context) {
  38. return new Column(
  39. children: <Widget>[
  40. _buildHitArea(),
  41. _buildBottomBar(context, widget.controller),
  42. ],
  43. );
  44. }
  45. @override
  46. void dispose() {
  47. _dispose();
  48. super.dispose();
  49. }
  50. void _dispose() {
  51. widget.controller.removeListener(_updateState);
  52. _hideTimer?.cancel();
  53. _showTimer?.cancel();
  54. _showAfterExpandCollapseTimer?.cancel();
  55. }
  56. @override
  57. void initState() {
  58. _initialize();
  59. super.initState();
  60. }
  61. @override
  62. void didUpdateWidget(MaterialControls oldWidget) {
  63. super.didUpdateWidget(oldWidget);
  64. if (widget.controller.dataSource != oldWidget.controller.dataSource) {
  65. _dispose();
  66. _initialize();
  67. }
  68. }
  69. AnimatedOpacity _buildBottomBar(
  70. BuildContext context,
  71. VideoPlayerController controller,
  72. ) {
  73. final iconColor = Theme.of(context).textTheme.button.color;
  74. return new AnimatedOpacity(
  75. opacity: _hideStuff ? 0.0 : 1.0,
  76. duration: new Duration(milliseconds: 300),
  77. child: new Container(
  78. height: barHeight,
  79. color: Theme.of(context).dialogBackgroundColor,
  80. child: new Row(
  81. children: <Widget>[
  82. _buildPlayPause(controller),
  83. _buildPosition(iconColor),
  84. _buildProgressBar(),
  85. _buildMuteButton(controller),
  86. _buildExpandButton(),
  87. ],
  88. ),
  89. ),
  90. );
  91. }
  92. GestureDetector _buildExpandButton() {
  93. return new GestureDetector(
  94. onTap: _onExpandCollapse,
  95. child: new AnimatedOpacity(
  96. opacity: _hideStuff ? 0.0 : 1.0,
  97. duration: new Duration(milliseconds: 300),
  98. child: new Container(
  99. height: barHeight,
  100. margin: new EdgeInsets.only(right: 12.0),
  101. padding: new EdgeInsets.only(
  102. left: 8.0,
  103. right: 8.0,
  104. ),
  105. child: new Center(
  106. child: new Icon(
  107. widget.fullScreen ? Icons.fullscreen_exit : Icons.fullscreen,
  108. ),
  109. ),
  110. ),
  111. ),
  112. );
  113. }
  114. Expanded _buildHitArea() {
  115. return new Expanded(
  116. child: new GestureDetector(
  117. onTap: _latestValue != null && _latestValue.isPlaying
  118. ? _cancelAndRestartTimer
  119. : () {
  120. _playPause();
  121. setState(() {
  122. _hideStuff = true;
  123. });
  124. },
  125. child: new Container(
  126. color: Colors.transparent,
  127. child: new Center(
  128. child: new AnimatedOpacity(
  129. opacity:
  130. _latestValue != null && !_latestValue.isPlaying && !_dragging
  131. ? 1.0
  132. : 0.0,
  133. duration: new Duration(milliseconds: 300),
  134. child: new GestureDetector(
  135. child: new Container(
  136. decoration: new BoxDecoration(
  137. color: Theme.of(context).dialogBackgroundColor,
  138. borderRadius: new BorderRadius.circular(48.0),
  139. ),
  140. child: new Padding(
  141. padding: new EdgeInsets.all(12.0),
  142. child: new Icon(Icons.play_arrow, size: 32.0),
  143. ),
  144. ),
  145. ),
  146. ),
  147. ),
  148. ),
  149. ),
  150. );
  151. }
  152. GestureDetector _buildMuteButton(
  153. VideoPlayerController controller,
  154. ) {
  155. return new GestureDetector(
  156. onTap: () {
  157. _cancelAndRestartTimer();
  158. if (_latestValue.volume == 0) {
  159. controller.setVolume(_latestVolume ?? 0.5);
  160. } else {
  161. _latestVolume = controller.value.volume;
  162. controller.setVolume(0.0);
  163. }
  164. },
  165. child: new AnimatedOpacity(
  166. opacity: _hideStuff ? 0.0 : 1.0,
  167. duration: new Duration(milliseconds: 300),
  168. child: new ClipRect(
  169. child: new Container(
  170. child: new Container(
  171. height: barHeight,
  172. padding: new EdgeInsets.only(
  173. left: 8.0,
  174. right: 8.0,
  175. ),
  176. child: new Icon(
  177. (_latestValue != null && _latestValue.volume > 0)
  178. ? Icons.volume_up
  179. : Icons.volume_off,
  180. ),
  181. ),
  182. ),
  183. ),
  184. ),
  185. );
  186. }
  187. GestureDetector _buildPlayPause(VideoPlayerController controller) {
  188. return new GestureDetector(
  189. onTap: _playPause,
  190. child: new Container(
  191. height: barHeight,
  192. color: Colors.transparent,
  193. margin: new EdgeInsets.only(left: 8.0, right: 4.0),
  194. padding: new EdgeInsets.only(
  195. left: 12.0,
  196. right: 12.0,
  197. ),
  198. child: new Icon(
  199. controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
  200. ),
  201. ),
  202. );
  203. }
  204. Widget _buildPosition(Color iconColor) {
  205. final position = _latestValue != null && _latestValue.position != null
  206. ? _latestValue.position
  207. : Duration.zero;
  208. final duration = _latestValue != null && _latestValue.duration != null
  209. ? _latestValue.duration
  210. : Duration.zero;
  211. return new Padding(
  212. padding: new EdgeInsets.only(right: 24.0),
  213. child: new Text(
  214. '${formatDuration(position)} / ${formatDuration(duration)}',
  215. style: new TextStyle(
  216. fontSize: 14.0,
  217. ),
  218. ),
  219. );
  220. }
  221. void _cancelAndRestartTimer() {
  222. _hideTimer?.cancel();
  223. _startHideTimer();
  224. setState(() {
  225. _hideStuff = false;
  226. });
  227. }
  228. Future<Null> _initialize() async {
  229. widget.controller.addListener(_updateState);
  230. _updateState();
  231. if ((widget.controller.value != null &&
  232. widget.controller.value.isPlaying) ||
  233. widget.autoPlay) {
  234. _startHideTimer();
  235. }
  236. _showTimer = new Timer(new Duration(milliseconds: 200), () {
  237. setState(() {
  238. _hideStuff = false;
  239. });
  240. });
  241. }
  242. void _onExpandCollapse() {
  243. setState(() {
  244. _hideStuff = true;
  245. widget.onExpandCollapse().then((dynamic _) {
  246. _showAfterExpandCollapseTimer =
  247. new Timer(new Duration(milliseconds: 300), () {
  248. setState(() {
  249. _cancelAndRestartTimer();
  250. });
  251. });
  252. });
  253. });
  254. }
  255. void _playPause() {
  256. setState(() {
  257. if (widget.controller.value.isPlaying) {
  258. _hideStuff = false;
  259. _hideTimer?.cancel();
  260. widget.controller.pause();
  261. } else {
  262. _cancelAndRestartTimer();
  263. if (!widget.controller.value.initialized) {
  264. widget.controller.initialize().then((_) {
  265. widget.controller.play();
  266. });
  267. } else {
  268. widget.controller.play();
  269. }
  270. }
  271. });
  272. }
  273. void _startHideTimer() {
  274. _hideTimer = new Timer(const Duration(seconds: 3), () {
  275. setState(() {
  276. _hideStuff = true;
  277. });
  278. });
  279. }
  280. void _updateState() {
  281. setState(() {
  282. _latestValue = widget.controller.value;
  283. });
  284. }
  285. Widget _buildProgressBar() {
  286. return new Expanded(
  287. child: new Padding(
  288. padding: new EdgeInsets.only(right: 20.0),
  289. child: new MaterialVideoProgressBar(
  290. widget.controller,
  291. onDragStart: () {
  292. setState(() {
  293. _dragging = true;
  294. });
  295. _hideTimer?.cancel();
  296. },
  297. onDragEnd: () {
  298. setState(() {
  299. _dragging = false;
  300. });
  301. _startHideTimer();
  302. },
  303. colors: widget.progressColors ??
  304. new ChewieProgressColors(
  305. playedColor: Theme.of(context).accentColor,
  306. handleColor: Theme.of(context).accentColor,
  307. bufferedColor: Theme.of(context).backgroundColor,
  308. backgroundColor: Theme.of(context).disabledColor),
  309. ),
  310. ),
  311. );
  312. }
  313. }