Parcourir la source

Decent looking material design player

Brian Egan il y a 8 ans
Parent
commit
c7e2fea088

+ 97 - 0
example/lib/chewie_player.dart

@@ -0,0 +1,97 @@
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter/widgets.dart';
+import 'package:chewie_example/player_with_controls.dart';
+import 'dart:async';
+import 'package:video_player/video_player.dart';
+
+class ChewiePlayer extends StatefulWidget {
+  final VideoPlayerController controller;
+  final bool autoPlay;
+  final bool looping;
+  final double aspectRatio;
+
+  ChewiePlayer({
+    Key key,
+    @required this.controller,
+    @required this.aspectRatio,
+    this.autoPlay = false,
+    this.looping = false,
+  }) : super(key: key);
+
+  @override
+  State<StatefulWidget> createState() {
+    return new _ChewiePlayerState();
+  }
+}
+
+class _ChewiePlayerState extends State<ChewiePlayer> {
+  @override
+  Widget build(BuildContext context) {
+    return new PlayerWithControls(
+      controller: widget.controller,
+      onExpandCollapse: () {
+        return _pushFullScreenWidget(context);
+      },
+      aspectRatio: widget.aspectRatio,
+    );
+  }
+
+  @override
+  void initState() {
+    _initialize();
+
+    super.initState();
+  }
+
+  _buildFullScreenVideo(BuildContext context, Animation<double> animation) {
+    return new Scaffold(
+      resizeToAvoidBottomPadding: false,
+      body: new Container(
+        color: Colors.black,
+        child: new PlayerWithControls(
+          controller: widget.controller,
+          onExpandCollapse: () => new Future.value(Navigator.of(context).pop()),
+          aspectRatio: widget.aspectRatio,
+          fullScreen: true,
+        ),
+      ),
+    );
+  }
+
+  Widget _fullScreenRoutePageBuilder(
+    BuildContext context,
+    Animation<double> animation,
+    Animation<double> secondaryAnimation,
+  ) {
+    return new AnimatedBuilder(
+      animation: animation,
+      builder: (BuildContext context, Widget child) {
+        return _buildFullScreenVideo(context, animation);
+      },
+    );
+  }
+
+  Future _initialize() async {
+    await widget.controller.setLooping(widget.looping);
+    await widget.controller.initialize();
+
+    if (widget.autoPlay) {
+      await widget.controller.play();
+    }
+  }
+
+  Future<dynamic> _pushFullScreenWidget(BuildContext context) {
+    final TransitionRoute<Null> route = new PageRouteBuilder<Null>(
+      settings: new RouteSettings(isInitialRoute: false),
+      pageBuilder: _fullScreenRoutePageBuilder,
+    );
+
+    SystemChrome.setEnabledSystemUIOverlays([]);
+
+    return Navigator.of(context).push(route).then((_) {
+      SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values);
+    });
+  }
+}

+ 459 - 0
example/lib/cupertino_controls.dart

@@ -0,0 +1,459 @@
+import 'dart:async';
+import 'dart:math' as math;
+import 'dart:ui';
+
+import 'package:chewie_example/utils.dart';
+import 'package:flutter/material.dart';
+import 'package:meta/meta.dart';
+import 'package:open_iconic_flutter/open_iconic_flutter.dart';
+import 'package:video_player/video_player.dart';
+
+class CupertinoControls extends StatefulWidget {
+  final Color backgroundColor;
+  final Color iconColor;
+  final VideoPlayerController controller;
+  final Future<dynamic> Function() onExpandCollapse;
+  final bool fullScreen;
+
+  CupertinoControls({
+    @required this.backgroundColor,
+    @required this.iconColor,
+    @required this.controller,
+    @required this.onExpandCollapse,
+    @required this.fullScreen,
+  });
+
+  @override
+  State<StatefulWidget> createState() {
+    return new _CupertinoControlsState();
+  }
+}
+
+class _CupertinoControlsState extends State<CupertinoControls> {
+  VideoPlayerValue _latestValue;
+  double _latestVolume;
+  bool _hideStuff = true;
+  bool _disposed = false;
+  Timer _hideTimer;
+  final barHeight = 30.0;
+  final marginSize = 5.0;
+
+  @override
+  Widget build(BuildContext context) {
+    final backgroundColor = widget.backgroundColor;
+    final iconColor = widget.iconColor;
+    final controller = widget.controller;
+
+    return new Column(
+      children: <Widget>[
+        _buildTopBar(backgroundColor, iconColor, controller),
+        _buildHitArea(),
+        _buildBottomBar(backgroundColor, iconColor, controller),
+      ],
+    );
+  }
+
+  @override
+  void dispose() {
+    widget.controller.removeListener(_updateState);
+    _disposed = true;
+    super.dispose();
+  }
+
+  @override
+  void initState() {
+    _initialize();
+
+    super.initState();
+  }
+
+  AnimatedOpacity _buildBottomBar(Color backgroundColor, Color iconColor,
+      VideoPlayerController controller) {
+    return new AnimatedOpacity(
+      opacity: _hideStuff ? 0.0 : 1.0,
+      duration: new Duration(milliseconds: 300),
+      child: new Container(
+        color: Colors.transparent,
+        alignment: Alignment.bottomCenter,
+        margin: new EdgeInsets.all(marginSize),
+        child: new ClipRect(
+          child: new BackdropFilter(
+            filter: new ImageFilter.blur(
+              sigmaX: 10.0,
+              sigmaY: 10.0,
+            ),
+            child: new Container(
+              height: barHeight,
+              decoration: new BoxDecoration(
+                color: backgroundColor,
+                borderRadius: new BorderRadius.all(
+                  new Radius.circular(10.0),
+                ),
+              ),
+              child: new Row(
+                children: <Widget>[
+                  _buildSkipBack(iconColor),
+                  _buildPlayPause(controller, iconColor),
+                  _buildSkipForward(iconColor),
+                  _buildPosition(iconColor),
+                  new _ProgressBar(controller),
+                  _buildRemaining(iconColor)
+                ],
+              ),
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+
+  GestureDetector _buildExpandButton(Color backgroundColor, Color iconColor) {
+    return new GestureDetector(
+      onTap: _onExpandCollapse,
+      child: new AnimatedOpacity(
+        opacity: _hideStuff ? 0.0 : 1.0,
+        duration: new Duration(milliseconds: 300),
+        child: new ClipRect(
+          child: new BackdropFilter(
+            filter: new ImageFilter.blur(sigmaX: 10.0),
+            child: new Container(
+              height: barHeight,
+              padding: new EdgeInsets.only(
+                left: 16.0,
+                right: 16.0,
+              ),
+              decoration: new BoxDecoration(
+                color: backgroundColor,
+                borderRadius: new BorderRadius.all(
+                  new Radius.circular(10.0),
+                ),
+              ),
+              child: new Center(
+                child: new Icon(
+                  widget.fullScreen
+                      ? OpenIconicIcons.fullscreenExit
+                      : OpenIconicIcons.fullscreenEnter,
+                  color: iconColor,
+                  size: 12.0,
+                ),
+              ),
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+
+  Expanded _buildHitArea() {
+    return new Expanded(
+      child: new GestureDetector(
+        onTap: _latestValue != null && _latestValue.isPlaying
+            ? _cancelAndRestartTimer
+            : () {
+                _hideTimer?.cancel();
+
+                setState(() {
+                  _hideStuff = false;
+                });
+              },
+        child: new Container(
+          color: Colors.transparent,
+        ),
+      ),
+    );
+  }
+
+  GestureDetector _buildMuteButton(VideoPlayerController controller,
+      Color backgroundColor, Color iconColor) {
+    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 BackdropFilter(
+            filter: new ImageFilter.blur(sigmaX: 10.0),
+            child: new Container(
+              decoration: new BoxDecoration(
+                color: backgroundColor,
+                borderRadius: new BorderRadius.all(
+                  new Radius.circular(10.0),
+                ),
+              ),
+              child: new Container(
+                height: barHeight,
+                padding: new EdgeInsets.only(
+                  left: 16.0,
+                  right: 16.0,
+                ),
+                child: new Icon(
+                  (_latestValue != null && _latestValue.volume > 0)
+                      ? Icons.volume_up
+                      : Icons.volume_off,
+                  color: iconColor,
+                  size: 16.0,
+                ),
+              ),
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+
+  GestureDetector _buildPlayPause(
+      VideoPlayerController controller, Color iconColor) {
+    return new GestureDetector(
+      onTap: _playPause,
+      child: new Container(
+        height: barHeight,
+        color: Colors.transparent,
+        padding: new EdgeInsets.only(
+          left: 6.0,
+          right: 6.0,
+        ),
+        child: new Icon(
+          controller.value.isPlaying
+              ? OpenIconicIcons.mediaPause
+              : OpenIconicIcons.mediaPlay,
+          color: iconColor,
+          size: 16.0,
+        ),
+      ),
+    );
+  }
+
+  Widget _buildPosition(Color iconColor) {
+    final position =
+        _latestValue != null ? _latestValue.position : new Duration(seconds: 0);
+
+    return new Padding(
+      padding: new EdgeInsets.only(right: 12.0),
+      child: new Text(
+        formatDuration(position),
+        style: new TextStyle(
+          color: iconColor,
+          fontSize: 12.0,
+        ),
+      ),
+    );
+  }
+
+  Widget _buildRemaining(Color iconColor) {
+    final position = _latestValue != null && _latestValue.duration != null
+        ? _latestValue.duration - _latestValue.position
+        : new Duration(seconds: 0);
+
+    return new Padding(
+      padding: new EdgeInsets.only(right: 12.0),
+      child: new Text(
+        '-${formatDuration(position)}',
+        style: new TextStyle(color: iconColor, fontSize: 12.0),
+      ),
+    );
+  }
+
+  GestureDetector _buildSkipBack(Color iconColor) {
+    return new GestureDetector(
+      onTap: _skipBack,
+      child: new Container(
+        height: barHeight,
+        color: Colors.transparent,
+        margin: new EdgeInsets.only(left: 10.0),
+        padding: new EdgeInsets.only(
+          left: 6.0,
+          right: 6.0,
+        ),
+        child: new Transform(
+          alignment: Alignment.center,
+          transform: new Matrix4.skewY(0.0)
+            ..rotateX(math.pi)
+            ..rotateZ(math.pi),
+          child: new Icon(
+            OpenIconicIcons.reload,
+            color: iconColor,
+            size: 12.0,
+          ),
+        ),
+      ),
+    );
+  }
+
+  GestureDetector _buildSkipForward(Color iconColor) {
+    return new GestureDetector(
+      onTap: _skipForward,
+      child: new Container(
+        height: barHeight,
+        color: Colors.transparent,
+        padding: new EdgeInsets.only(
+          left: 6.0,
+          right: 8.0,
+        ),
+        margin: new EdgeInsets.only(
+          right: 8.0,
+        ),
+        child: new Icon(
+          OpenIconicIcons.reload,
+          color: iconColor,
+          size: 12.0,
+        ),
+      ),
+    );
+  }
+
+  Widget _buildTopBar(Color backgroundColor, Color iconColor,
+      VideoPlayerController controller) {
+    return new Container(
+      height: barHeight,
+      margin: new EdgeInsets.only(
+        top: marginSize,
+        right: marginSize,
+        left: marginSize,
+      ),
+      child: new Row(
+        children: <Widget>[
+          _buildExpandButton(backgroundColor, iconColor),
+          new Expanded(child: new Container()),
+          _buildMuteButton(controller, backgroundColor, iconColor),
+        ],
+      ),
+    );
+  }
+
+  _cancelAndRestartTimer() {
+    _hideTimer?.cancel();
+
+    setState(() {
+      _hideStuff = false;
+
+      _startHideTimer();
+    });
+  }
+
+  Future<Null> _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 _skipBack() {
+    final beginning = new Duration(seconds: 0).inMicroseconds;
+    final skip =
+        (_latestValue.position - new Duration(seconds: 15)).inMicroseconds;
+    widget.controller
+        .seekTo(new Duration(milliseconds: math.max(skip, beginning)));
+  }
+
+  void _skipForward() {
+    final end = _latestValue.duration.inMicroseconds;
+    final skip =
+        (_latestValue.position + new Duration(seconds: 15)).inMicroseconds;
+    widget.controller.seekTo(new Duration(milliseconds: math.min(skip, end)));
+  }
+
+  void _startHideTimer() {
+    _hideTimer = new Timer(const Duration(seconds: 3), () {
+      if (!_disposed) {
+        setState(() {
+          _hideStuff = true;
+        });
+      }
+    });
+  }
+
+  void _updateState() {
+    setState(() {
+      _latestValue = widget.controller.value;
+    });
+  }
+}
+
+class _ProgressBar extends StatelessWidget {
+  final VideoPlayerController controller;
+
+  _ProgressBar(this.controller);
+
+  @override
+  Widget build(BuildContext context) {
+    return new Expanded(
+      child: new Padding(
+        padding: new EdgeInsets.only(right: 12.0),
+        child: new ClipRRect(
+          borderRadius: new BorderRadius.all(new Radius.circular(10.0)),
+          child: new Container(
+            height: 5.0,
+            child: new VideoProgressBar(
+              controller,
+              colors: new VideoProgressColors(
+                playedColor: new Color.fromARGB(
+                  255,
+                  255,
+                  255,
+                  255,
+                ),
+                handleColor: new Color.fromARGB(
+                  255,
+                  255,
+                  255,
+                  255,
+                ),
+                bufferedColor: new Color.fromARGB(
+                  140,
+                  255,
+                  255,
+                  255,
+                ),
+              ),
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+}

+ 54 - 634
example/lib/main.dart

@@ -1,664 +1,84 @@
-import 'dart:async';
-import 'dart:math' as math;
-import 'dart:ui';
-
+import 'package:chewie_example/chewie_player.dart';
 import 'package:flutter/cupertino.dart';
-import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
-import 'package:flutter/services.dart';
-import 'package:open_iconic_flutter/open_iconic_flutter.dart';
 import 'package:video_player/video_player.dart';
 
-void main() => runApp(new MyApp());
+void main() => runApp(new ChewieDemo());
 
 const String butterflyUri =
     'https://flutter.github.io/assets-for-api-docs/videos/butterfly.mp4';
 
-String _formatDuration(Duration position) {
-  final ms = position.inMilliseconds;
-
-  int seconds = ms ~/ 1000;
-  final int hours = seconds ~/ 3600;
-  seconds = seconds % 3600;
-  var minutes = seconds ~/ 60;
-  seconds = seconds % 60;
-
-  final hoursString = hours > 10 ? '$hours' : hours == 0 ? '00' : '0$hours';
-
-  final minutesString =
-      minutes > 10 ? '$minutes' : minutes == 0 ? '00' : '0$minutes';
-
-  final secondsString =
-      seconds > 10 ? '$seconds' : seconds == 0 ? '00' : '0$seconds';
-
-  final formattedTime = '${hoursString == '00' ? '' : hoursString +
-      ':'}$minutesString:$secondsString';
-
-  return formattedTime;
-}
-
-class CupertinoControls extends StatefulWidget {
-  final Color backgroundColor;
-  final Color iconColor;
-  final VideoPlayerController controller;
-  final Future<dynamic> Function() onExpandCollapse;
-  final bool fullScreen;
-
-  CupertinoControls({
-    @required this.backgroundColor,
-    @required this.iconColor,
-    @required this.controller,
-    @required this.onExpandCollapse,
-    @required this.fullScreen,
-  });
-
-  @override
-  State<StatefulWidget> createState() {
-    return new CupertinoControlsState();
-  }
-}
-
-class CupertinoControlsState extends State<CupertinoControls> {
-  VideoPlayerValue _latestValue;
-  double _latestVolume;
-  bool _hideStuff = true;
-  bool _disposed = false;
-  Timer _hideTimer;
-  final barHeight = 30.0;
-  final marginSize = 5.0;
-
-  @override
-  Widget build(BuildContext context) {
-    final backgroundColor = widget.backgroundColor;
-    final iconColor = widget.iconColor;
-    final controller = widget.controller;
-
-    return new Column(
-      children: <Widget>[
-        _buildTopBar(backgroundColor, iconColor, controller),
-        _buildHitArea(),
-        _buildBottomBar(backgroundColor, iconColor, controller),
-      ],
-    );
-  }
-
-  @override
-  void dispose() {
-    widget.controller.removeListener(_updateState);
-    _disposed = true;
-    super.dispose();
-  }
-
-  @override
-  void initState() {
-    _initialize();
-
-    super.initState();
-  }
-
-  AnimatedOpacity _buildBottomBar(Color backgroundColor, Color iconColor,
-      VideoPlayerController controller) {
-    return new AnimatedOpacity(
-      opacity: _hideStuff ? 0.0 : 1.0,
-      duration: new Duration(milliseconds: 300),
-      child: new Container(
-        color: Colors.transparent,
-        alignment: Alignment.bottomCenter,
-        margin: new EdgeInsets.all(marginSize),
-        child: new ClipRect(
-          child: new BackdropFilter(
-            filter: new ImageFilter.blur(
-              sigmaX: 10.0,
-              sigmaY: 10.0,
-            ),
-            child: new Container(
-              height: barHeight,
-              decoration: new BoxDecoration(
-                color: backgroundColor,
-                borderRadius: new BorderRadius.all(
-                  new Radius.circular(10.0),
-                ),
-              ),
-              child: new Row(
-                children: <Widget>[
-                  _buildSkipBack(iconColor),
-                  _buildPlayPause(controller, iconColor),
-                  _buildSkipForward(iconColor),
-                  _buildPosition(iconColor),
-                  new ProgressBar(controller),
-                  _buildRemaining(iconColor)
-                ],
-              ),
-            ),
-          ),
-        ),
-      ),
-    );
-  }
-
-  GestureDetector _buildExpandButton(Color backgroundColor, Color iconColor) {
-    return new GestureDetector(
-      onTap: _onExpandCollapse,
-      child: new AnimatedOpacity(
-        opacity: _hideStuff ? 0.0 : 1.0,
-        duration: new Duration(milliseconds: 300),
-        child: new ClipRect(
-          child: new BackdropFilter(
-            filter: new ImageFilter.blur(sigmaX: 10.0),
-            child: new Container(
-              height: barHeight,
-              padding: new EdgeInsets.only(
-                left: 16.0,
-                right: 16.0,
-              ),
-              decoration: new BoxDecoration(
-                color: backgroundColor,
-                borderRadius: new BorderRadius.all(
-                  new Radius.circular(10.0),
-                ),
-              ),
-              child: new Center(
-                child: new Icon(
-                  widget.fullScreen
-                      ? OpenIconicIcons.fullscreenExit
-                      : OpenIconicIcons.fullscreenEnter,
-                  color: iconColor,
-                  size: 12.0,
-                ),
-              ),
-            ),
-          ),
-        ),
-      ),
-    );
-  }
-
-  Expanded _buildHitArea() {
-    return new Expanded(
-      child: new GestureDetector(
-        onTap: _latestValue != null && _latestValue.isPlaying
-            ? _cancelAndRestartTimer
-            : () {
-                _hideTimer.cancel();
-
-                setState(() {
-                  _hideStuff = false;
-                });
-              },
-        child: new Container(
-          color: Colors.transparent,
-        ),
-      ),
-    );
-  }
-
-  GestureDetector _buildMuteButton(VideoPlayerController controller,
-      Color backgroundColor, Color iconColor) {
-    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 BackdropFilter(
-            filter: new ImageFilter.blur(sigmaX: 10.0),
-            child: new Container(
-              decoration: new BoxDecoration(
-                color: backgroundColor,
-                borderRadius: new BorderRadius.all(
-                  new Radius.circular(10.0),
-                ),
-              ),
-              child: new Container(
-                height: barHeight,
-                padding: new EdgeInsets.only(
-                  left: 16.0,
-                  right: 16.0,
-                ),
-                child: new Icon(
-                  (_latestValue != null && _latestValue.volume > 0)
-                      ? Icons.volume_up
-                      : Icons.volume_mute,
-                  color: iconColor,
-                  size: 16.0,
-                ),
-              ),
-            ),
-          ),
-        ),
-      ),
-    );
-  }
-
-  GestureDetector _buildPlayPause(
-      VideoPlayerController controller, Color iconColor) {
-    return new GestureDetector(
-      onTap: _playPause,
-      child: new Container(
-        height: barHeight,
-        color: Colors.transparent,
-        padding: new EdgeInsets.only(
-          left: 6.0,
-          right: 6.0,
-        ),
-        child: new Icon(
-          controller.value.isPlaying
-              ? OpenIconicIcons.mediaPause
-              : OpenIconicIcons.mediaPlay,
-          color: iconColor,
-          size: 16.0,
-        ),
-      ),
-    );
-  }
-
-  Widget _buildPosition(Color iconColor) {
-    final position =
-        _latestValue != null ? _latestValue.position : new Duration(seconds: 0);
-
-    return new Padding(
-      padding: new EdgeInsets.only(right: 12.0),
-      child: new Text(
-        _formatDuration(position),
-        style: new TextStyle(
-          color: iconColor,
-          fontSize: 12.0,
-        ),
-      ),
-    );
-  }
-
-  Widget _buildRemaining(Color iconColor) {
-    final position = _latestValue != null && _latestValue.duration != null
-        ? _latestValue.duration - _latestValue.position
-        : new Duration(seconds: 0);
-
-    return new Padding(
-      padding: new EdgeInsets.only(right: 12.0),
-      child: new Text(
-        '-${_formatDuration(position)}',
-        style: new TextStyle(color: iconColor, fontSize: 12.0),
-      ),
-    );
-  }
-
-  GestureDetector _buildSkipBack(Color iconColor) {
-    return new GestureDetector(
-      onTap: _skipBack,
-      child: new Container(
-        height: barHeight,
-        color: Colors.transparent,
-        margin: new EdgeInsets.only(left: 10.0),
-        padding: new EdgeInsets.only(
-          left: 6.0,
-          right: 6.0,
-        ),
-        child: new Transform(
-          alignment: Alignment.center,
-          transform: new Matrix4.skewY(0.0)
-            ..rotateX(math.pi)
-            ..rotateZ(math.pi),
-          child: new Icon(
-            OpenIconicIcons.reload,
-            color: iconColor,
-            size: 12.0,
-          ),
-        ),
-      ),
-    );
-  }
-
-  GestureDetector _buildSkipForward(Color iconColor) {
-    return new GestureDetector(
-      onTap: _skipForward,
-      child: new Container(
-        height: barHeight,
-        color: Colors.transparent,
-        padding: new EdgeInsets.only(
-          left: 6.0,
-          right: 8.0,
-        ),
-        margin: new EdgeInsets.only(
-          right: 8.0,
-        ),
-        child: new Icon(
-          OpenIconicIcons.reload,
-          color: iconColor,
-          size: 12.0,
-        ),
-      ),
-    );
-  }
-
-  Widget _buildTopBar(Color backgroundColor, Color iconColor,
-      VideoPlayerController controller) {
-    return new Container(
-      height: barHeight,
-      margin: new EdgeInsets.only(
-        top: marginSize,
-        right: marginSize,
-        left: marginSize,
-      ),
-      child: new Row(
-        children: <Widget>[
-          _buildExpandButton(backgroundColor, iconColor),
-          new Expanded(child: new Container()),
-          _buildMuteButton(controller, backgroundColor, iconColor),
-        ],
-      ),
-    );
-  }
-
-  _cancelAndRestartTimer() {
-    _hideTimer.cancel();
-
-    setState(() {
-      _hideStuff = false;
-
-      _startHideTimer();
-    });
-  }
-
-  Future<Null> _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((_) {
-        _cancelAndRestartTimer();
-
-        new Timer(new Duration(milliseconds: 300), () {
-          if (!_disposed) {
-            setState(() {
-              _hideStuff = false;
-            });
-          }
-        });
-      });
-    });
-  }
-
-  void _playPause() {
-    setState(() {
-      if (widget.controller.value.isPlaying) {
-        _hideStuff = false;
-        _hideTimer.cancel();
-        widget.controller.pause();
-      } else {
-        _cancelAndRestartTimer();
-        widget.controller.play();
-      }
-    });
-  }
-
-  void _skipBack() {
-    final beginning = new Duration(seconds: 0).inMicroseconds;
-    final skip =
-        (_latestValue.position - new Duration(seconds: 15)).inMicroseconds;
-    widget.controller
-        .seekTo(new Duration(milliseconds: math.max(skip, beginning)));
-  }
-
-  void _skipForward() {
-    final end = _latestValue.duration.inMicroseconds;
-    final skip =
-        (_latestValue.position + new Duration(seconds: 15)).inMicroseconds;
-    widget.controller.seekTo(new Duration(milliseconds: math.min(skip, end)));
-  }
-
-  void _startHideTimer() {
-    _hideTimer = new Timer(const Duration(seconds: 3), () {
-      if (!_disposed) {
-        setState(() {
-          _hideStuff = true;
-        });
-      }
-    });
-  }
-
-  void _updateState() {
-    setState(() {
-      _latestValue = widget.controller.value;
-    });
-  }
-}
-
-class MyApp extends StatelessWidget {
-  // This widget is the root of your application.
-  @override
-  Widget build(BuildContext context) {
-    return new MaterialApp(
-      title: 'Chewie Demo',
-      theme: new ThemeData(
-        primarySwatch: Colors.blue,
-      ),
-      home: new MyHomePage(title: 'Chewie Demo'),
-    );
-  }
-}
-
-class MyHomePage extends StatefulWidget {
+class ChewieDemo extends StatefulWidget {
   final String title;
 
-  MyHomePage({Key key, this.title}) : super(key: key);
-
-  @override
-  _MyHomePageState createState() => new _MyHomePageState();
-}
-
-class ProgressBar extends StatelessWidget {
-  final VideoPlayerController controller;
-
-  ProgressBar(this.controller);
-
-  @override
-  Widget build(BuildContext context) {
-    return new Expanded(
-      child: new Padding(
-        padding: new EdgeInsets.only(right: 12.0),
-        child: new ClipRRect(
-          borderRadius: new BorderRadius.all(new Radius.circular(10.0)),
-          child: new Container(
-            height: 5.0,
-            child: new VideoProgressBar(
-              controller,
-              colors: new VideoProgressColors(
-                playedColor: new Color.fromARGB(
-                  255,
-                  255,
-                  255,
-                  255,
-                ),
-                handleColor: new Color.fromARGB(
-                  255,
-                  255,
-                  255,
-                  255,
-                ),
-                bufferedColor: new Color.fromARGB(
-                  140,
-                  255,
-                  255,
-                  255,
-                ),
-              ),
-            ),
-          ),
-        ),
-      ),
-    );
-  }
-}
-
-class VideoPlayerWithControls extends StatefulWidget {
-  final VideoPlayerController controller;
-  final Future<dynamic> Function() onExpandCollapse;
-  final bool fullScreen;
-
-  VideoPlayerWithControls(
-    this.controller,
-    this.onExpandCollapse, {
-    this.fullScreen = false,
-  });
+  ChewieDemo({this.title = 'Chewie Demo'});
 
   @override
-  State createState() {
-    return new _VideoPlayerWithControlsState();
+  State<StatefulWidget> createState() {
+    return new _ChewieDemoState();
   }
 }
 
-class _MyHomePageState extends State<MyHomePage> {
+class _ChewieDemoState extends State<ChewieDemo> {
   final controller = new VideoPlayerController(
     butterflyUri,
   );
+  TargetPlatform _platform;
 
   @override
   Widget build(BuildContext context) {
-    return new Scaffold(
-      appBar: new AppBar(
-        title: new Text(widget.title),
+    return new MaterialApp(
+      title: widget.title,
+      theme: new ThemeData.light().copyWith(
+        platform: _platform ?? Theme.of(context).platform,
       ),
-      body: new VideoPlayerWithControls(controller, () {
-        return _pushFullScreenWidget(context);
-      }),
-    );
-  }
-
-  @override
-  void initState() {
-    _initialize();
-
-    super.initState();
-  }
-
-  _buildFullScreenVideo(BuildContext context, Animation<double> animation) {
-    return new Scaffold(
-      resizeToAvoidBottomPadding: false,
-      body: new Container(
-        color: Colors.black,
-        child: new VideoPlayerWithControls(
-          controller,
-          () => new Future.value(Navigator.of(context).pop()),
-          fullScreen: true,
+      home: new Scaffold(
+        appBar: new AppBar(
+          title: new Text(widget.title),
         ),
-      ),
-    );
-  }
-
-  Widget _fullScreenRoutePageBuilder(
-    BuildContext context,
-    Animation<double> animation,
-    Animation<double> secondaryAnimation,
-  ) {
-    SystemChrome.setEnabledSystemUIOverlays([]);
-
-    return new AnimatedBuilder(
-      animation: animation,
-      builder: (BuildContext context, Widget child) {
-        return _buildFullScreenVideo(context, animation);
-      },
-    );
-  }
-
-  Future _initialize() async {
-    await controller.setLooping(true);
-    await controller.initialize();
-
-    await controller.play();
-  }
-
-  Future<dynamic> _pushFullScreenWidget(BuildContext context) {
-    final TransitionRoute<Null> route = new PageRouteBuilder<Null>(
-      settings: new RouteSettings(isInitialRoute: false),
-      pageBuilder: _fullScreenRoutePageBuilder,
-    );
-
-    return Navigator.of(context).push(route).then((_) {
-      new Timer(new Duration(milliseconds: 800), () {
-        SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values);
-      });
-    });
-  }
-}
-
-class _VideoPlayerWithControlsState extends State<VideoPlayerWithControls> {
-  @override
-  Widget build(BuildContext context) {
-    final iconColor = new Color.fromARGB(255, 200, 200, 200);
-    final backgroundColor = new Color.fromRGBO(41, 41, 41, 0.7);
-    final controller = widget.controller;
-
-    return new Center(
-      child: new Container(
-        width: MediaQuery.of(context).size.width,
-        child: new AspectRatio(
-          aspectRatio: 3 / 2,
-          child: new Container(
-            child: new Stack(
-              children: <Widget>[
-                new Hero(
-                  tag: controller,
-                  child: new VideoPlayer(controller),
+        body: new Column(
+          children: <Widget>[
+            new Expanded(
+              child: new Center(
+                child: new ChewiePlayer(
+                  controller: controller,
+                  aspectRatio: 3 / 2,
+                  looping: true,
+                  autoPlay: true,
                 ),
-                Theme.of(context).platform == TargetPlatform.android
-                    ? new CupertinoControls(
-                        backgroundColor: backgroundColor,
-                        iconColor: iconColor,
-                        controller: controller,
-                        onExpandCollapse: widget.onExpandCollapse,
-                        fullScreen: widget.fullScreen,
-                      )
-                    : new CupertinoControls(
-                        backgroundColor: backgroundColor,
-                        iconColor: iconColor,
-                        controller: controller,
-                        onExpandCollapse: widget.onExpandCollapse,
-                        fullScreen: widget.fullScreen,
-                      ),
-              ],
+              ),
             ),
-          ),
+            new Row(
+              children: <Widget>[
+                new Expanded(
+                    child: new FlatButton(
+                  onPressed: () {
+                    setState(() {
+                      _platform = TargetPlatform.android;
+                    });
+                  },
+                  child: new Padding(
+                    child: new Text("Android controls"),
+                    padding: new EdgeInsets.symmetric(vertical: 16.0),
+                  ),
+                )),
+                new Expanded(
+                  child: new FlatButton(
+                      onPressed: () {
+                        setState(() {
+                          _platform = TargetPlatform.iOS;
+                        });
+                      },
+                      child: new Padding(
+                        padding: new EdgeInsets.symmetric(vertical: 16.0),
+                        child: new Text("iOS controls"),
+                      )),
+                )
+              ],
+            )
+          ],
         ),
       ),
     );
   }
-
-  @override
-  void initState() {
-    // Hack to show the video when it starts playing. Should be fixed by the
-    // Plugin IMO.
-    widget.controller.addListener(_onPlay);
-
-    super.initState();
-  }
-
-  void _onPlay() {
-    if (widget.controller.value.isPlaying) {
-      setState(() {
-        widget.controller.removeListener(_onPlay);
-      });
-    }
-  }
 }

+ 324 - 0
example/lib/material_controls.dart

@@ -0,0 +1,324 @@
+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<dynamic> Function() onExpandCollapse;
+
+  MaterialControls({
+    @required this.controller,
+    @required this.fullScreen,
+    @required this.onExpandCollapse,
+  });
+
+  @override
+  State<StatefulWidget> createState() {
+    return new _MaterialControlsState();
+  }
+}
+
+class _MaterialControlsState extends State<MaterialControls> {
+  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: <Widget>[
+        _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: <Widget>[
+            _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<Null> _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),
+        ),
+      ),
+    );
+  }
+}

+ 182 - 0
example/lib/material_progress_bar.dart

@@ -0,0 +1,182 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:video_player/video_player.dart';
+
+class MaterialVideoProgressBar extends StatefulWidget {
+  final VideoPlayerController controller;
+  final VideoProgressColors colors;
+  final Function() onDragStart;
+  final Function() onDragEnd;
+  final Function() onDragUpdate;
+
+  MaterialVideoProgressBar(
+    this.controller, {
+    VideoProgressColors colors,
+    this.onDragEnd,
+    this.onDragStart,
+    this.onDragUpdate,
+  })
+      : colors = colors ?? new VideoProgressColors();
+
+  @override
+  _VideoProgressBarState createState() {
+    return new _VideoProgressBarState();
+  }
+}
+
+class _VideoProgressBarState extends State<MaterialVideoProgressBar> {
+  VoidCallback listener;
+
+  bool _controllerWasPlaying = false;
+
+  _VideoProgressBarState() {
+    listener = () {
+      setState(() {});
+    };
+  }
+
+  VideoPlayerController get controller => widget.controller;
+
+  @override
+  void initState() {
+    super.initState();
+    controller.addListener(listener);
+  }
+
+  @override
+  void deactivate() {
+    controller.removeListener(listener);
+    super.deactivate();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    void seekToRelativePosition(Offset globalPosition) {
+      final RenderBox box = context.findRenderObject();
+      final Offset tapPos = box.globalToLocal(globalPosition);
+      final double relative = tapPos.dx / box.size.width;
+      final Duration position = controller.value.duration * relative;
+      controller.seekTo(position);
+    }
+
+    return new GestureDetector(
+      child: (controller.value.isErroneous)
+          ? new Text(controller.value.errorDescription)
+          : new Center(
+              child: new Container(
+                height: MediaQuery.of(context).size.height / 2,
+                width: MediaQuery.of(context).size.width,
+                color: Colors.transparent,
+                child: new CustomPaint(
+                  painter: new _ProgressBarPainter(
+                    controller.value,
+                    widget.colors,
+                  ),
+                ),
+              ),
+            ),
+      onHorizontalDragStart: (DragStartDetails details) {
+        if (!controller.value.initialized) {
+          return;
+        }
+        _controllerWasPlaying = controller.value.isPlaying;
+        if (_controllerWasPlaying) {
+          controller.pause();
+        }
+
+        if (widget.onDragStart != null) {
+          widget.onDragStart();
+        }
+      },
+      onHorizontalDragUpdate: (DragUpdateDetails details) {
+        if (!controller.value.initialized) {
+          return;
+        }
+        seekToRelativePosition(details.globalPosition);
+
+
+        if (widget.onDragUpdate != null) {
+          widget.onDragUpdate();
+        }
+      },
+      onHorizontalDragEnd: (DragEndDetails details) {
+        if (_controllerWasPlaying) {
+          controller.play();
+        }
+
+        if (widget.onDragEnd != null) {
+          widget.onDragEnd();
+        }
+      },
+      onTapDown: (TapDownDetails details) {
+        if (!controller.value.initialized) {
+          return;
+        }
+        seekToRelativePosition(details.globalPosition);
+      },
+    );
+  }
+}
+
+class _ProgressBarPainter extends CustomPainter {
+  VideoPlayerValue value;
+  VideoProgressColors colors;
+
+  _ProgressBarPainter(this.value, this.colors);
+
+  @override
+  bool shouldRepaint(CustomPainter painter) {
+    return true;
+  }
+
+  @override
+  void paint(Canvas canvas, Size size) {
+    final height = 2.0;
+
+    canvas.drawRRect(
+      new RRect.fromRectAndRadius(
+        new Rect.fromPoints(
+          new Offset(0.0, size.height / 2),
+          new Offset(size.width, size.height / 2 + height),
+        ),
+        new Radius.circular(4.0),
+      ),
+      colors.disabledPaint,
+    );
+    if (!value.initialized) {
+      return;
+    }
+    final double playedPart = value.position.inMilliseconds /
+        value.duration.inMilliseconds *
+        size.width;
+    for (DurationRange range in value.buffered) {
+      final double start = range.startFraction(value.duration) * size.width;
+      final double end = range.endFraction(value.duration) * size.width;
+      canvas.drawRRect(
+        new RRect.fromRectAndRadius(
+          new Rect.fromPoints(
+            new Offset(start, size.height / 2),
+            new Offset(end, size.height / 2 + height),
+          ),
+          new Radius.circular(4.0),
+        ),
+        colors.bufferedPaint,
+      );
+    }
+    canvas.drawRRect(
+      new RRect.fromRectAndRadius(
+        new Rect.fromPoints(
+          new Offset(0.0, size.height / 2),
+          new Offset(playedPart, size.height / 2 + height),
+        ),
+        new Radius.circular(4.0),
+      ),
+      colors.playedPaint,
+    );
+    canvas.drawCircle(
+      new Offset(playedPart, size.height / 2 + height / 2),
+      height * 3,
+      colors.handlePaint,
+    );
+  }
+}

+ 88 - 0
example/lib/player_with_controls.dart

@@ -0,0 +1,88 @@
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:video_player/video_player.dart';
+
+import 'dart:async';
+
+import 'dart:ui';
+import 'package:chewie_example/material_controls.dart';
+import 'package:chewie_example/cupertino_controls.dart';
+
+class PlayerWithControls extends StatefulWidget {
+  final VideoPlayerController controller;
+  final Future<dynamic> Function() onExpandCollapse;
+  final bool fullScreen;
+
+  final double aspectRatio;
+
+  PlayerWithControls(
+      {@required this.controller,
+      @required this.onExpandCollapse,
+      @required this.aspectRatio,
+      this.fullScreen = false});
+
+  @override
+  State createState() {
+    return new _VideoPlayerWithControlsState();
+  }
+}
+
+class _VideoPlayerWithControlsState extends State<PlayerWithControls> {
+  @override
+  Widget build(BuildContext context) {
+    final controller = widget.controller;
+
+    return new Center(
+      child: new Container(
+        width: MediaQuery.of(context).size.width,
+        child: new AspectRatio(
+          aspectRatio: widget.aspectRatio,
+          child: new Container(
+            child: new Stack(
+              children: <Widget>[
+                new Hero(
+                  tag: controller,
+                  child: new Center(
+                    child: new AspectRatio(
+                        aspectRatio: widget.aspectRatio,
+                        child: new VideoPlayer(controller)),
+                  ),
+                ),
+                Theme.of(context).platform == TargetPlatform.android
+                    ? new MaterialControls(
+                        controller: controller,
+                        onExpandCollapse: widget.onExpandCollapse,
+                        fullScreen: widget.fullScreen,
+                      )
+                    : new CupertinoControls(
+                        backgroundColor: new Color.fromRGBO(41, 41, 41, 0.7),
+                        iconColor: new Color.fromARGB(255, 200, 200, 200),
+                        controller: controller,
+                        onExpandCollapse: widget.onExpandCollapse,
+                        fullScreen: widget.fullScreen,
+                      ),
+              ],
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+
+  @override
+  void initState() {
+    // Hack to show the video when it starts playing. Should be fixed by the
+    // Plugin IMO.
+    widget.controller.addListener(_onPlay);
+
+    super.initState();
+  }
+
+  void _onPlay() {
+    if (widget.controller.value.isPlaying) {
+      setState(() {
+        widget.controller.removeListener(_onPlay);
+      });
+    }
+  }
+}

+ 22 - 0
example/lib/utils.dart

@@ -0,0 +1,22 @@
+String formatDuration(Duration position) {
+  final ms = position.inMilliseconds;
+
+  int seconds = ms ~/ 1000;
+  final int hours = seconds ~/ 3600;
+  seconds = seconds % 3600;
+  var minutes = seconds ~/ 60;
+  seconds = seconds % 60;
+
+  final hoursString = hours > 10 ? '$hours' : hours == 0 ? '00' : '0$hours';
+
+  final minutesString =
+      minutes > 10 ? '$minutes' : minutes == 0 ? '00' : '0$minutes';
+
+  final secondsString =
+      seconds > 10 ? '$seconds' : seconds == 0 ? '00' : '0$seconds';
+
+  final formattedTime = '${hoursString == '00' ? '' : hoursString +
+      ':'}$minutesString:$secondsString';
+
+  return formattedTime;
+}

+ 0 - 5
example/test/widget_test.dart

@@ -4,11 +4,6 @@
 // find child widgets in the widget tree, read text, and verify that the values of widget properties
 // are correct.
 
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-//import 'package:chewie/main.dart';
-
 void main() {
 //  testWidgets('Counter increments smoke test', (WidgetTester tester) async {
 //    // Build our app and trigger a frame.

+ 0 - 5
test/widget_test.dart

@@ -4,11 +4,6 @@
 // find child widgets in the widget tree, read text, and verify that the values of widget properties
 // are correct.
 
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-import 'package:chewie/chewie.dart';
-
 void main() {
 //  testWidgets('Counter increments smoke test', (WidgetTester tester) async {
 //    // Build our app and trigger a frame.