material_controls.dart 8.0 KB

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