import 'dart:async'; import 'dart:ui'; import 'package:chewie_example/material_progress_bar.dart'; import 'package:chewie_example/utils.dart'; import 'package:flutter/material.dart'; import 'package:meta/meta.dart'; import 'package:video_player/video_player.dart'; class MaterialControls extends StatefulWidget { final VideoPlayerController controller; final bool fullScreen; final Future Function() onExpandCollapse; MaterialControls({ @required this.controller, @required this.fullScreen, @required this.onExpandCollapse, }); @override State createState() { return new _MaterialControlsState(); } } class _MaterialControlsState extends State { VideoPlayerValue _latestValue; double _latestVolume; bool _hideStuff = true; bool _disposed = false; Timer _hideTimer; bool _dragging = false; final barHeight = 48.0; final marginSize = 5.0; @override Widget build(BuildContext context) { final controller = widget.controller; return new Column( children: [ _buildHitArea(), _buildBottomBar(context, controller), ], ); } @override void dispose() { widget.controller.removeListener(_updateState); _disposed = true; super.dispose(); } @override void initState() { _initialize(); super.initState(); } AnimatedOpacity _buildBottomBar( BuildContext context, VideoPlayerController controller, ) { final iconColor = Theme.of(context).textTheme.button.color; return new AnimatedOpacity( opacity: _hideStuff ? 0.0 : 1.0, duration: new Duration(milliseconds: 300), child: new Container( height: barHeight, color: Theme.of(context).dialogBackgroundColor, child: new Row( children: [ _buildPlayPause(controller), _buildPosition(iconColor), _buildProgressBar(), _buildMuteButton(controller), _buildExpandButton(), ], ), ), ); } GestureDetector _buildExpandButton() { return new GestureDetector( onTap: _onExpandCollapse, child: new AnimatedOpacity( opacity: _hideStuff ? 0.0 : 1.0, duration: new Duration(milliseconds: 300), child: new Container( height: barHeight, margin: new EdgeInsets.only(right: 12.0), padding: new EdgeInsets.only( left: 8.0, right: 8.0, ), child: new Center( child: new Icon( widget.fullScreen ? Icons.fullscreen_exit : Icons.fullscreen, ), ), ), ), ); } Expanded _buildHitArea() { return new Expanded( child: new GestureDetector( onTap: _latestValue != null && _latestValue.isPlaying ? _cancelAndRestartTimer : () { widget.controller.play(); setState(() { _hideStuff = true; }); }, child: new Container( color: Colors.transparent, child: new Center( child: new AnimatedOpacity( opacity: _latestValue != null && !_latestValue.isPlaying && !_dragging ? 1.0 : 0.0, duration: new Duration(milliseconds: 300), child: new GestureDetector( child: new Container( decoration: new BoxDecoration( color: Theme.of(context).dialogBackgroundColor, borderRadius: new BorderRadius.circular(48.0), ), child: new Padding( padding: new EdgeInsets.all(12.0), child: new Icon(Icons.play_arrow, size: 32.0), ), ), ), ), ), ), ), ); } GestureDetector _buildMuteButton( VideoPlayerController controller, ) { return new GestureDetector( onTap: () { _cancelAndRestartTimer(); if (_latestValue.volume == 0) { controller.setVolume(_latestVolume ?? 0.5); } else { _latestVolume = controller.value.volume; controller.setVolume(0.0); } }, child: new AnimatedOpacity( opacity: _hideStuff ? 0.0 : 1.0, duration: new Duration(milliseconds: 300), child: new ClipRect( child: new Container( child: new Container( height: barHeight, padding: new EdgeInsets.only( left: 8.0, right: 8.0, ), child: new Icon( (_latestValue != null && _latestValue.volume > 0) ? Icons.volume_up : Icons.volume_off, ), ), ), ), ), ); } GestureDetector _buildPlayPause(VideoPlayerController controller) { return new GestureDetector( onTap: _playPause, child: new Container( height: barHeight, color: Colors.transparent, margin: new EdgeInsets.only(left: 8.0, right: 4.0), padding: new EdgeInsets.only( left: 12.0, right: 12.0, ), child: new Icon( controller.value.isPlaying ? Icons.pause : Icons.play_arrow, ), ), ); } Widget _buildPosition(Color iconColor) { final position = _latestValue != null && _latestValue.position != null ? _latestValue.position : Duration.zero; final duration = _latestValue != null && _latestValue.duration != null ? _latestValue.duration : Duration.zero; return new Padding( padding: new EdgeInsets.only(right: 24.0), child: new Text( '${formatDuration(position)} / ${formatDuration(duration)}', style: new TextStyle( fontSize: 14.0, ), ), ); } _cancelAndRestartTimer() { _hideTimer?.cancel(); setState(() { _hideStuff = false; _startHideTimer(); }); } Future _initialize() async { widget.controller.addListener(_updateState); _updateState(); _startHideTimer(); new Timer(new Duration(milliseconds: 200), () { setState(() { _hideStuff = false; }); }); } void _onExpandCollapse() { setState(() { _hideStuff = true; widget.onExpandCollapse().then((_) { new Timer(new Duration(milliseconds: 300), () { if (!_disposed) { setState(() { _cancelAndRestartTimer(); }); } }); }); }); } void _playPause() { setState(() { if (widget.controller.value.isPlaying) { _hideStuff = false; _hideTimer?.cancel(); widget.controller.pause(); } else { _cancelAndRestartTimer(); widget.controller.play(); } }); } void _startHideTimer() { _hideTimer = new Timer(const Duration(seconds: 3), () { if (!_disposed) { setState(() { _hideStuff = true; }); } }); } void _updateState() { setState(() { _latestValue = widget.controller.value; }); } Widget _buildProgressBar() { return new Expanded( child: new Padding( padding: new EdgeInsets.only(right: 20.0), child: new MaterialVideoProgressBar( widget.controller, onDragStart: () { setState(() { _dragging = true; }); _hideTimer?.cancel(); }, onDragEnd: () { setState(() { _dragging = false; }); _startHideTimer(); }, colors: new VideoProgressColors( playedColor: Theme.of(context).accentColor, handleColor: Theme.of(context).accentColor, bufferedColor: Theme.of(context).backgroundColor, disabledColor: Theme.of(context).disabledColor), ), ), ); } }