cupertino_controls.dart 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. import 'dart:async';
  2. import 'dart:math' as math;
  3. import 'dart:ui';
  4. import 'package:chewie_example/utils.dart';
  5. import 'package:flutter/material.dart';
  6. import 'package:meta/meta.dart';
  7. import 'package:open_iconic_flutter/open_iconic_flutter.dart';
  8. import 'package:video_player/video_player.dart';
  9. class CupertinoControls extends StatefulWidget {
  10. final Color backgroundColor;
  11. final Color iconColor;
  12. final VideoPlayerController controller;
  13. final Future<dynamic> Function() onExpandCollapse;
  14. final bool fullScreen;
  15. CupertinoControls({
  16. @required this.backgroundColor,
  17. @required this.iconColor,
  18. @required this.controller,
  19. @required this.onExpandCollapse,
  20. @required this.fullScreen,
  21. });
  22. @override
  23. State<StatefulWidget> createState() {
  24. return new _CupertinoControlsState();
  25. }
  26. }
  27. class _CupertinoControlsState extends State<CupertinoControls> {
  28. VideoPlayerValue _latestValue;
  29. double _latestVolume;
  30. bool _hideStuff = true;
  31. bool _disposed = false;
  32. Timer _hideTimer;
  33. final barHeight = 30.0;
  34. final marginSize = 5.0;
  35. @override
  36. Widget build(BuildContext context) {
  37. final backgroundColor = widget.backgroundColor;
  38. final iconColor = widget.iconColor;
  39. final controller = widget.controller;
  40. return new Column(
  41. children: <Widget>[
  42. _buildTopBar(backgroundColor, iconColor, controller),
  43. _buildHitArea(),
  44. _buildBottomBar(backgroundColor, iconColor, controller),
  45. ],
  46. );
  47. }
  48. @override
  49. void dispose() {
  50. widget.controller.removeListener(_updateState);
  51. _disposed = true;
  52. super.dispose();
  53. }
  54. @override
  55. void initState() {
  56. _initialize();
  57. super.initState();
  58. }
  59. AnimatedOpacity _buildBottomBar(Color backgroundColor, Color iconColor,
  60. VideoPlayerController controller) {
  61. return new AnimatedOpacity(
  62. opacity: _hideStuff ? 0.0 : 1.0,
  63. duration: new Duration(milliseconds: 300),
  64. child: new Container(
  65. color: Colors.transparent,
  66. alignment: Alignment.bottomCenter,
  67. margin: new EdgeInsets.all(marginSize),
  68. child: new ClipRect(
  69. child: new BackdropFilter(
  70. filter: new ImageFilter.blur(
  71. sigmaX: 10.0,
  72. sigmaY: 10.0,
  73. ),
  74. child: new Container(
  75. height: barHeight,
  76. decoration: new BoxDecoration(
  77. color: backgroundColor,
  78. borderRadius: new BorderRadius.all(
  79. new Radius.circular(10.0),
  80. ),
  81. ),
  82. child: new Row(
  83. children: <Widget>[
  84. _buildSkipBack(iconColor),
  85. _buildPlayPause(controller, iconColor),
  86. _buildSkipForward(iconColor),
  87. _buildPosition(iconColor),
  88. new _ProgressBar(controller),
  89. _buildRemaining(iconColor)
  90. ],
  91. ),
  92. ),
  93. ),
  94. ),
  95. ),
  96. );
  97. }
  98. GestureDetector _buildExpandButton(Color backgroundColor, Color iconColor) {
  99. return new GestureDetector(
  100. onTap: _onExpandCollapse,
  101. child: new AnimatedOpacity(
  102. opacity: _hideStuff ? 0.0 : 1.0,
  103. duration: new Duration(milliseconds: 300),
  104. child: new ClipRect(
  105. child: new BackdropFilter(
  106. filter: new ImageFilter.blur(sigmaX: 10.0),
  107. child: new Container(
  108. height: barHeight,
  109. padding: new EdgeInsets.only(
  110. left: 16.0,
  111. right: 16.0,
  112. ),
  113. decoration: new BoxDecoration(
  114. color: backgroundColor,
  115. borderRadius: new BorderRadius.all(
  116. new Radius.circular(10.0),
  117. ),
  118. ),
  119. child: new Center(
  120. child: new Icon(
  121. widget.fullScreen
  122. ? OpenIconicIcons.fullscreenExit
  123. : OpenIconicIcons.fullscreenEnter,
  124. color: iconColor,
  125. size: 12.0,
  126. ),
  127. ),
  128. ),
  129. ),
  130. ),
  131. ),
  132. );
  133. }
  134. Expanded _buildHitArea() {
  135. return new Expanded(
  136. child: new GestureDetector(
  137. onTap: _latestValue != null && _latestValue.isPlaying
  138. ? _cancelAndRestartTimer
  139. : () {
  140. _hideTimer?.cancel();
  141. setState(() {
  142. _hideStuff = false;
  143. });
  144. },
  145. child: new Container(
  146. color: Colors.transparent,
  147. ),
  148. ),
  149. );
  150. }
  151. GestureDetector _buildMuteButton(VideoPlayerController controller,
  152. Color backgroundColor, Color iconColor) {
  153. return new GestureDetector(
  154. onTap: () {
  155. _cancelAndRestartTimer();
  156. if (_latestValue.volume == 0) {
  157. controller.setVolume(_latestVolume ?? 0.5);
  158. } else {
  159. _latestVolume = controller.value.volume;
  160. controller.setVolume(0.0);
  161. }
  162. },
  163. child: new AnimatedOpacity(
  164. opacity: _hideStuff ? 0.0 : 1.0,
  165. duration: new Duration(milliseconds: 300),
  166. child: new ClipRect(
  167. child: new BackdropFilter(
  168. filter: new ImageFilter.blur(sigmaX: 10.0),
  169. child: new Container(
  170. decoration: new BoxDecoration(
  171. color: backgroundColor,
  172. borderRadius: new BorderRadius.all(
  173. new Radius.circular(10.0),
  174. ),
  175. ),
  176. child: new Container(
  177. height: barHeight,
  178. padding: new EdgeInsets.only(
  179. left: 16.0,
  180. right: 16.0,
  181. ),
  182. child: new Icon(
  183. (_latestValue != null && _latestValue.volume > 0)
  184. ? Icons.volume_up
  185. : Icons.volume_off,
  186. color: iconColor,
  187. size: 16.0,
  188. ),
  189. ),
  190. ),
  191. ),
  192. ),
  193. ),
  194. );
  195. }
  196. GestureDetector _buildPlayPause(
  197. VideoPlayerController controller, Color iconColor) {
  198. return new GestureDetector(
  199. onTap: _playPause,
  200. child: new Container(
  201. height: barHeight,
  202. color: Colors.transparent,
  203. padding: new EdgeInsets.only(
  204. left: 6.0,
  205. right: 6.0,
  206. ),
  207. child: new Icon(
  208. controller.value.isPlaying
  209. ? OpenIconicIcons.mediaPause
  210. : OpenIconicIcons.mediaPlay,
  211. color: iconColor,
  212. size: 16.0,
  213. ),
  214. ),
  215. );
  216. }
  217. Widget _buildPosition(Color iconColor) {
  218. final position =
  219. _latestValue != null ? _latestValue.position : new Duration(seconds: 0);
  220. return new Padding(
  221. padding: new EdgeInsets.only(right: 12.0),
  222. child: new Text(
  223. formatDuration(position),
  224. style: new TextStyle(
  225. color: iconColor,
  226. fontSize: 12.0,
  227. ),
  228. ),
  229. );
  230. }
  231. Widget _buildRemaining(Color iconColor) {
  232. final position = _latestValue != null && _latestValue.duration != null
  233. ? _latestValue.duration - _latestValue.position
  234. : new Duration(seconds: 0);
  235. return new Padding(
  236. padding: new EdgeInsets.only(right: 12.0),
  237. child: new Text(
  238. '-${formatDuration(position)}',
  239. style: new TextStyle(color: iconColor, fontSize: 12.0),
  240. ),
  241. );
  242. }
  243. GestureDetector _buildSkipBack(Color iconColor) {
  244. return new GestureDetector(
  245. onTap: _skipBack,
  246. child: new Container(
  247. height: barHeight,
  248. color: Colors.transparent,
  249. margin: new EdgeInsets.only(left: 10.0),
  250. padding: new EdgeInsets.only(
  251. left: 6.0,
  252. right: 6.0,
  253. ),
  254. child: new Transform(
  255. alignment: Alignment.center,
  256. transform: new Matrix4.skewY(0.0)
  257. ..rotateX(math.pi)
  258. ..rotateZ(math.pi),
  259. child: new Icon(
  260. OpenIconicIcons.reload,
  261. color: iconColor,
  262. size: 12.0,
  263. ),
  264. ),
  265. ),
  266. );
  267. }
  268. GestureDetector _buildSkipForward(Color iconColor) {
  269. return new GestureDetector(
  270. onTap: _skipForward,
  271. child: new Container(
  272. height: barHeight,
  273. color: Colors.transparent,
  274. padding: new EdgeInsets.only(
  275. left: 6.0,
  276. right: 8.0,
  277. ),
  278. margin: new EdgeInsets.only(
  279. right: 8.0,
  280. ),
  281. child: new Icon(
  282. OpenIconicIcons.reload,
  283. color: iconColor,
  284. size: 12.0,
  285. ),
  286. ),
  287. );
  288. }
  289. Widget _buildTopBar(Color backgroundColor, Color iconColor,
  290. VideoPlayerController controller) {
  291. return new Container(
  292. height: barHeight,
  293. margin: new EdgeInsets.only(
  294. top: marginSize,
  295. right: marginSize,
  296. left: marginSize,
  297. ),
  298. child: new Row(
  299. children: <Widget>[
  300. _buildExpandButton(backgroundColor, iconColor),
  301. new Expanded(child: new Container()),
  302. _buildMuteButton(controller, backgroundColor, iconColor),
  303. ],
  304. ),
  305. );
  306. }
  307. _cancelAndRestartTimer() {
  308. _hideTimer?.cancel();
  309. setState(() {
  310. _hideStuff = false;
  311. _startHideTimer();
  312. });
  313. }
  314. Future<Null> _initialize() async {
  315. widget.controller.addListener(_updateState);
  316. _updateState();
  317. _startHideTimer();
  318. new Timer(new Duration(milliseconds: 200), () {
  319. setState(() {
  320. _hideStuff = false;
  321. });
  322. });
  323. }
  324. void _onExpandCollapse() {
  325. setState(() {
  326. _hideStuff = true;
  327. widget.onExpandCollapse().then((_) {
  328. new Timer(new Duration(milliseconds: 300), () {
  329. if (!_disposed) {
  330. setState(() {
  331. _cancelAndRestartTimer();
  332. });
  333. }
  334. });
  335. });
  336. });
  337. }
  338. void _playPause() {
  339. setState(() {
  340. if (widget.controller.value.isPlaying) {
  341. _hideStuff = false;
  342. _hideTimer?.cancel();
  343. widget.controller.pause();
  344. } else {
  345. _cancelAndRestartTimer();
  346. widget.controller.play();
  347. }
  348. });
  349. }
  350. void _skipBack() {
  351. final beginning = new Duration(seconds: 0).inMicroseconds;
  352. final skip =
  353. (_latestValue.position - new Duration(seconds: 15)).inMicroseconds;
  354. widget.controller
  355. .seekTo(new Duration(milliseconds: math.max(skip, beginning)));
  356. }
  357. void _skipForward() {
  358. final end = _latestValue.duration.inMicroseconds;
  359. final skip =
  360. (_latestValue.position + new Duration(seconds: 15)).inMicroseconds;
  361. widget.controller.seekTo(new Duration(milliseconds: math.min(skip, end)));
  362. }
  363. void _startHideTimer() {
  364. _hideTimer = new Timer(const Duration(seconds: 3), () {
  365. if (!_disposed) {
  366. setState(() {
  367. _hideStuff = true;
  368. });
  369. }
  370. });
  371. }
  372. void _updateState() {
  373. setState(() {
  374. _latestValue = widget.controller.value;
  375. });
  376. }
  377. }
  378. class _ProgressBar extends StatelessWidget {
  379. final VideoPlayerController controller;
  380. _ProgressBar(this.controller);
  381. @override
  382. Widget build(BuildContext context) {
  383. return new Expanded(
  384. child: new Padding(
  385. padding: new EdgeInsets.only(right: 12.0),
  386. child: new ClipRRect(
  387. borderRadius: new BorderRadius.all(new Radius.circular(10.0)),
  388. child: new Container(
  389. height: 5.0,
  390. child: new VideoProgressBar(
  391. controller,
  392. colors: new VideoProgressColors(
  393. playedColor: new Color.fromARGB(
  394. 255,
  395. 255,
  396. 255,
  397. 255,
  398. ),
  399. handleColor: new Color.fromARGB(
  400. 255,
  401. 255,
  402. 255,
  403. 255,
  404. ),
  405. bufferedColor: new Color.fromARGB(
  406. 140,
  407. 255,
  408. 255,
  409. 255,
  410. ),
  411. ),
  412. ),
  413. ),
  414. ),
  415. ),
  416. );
  417. }
  418. }