Forráskód Böngészése

Merge pull request #99 from cbenhagen/ChewieController

ChewieController
Ben Hagen 6 éve
szülő
commit
58931ed740

+ 5 - 0
CHANGELOG.md

@@ -1,5 +1,10 @@
 # Changelog
 
+## 0.9.0
+
+  * **Breaking changes**: Add a `ChewieController` to make customizations and control from outside of the player easier.
+    Refer to the [README](README.md) for details on how to upgrade from previous versions.
+
 ## 0.8.0
 
   * Update to work with `video_player: ">=0.7.0 <0.8.0` - Thanks @Sub6Resources 

+ 45 - 5
README.md

@@ -21,23 +21,63 @@ dependencies:
 
 ```dart
 import 'package:chewie/chewie.dart';
+final videoPlayerController = VideoPlayerController.network(
+    'https://flutter.github.io/assets-for-api-docs/videos/butterfly.mp4');
 
-final playerWidget = new Chewie(
-  new VideoPlayerController.network(
-    'https://flutter.github.io/assets-for-api-docs/videos/butterfly.mp4'
-  ),
+final chewieController = ChewieController(
+  videoPlayerController: videoPlayerController,
   aspectRatio: 3 / 2,
   autoPlay: true,
   looping: true,
 );
+
+final playerWidget = Chewie(
+  controller: chewieController,
+);
+```
+
+Please make sure to dispose both controller widgets after use. For example by overriding the dispose method of the a `StatefulWidget`:
+```dart
+@override
+void dispose() {
+videoPlayerController.dispose();
+chewieController.dispose();
+super.dispose();
+}
 ```
 
 ## Example
 
 Please run the app in the [`example/`](https://github.com/brianegan/chewie/tree/master/example) folder to start playing!
 
+## Migrating from Chewie < 0.9.0
+Instead of passing the `VideoPlayerController` and your options to the `Chewie` widget you now pass them to the `ChewieController` and pass that latter to the `Chewie` widget.
+
+```dart
+final playerWidget = Chewie(
+  videoPlayerController,
+  aspectRatio: 3 / 2,
+  autoPlay: true,
+  looping: true,
+);
+```
+
+becomes
+
+```dart
+final chewieController = ChewieController(
+  videoPlayerController: videoPlayerController,
+  aspectRatio: 3 / 2,
+  autoPlay: true,
+  looping: true,
+);
+
+final playerWidget = Chewie(
+  controller: chewieController,
+);
+```
 
-## iOS Warning
+## iOS warning
 
 The video player plugin used by chewie is not functional on iOS simulators. An iOS device must be used during development/testing. Please refer to this [issue](https://github.com/flutter/flutter/issues/14647).
 

+ 2 - 0
analysis_options.yaml

@@ -10,6 +10,8 @@ linter:
     - hash_and_equals
     - iterable_contains_unrelated_type
     - list_remove_unrelated_type
+    - sort_constructors_first
     - test_types_in_equals
+    - unnecessary_new
     - unrelated_type_equality_checks
     - valid_regexps

+ 59 - 27
example/lib/main.dart

@@ -1,4 +1,5 @@
 import 'package:chewie/chewie.dart';
+import 'package:chewie/src/chewie_player.dart';
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
 import 'package:video_player/video_player.dart';
@@ -10,10 +11,10 @@ void main() {
 }
 
 class ChewieDemo extends StatefulWidget {
-  final String title;
-
   ChewieDemo({this.title = 'Chewie Demo'});
 
+  final String title;
+
   @override
   State<StatefulWidget> createState() {
     return _ChewieDemoState();
@@ -22,16 +23,46 @@ class ChewieDemo extends StatefulWidget {
 
 class _ChewieDemoState extends State<ChewieDemo> {
   TargetPlatform _platform;
-  VideoPlayerController _controller;
+  VideoPlayerController _videoPlayerController1;
+  VideoPlayerController _videoPlayerController2;
+  ChewieController _chewieController;
 
   @override
   void initState() {
     super.initState();
-    _controller = VideoPlayerController.network(
-      'https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4',
+    _videoPlayerController1 = VideoPlayerController.network(
+        'https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4');
+    _videoPlayerController2 = VideoPlayerController.network(
+        'https://www.sample-videos.com/video123/mp4/480/big_buck_bunny_480p_20mb.mp4');
+    _chewieController = ChewieController(
+      videoPlayerController: _videoPlayerController1,
+      aspectRatio: 3 / 2,
+      autoPlay: true,
+      looping: true,
+      // Try playing around with some of these other options:
+
+      // showControls: false,
+      // materialProgressColors: ChewieProgressColors(
+      //   playedColor: Colors.red,
+      //   handleColor: Colors.blue,
+      //   backgroundColor: Colors.grey,
+      //   bufferedColor: Colors.lightGreen,
+      // ),
+      // placeholder: Container(
+      //   color: Colors.grey,
+      // ),
+      // autoInitialize: true,
     );
   }
 
+  @override
+  void dispose() {
+    _videoPlayerController1.dispose();
+    _videoPlayerController2.dispose();
+    _chewieController.dispose();
+    super.dispose();
+  }
+
   @override
   Widget build(BuildContext context) {
     return MaterialApp(
@@ -48,35 +79,30 @@ class _ChewieDemoState extends State<ChewieDemo> {
             Expanded(
               child: Center(
                 child: Chewie(
-                  _controller,
-                  aspectRatio: 3 / 2,
-                  autoPlay: true,
-                  looping: true,
-
-                  // Try playing around with some of these other options:
-
-                  // showControls: false,
-                  // materialProgressColors: ChewieProgressColors(
-                  //   playedColor: Colors.red,
-                  //   handleColor: Colors.blue,
-                  //   backgroundColor: Colors.grey,
-                  //   bufferedColor: Colors.lightGreen,
-                  // ),
-                  // placeholder: Container(
-                  //   color: Colors.grey,
-                  // ),
-                  // autoInitialize: true,
+                  controller: _chewieController,
                 ),
               ),
             ),
+            FlatButton(
+              onPressed: () {
+                _chewieController.enterFullScreen();
+              },
+              child: Text('Fullscreen'),
+            ),
             Row(
               children: <Widget>[
                 Expanded(
                   child: FlatButton(
                     onPressed: () {
                       setState(() {
-                        _controller = VideoPlayerController.network(
-                          'https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4',
+                        _chewieController.dispose();
+                        _videoPlayerController2.pause();
+                        _videoPlayerController2.seekTo(Duration(seconds: 0));
+                        _chewieController = ChewieController(
+                          videoPlayerController: _videoPlayerController1,
+                          aspectRatio: 3 / 2,
+                          autoPlay: true,
+                          looping: true,
                         );
                       });
                     },
@@ -90,8 +116,14 @@ class _ChewieDemoState extends State<ChewieDemo> {
                   child: FlatButton(
                     onPressed: () {
                       setState(() {
-                        _controller = VideoPlayerController.network(
-                          'https://www.sample-videos.com/video123/mp4/480/big_buck_bunny_480p_20mb.mp4',
+                        _chewieController.dispose();
+                        _videoPlayerController1.pause();
+                        _videoPlayerController1.seekTo(Duration(seconds: 0));
+                        _chewieController = ChewieController(
+                          videoPlayerController: _videoPlayerController2,
+                          aspectRatio: 3 / 2,
+                          autoPlay: true,
+                          looping: true,
                         );
                       });
                     },

+ 217 - 139
lib/src/chewie_player.dart

@@ -13,8 +13,161 @@ import 'package:video_player/video_player.dart';
 /// `video_player` is pretty low level. Chewie wraps it in a friendly skin to
 /// make it easy to use!
 class Chewie extends StatefulWidget {
-  /// The Controller for the Video you want to play
-  final VideoPlayerController controller;
+  Chewie({
+    Key key,
+    this.controller,
+  })  : assert(controller != null, 'You must provide a chewie controller'),
+        super(key: key);
+
+  /// The [ChewieController]
+  final ChewieController controller;
+
+  @override
+  ChewieState createState() {
+    return ChewieState();
+  }
+}
+
+class ChewieState extends State<Chewie> {
+  bool _isFullScreen = false;
+
+  @override
+  void initState() {
+    super.initState();
+    widget.controller.addListener(listener);
+  }
+
+  @override
+  void dispose() {
+    widget.controller.removeListener(listener);
+    super.dispose();
+  }
+
+  @override
+  void didUpdateWidget(Chewie oldWidget) {
+    if (oldWidget.controller != widget.controller) {
+      widget.controller.addListener(listener);
+    }
+    super.didUpdateWidget(oldWidget);
+  }
+
+  void listener() async {
+    if (widget.controller.isFullScreen && !_isFullScreen) {
+      _isFullScreen = true;
+      await _pushFullScreenWidget(context);
+    } else if (_isFullScreen) {
+      Navigator.of(context).pop();
+      _isFullScreen = false;
+    }
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return _ChewieControllerProvider(
+      controller: widget.controller,
+      child: PlayerWithControls(),
+    );
+  }
+
+  Widget _buildFullScreenVideo(
+      BuildContext context, Animation<double> animation) {
+    return Scaffold(
+      resizeToAvoidBottomPadding: false,
+      body: Container(
+        alignment: Alignment.center,
+        color: Colors.black,
+        child: _ChewieControllerProvider(
+          controller: widget.controller,
+          child: PlayerWithControls(),
+        ),
+      ),
+    );
+  }
+
+  Widget _fullScreenRoutePageBuilder(
+    BuildContext context,
+    Animation<double> animation,
+    Animation<double> secondaryAnimation,
+  ) {
+    return AnimatedBuilder(
+      animation: animation,
+      builder: (BuildContext context, Widget child) {
+        return _buildFullScreenVideo(context, animation);
+      },
+    );
+  }
+
+  Future<dynamic> _pushFullScreenWidget(BuildContext context) async {
+    final isAndroid = Theme.of(context).platform == TargetPlatform.android;
+    final TransitionRoute<Null> route = PageRouteBuilder<Null>(
+      settings: RouteSettings(isInitialRoute: false),
+      pageBuilder: _fullScreenRoutePageBuilder,
+    );
+
+    SystemChrome.setEnabledSystemUIOverlays([]);
+    if (isAndroid) {
+      SystemChrome.setPreferredOrientations([
+        DeviceOrientation.landscapeLeft,
+        DeviceOrientation.landscapeRight,
+      ]);
+    }
+
+    if (!widget.controller.allowedScreenSleep) {
+      Screen.keepOn(true);
+    }
+
+    await Navigator.of(context).push(route);
+    _isFullScreen = false;
+    widget.controller.exitFullScreen();
+
+    bool isKeptOn = await Screen.isKeptOn;
+    if (isKeptOn) {
+      Screen.keepOn(false);
+    }
+
+    SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values);
+    SystemChrome.setPreferredOrientations([
+      DeviceOrientation.portraitUp,
+      DeviceOrientation.portraitDown,
+      DeviceOrientation.landscapeLeft,
+      DeviceOrientation.landscapeRight,
+    ]);
+  }
+}
+
+/// The ChewieController is used to configure and drive the Chewie Player
+/// Widgets. It provides methods to control playback, such as [pause] and
+/// [play], as well as methods that control the visual appearance of the player,
+/// such as [enterFullScreen] or [exitFullScreen].
+///
+/// In addition, you can listen to the ChewieController for presentational
+/// changes, such as entering and exiting full screen mode. To listen for
+/// changes to the playback, such as a change to the seek position of the
+/// player, please use the standard information provided by the
+/// `VideoPlayerController`.
+class ChewieController extends ChangeNotifier {
+  ChewieController({
+    this.videoPlayerController,
+    this.aspectRatio,
+    this.autoInitialize = false,
+    this.autoPlay = false,
+    this.startAt,
+    this.looping = false,
+    this.fullScreenByDefault = false,
+    this.cupertinoProgressColors,
+    this.materialProgressColors,
+    this.placeholder,
+    this.showControls = true,
+    this.customControls,
+    this.allowedScreenSleep = true,
+    this.isLive = false,
+  }) : assert(videoPlayerController != null,
+            'You must provide a controller to play a video') {
+    _initialize();
+  }
+
+  /// The controller for the video you want to play
+  final VideoPlayerController videoPlayerController;
 
   /// Initialize the Video on Startup. This will prep the video for playback.
   final bool autoInitialize;
@@ -31,6 +184,10 @@ class Chewie extends StatefulWidget {
   /// Whether or not to show the controls
   final bool showControls;
 
+  /// Defines customised controls. Check [MaterialControls] or
+  /// [CupertinoControls] for reference.
+  final Widget customControls;
+
   /// The Aspect Ratio of the Video. Important to get the correct size of the
   /// video!
   ///
@@ -58,173 +215,94 @@ class Chewie extends StatefulWidget {
   /// Defines if the controls should be for live stream video
   final bool isLive;
 
-  Chewie(
-    this.controller, {
-    Key key,
-    this.aspectRatio,
-    this.autoInitialize = false,
-    this.autoPlay = false,
-    this.startAt,
-    this.looping = false,
-    this.fullScreenByDefault = false,
-    this.cupertinoProgressColors,
-    this.materialProgressColors,
-    this.placeholder,
-    this.showControls = true,
-    this.allowedScreenSleep = true,
-    this.isLive = false,
-  })  : assert(controller != null,
-            'You must provide a controller to play a video'),
-        super(key: key);
+  static ChewieController of(BuildContext context) {
+    final _ChewieControllerProvider chewieControllerProvider =
+        context.inheritFromWidgetOfExactType(_ChewieControllerProvider);
 
-  @override
-  State<StatefulWidget> createState() {
-    return new _ChewiePlayerState();
+    return chewieControllerProvider.controller;
   }
-}
 
-class _ChewiePlayerState extends State<Chewie> {
-  VideoPlayerController _controller;
   bool _isFullScreen = false;
 
-  @override
-  Widget build(BuildContext context) {
-    return new PlayerWithControls(
-      controller: _controller,
-      onExpandCollapse: () => _pushFullScreenWidget(context),
-      aspectRatio: widget.aspectRatio ?? _calculateAspectRatio(context),
-      cupertinoProgressColors: widget.cupertinoProgressColors,
-      materialProgressColors: widget.materialProgressColors,
-      placeholder: widget.placeholder,
-      autoPlay: widget.autoPlay,
-      showControls: widget.showControls,
-      isLive: widget.isLive,
-    );
-  }
-
-  @override
-  void initState() {
-    super.initState();
-    _controller = widget.controller;
-    _initialize();
-  }
-
-  Widget _buildFullScreenVideo(
-      BuildContext context, Animation<double> animation) {
-    return new Scaffold(
-      resizeToAvoidBottomPadding: false,
-      body: new Container(
-        alignment: Alignment.center,
-        color: Colors.black,
-        child: new PlayerWithControls(
-          controller: _controller,
-          onExpandCollapse: () =>
-              new Future<dynamic>.value(Navigator.of(context).pop()),
-          aspectRatio: widget.aspectRatio ?? _calculateAspectRatio(context),
-          fullScreen: true,
-          isLive: widget.isLive,
-          cupertinoProgressColors: widget.cupertinoProgressColors,
-          materialProgressColors: widget.materialProgressColors,
-        ),
-      ),
-    );
-  }
-
-  Widget _fullScreenRoutePageBuilder(
-    BuildContext context,
-    Animation<double> animation,
-    Animation<double> secondaryAnimation,
-  ) {
-    return new AnimatedBuilder(
-      animation: animation,
-      builder: (BuildContext context, Widget child) {
-        return _buildFullScreenVideo(context, animation);
-      },
-    );
-  }
+  bool get isFullScreen => _isFullScreen;
 
   Future _initialize() async {
-    await _controller.setLooping(widget.looping);
+    await videoPlayerController.setLooping(looping);
 
-    if (widget.autoInitialize || widget.autoPlay) {
-      await _controller.initialize();
+    if ((autoInitialize || autoPlay) &&
+        !videoPlayerController.value.initialized) {
+      await videoPlayerController.initialize();
     }
 
-    if (widget.autoPlay) {
-      if (widget.fullScreenByDefault) {
-        _isFullScreen = true;
-        await _pushFullScreenWidget(context);
+    if (autoPlay) {
+      if (fullScreenByDefault) {
+        enterFullScreen();
       }
 
-      await _controller.play();
+      await videoPlayerController.play();
     }
 
-    if (widget.startAt != null) {
-      await _controller.seekTo(widget.startAt);
+    if (startAt != null) {
+      await videoPlayerController.seekTo(startAt);
     }
 
-    if (widget.fullScreenByDefault) {
-      widget.controller.addListener(() async {
-        if (await widget.controller.value.isPlaying && !_isFullScreen) {
-          _isFullScreen = true;
-          await _pushFullScreenWidget(context);
+    if (fullScreenByDefault) {
+      videoPlayerController.addListener(() async {
+        if (await videoPlayerController.value.isPlaying && !_isFullScreen) {
+          enterFullScreen();
         }
       });
     }
   }
 
-  @override
-  void didUpdateWidget(Chewie oldWidget) {
-    super.didUpdateWidget(oldWidget);
+  void enterFullScreen() {
+    _isFullScreen = true;
+    notifyListeners();
+  }
 
-    if (widget.controller.dataSource != _controller.dataSource) {
-      _controller.dispose();
-      _controller = widget.controller;
-      _isFullScreen = false;
-      _initialize();
-    }
+  void exitFullScreen() {
+    _isFullScreen = false;
+    notifyListeners();
   }
 
-  Future<dynamic> _pushFullScreenWidget(BuildContext context) async {
-    final isAndroid = Theme.of(context).platform == TargetPlatform.android;
-    final TransitionRoute<Null> route = new PageRouteBuilder<Null>(
-      settings: new RouteSettings(isInitialRoute: false),
-      pageBuilder: _fullScreenRoutePageBuilder,
-    );
+  void toggleFullScreen() {
+    _isFullScreen = !_isFullScreen;
+    notifyListeners();
+  }
 
-    SystemChrome.setEnabledSystemUIOverlays([]);
-    if (isAndroid) {
-      SystemChrome.setPreferredOrientations([
-        DeviceOrientation.landscapeLeft,
-        DeviceOrientation.landscapeRight,
-      ]);
-    }
+  Future<void> play() async {
+    await videoPlayerController.play();
+  }
 
-    if (!widget.allowedScreenSleep) {
-      Screen.keepOn(true);
-    }
+  Future<void> setLooping(bool looping) async {
+    await videoPlayerController.setLooping(looping);
+  }
 
-    await Navigator.of(context).push(route);
+  Future<void> pause() async {
+    await videoPlayerController.pause();
+  }
 
-    bool isKeptOn = await Screen.isKeptOn;
-    if (isKeptOn) {
-      Screen.keepOn(false);
-    }
+  Future<void> seekTo(Duration moment) async {
+    await videoPlayerController.seekTo(moment);
+  }
 
-    SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values);
-    SystemChrome.setPreferredOrientations([
-      DeviceOrientation.portraitUp,
-      DeviceOrientation.portraitDown,
-      DeviceOrientation.landscapeLeft,
-      DeviceOrientation.landscapeRight,
-    ]);
+  Future<void> setVolume(double volume) async {
+    await videoPlayerController.setVolume(volume);
   }
+}
+
+class _ChewieControllerProvider extends InheritedWidget {
+  const _ChewieControllerProvider({
+    Key key,
+    @required this.controller,
+    @required Widget child,
+  })  : assert(controller != null),
+        assert(child != null),
+        super(key: key, child: child);
 
-  double _calculateAspectRatio(BuildContext context) {
-    final size = MediaQuery.of(context).size;
-    final width = size.width;
-    final height = size.height;
+  final ChewieController controller;
 
-    return width > height ? width / height : height / width;
-  }
+  @override
+  bool updateShouldNotify(_ChewieControllerProvider old) =>
+      controller != old.controller;
 }

+ 9 - 9
lib/src/chewie_progress_colors.dart

@@ -1,18 +1,18 @@
 import 'package:flutter/rendering.dart';
 
 class ChewieProgressColors {
-  final Paint playedPaint;
-  final Paint bufferedPaint;
-  final Paint handlePaint;
-  final Paint backgroundPaint;
-
   ChewieProgressColors({
     Color playedColor: const Color.fromRGBO(255, 0, 0, 0.7),
     Color bufferedColor: const Color.fromRGBO(30, 30, 200, 0.2),
     Color handleColor: const Color.fromRGBO(200, 200, 200, 1.0),
     Color backgroundColor: const Color.fromRGBO(200, 200, 200, 0.5),
-  })  : playedPaint = new Paint()..color = playedColor,
-        bufferedPaint = new Paint()..color = bufferedColor,
-        handlePaint = new Paint()..color = handleColor,
-        backgroundPaint = new Paint()..color = backgroundColor;
+  })  : playedPaint = Paint()..color = playedColor,
+        bufferedPaint = Paint()..color = bufferedColor,
+        handlePaint = Paint()..color = handleColor,
+        backgroundPaint = Paint()..color = backgroundColor;
+
+  final Paint playedPaint;
+  final Paint bufferedPaint;
+  final Paint handlePaint;
+  final Paint backgroundPaint;
 }

+ 130 - 148
lib/src/cupertino_controls.dart

@@ -2,6 +2,7 @@ import 'dart:async';
 import 'dart:math' as math;
 import 'dart:ui' as ui;
 
+import 'package:chewie/src/chewie_player.dart';
 import 'package:chewie/src/chewie_progress_colors.dart';
 import 'package:chewie/src/cupertino_progress_bar.dart';
 import 'package:chewie/src/utils.dart';
@@ -11,29 +12,17 @@ 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;
-  final ChewieProgressColors progressColors;
-  final bool autoPlay;
-  final bool isLive;
-
   CupertinoControls({
     @required this.backgroundColor,
     @required this.iconColor,
-    @required this.controller,
-    @required this.onExpandCollapse,
-    @required this.fullScreen,
-    @required this.progressColors,
-    @required this.autoPlay,
-    @required this.isLive,
   });
 
+  final Color backgroundColor;
+  final Color iconColor;
+
   @override
   State<StatefulWidget> createState() {
-    return new _CupertinoControlsState();
+    return _CupertinoControlsState();
   }
 }
 
@@ -46,21 +35,24 @@ class _CupertinoControlsState extends State<CupertinoControls> {
   Timer _expandCollapseTimer;
   Timer _initTimer;
 
+  VideoPlayerController controller;
+  ChewieController chewieController;
+
   @override
   Widget build(BuildContext context) {
     final backgroundColor = widget.backgroundColor;
     final iconColor = widget.iconColor;
-    final controller = widget.controller;
+    chewieController = ChewieController.of(context);
+    controller = chewieController.videoPlayerController;
     final orientation = MediaQuery.of(context).orientation;
     final barHeight = orientation == Orientation.portrait ? 30.0 : 47.0;
     final buttonPadding = orientation == Orientation.portrait ? 16.0 : 24.0;
 
-    return new Column(
+    return Column(
       children: <Widget>[
-        _buildTopBar(
-            backgroundColor, iconColor, controller, barHeight, buttonPadding),
+        _buildTopBar(backgroundColor, iconColor, barHeight, buttonPadding),
         _buildHitArea(),
-        _buildBottomBar(backgroundColor, iconColor, controller, barHeight),
+        _buildBottomBar(backgroundColor, iconColor, barHeight),
       ],
     );
   }
@@ -72,57 +64,53 @@ class _CupertinoControlsState extends State<CupertinoControls> {
   }
 
   void _dispose() {
-    widget.controller.removeListener(_updateState);
+    controller.removeListener(_updateState);
     _hideTimer?.cancel();
     _expandCollapseTimer?.cancel();
     _initTimer?.cancel();
   }
 
   @override
-  void initState() {
-    _initialize();
+  void didChangeDependencies() {
+    final _oldController = chewieController;
+    chewieController = ChewieController.of(context);
+    controller = chewieController.videoPlayerController;
 
-    super.initState();
-  }
-
-  @override
-  void didUpdateWidget(CupertinoControls oldWidget) {
-    super.didUpdateWidget(oldWidget);
-
-    if (widget.controller.dataSource != oldWidget.controller.dataSource) {
+    if (_oldController != chewieController) {
       _dispose();
       _initialize();
     }
+
+    super.didChangeDependencies();
   }
 
   AnimatedOpacity _buildBottomBar(
     Color backgroundColor,
     Color iconColor,
-    VideoPlayerController controller,
     double barHeight,
   ) {
-    return new AnimatedOpacity(
+    return AnimatedOpacity(
       opacity: _hideStuff ? 0.0 : 1.0,
-      duration: new Duration(milliseconds: 300),
-      child: new Container(
+      duration: Duration(milliseconds: 300),
+      child: Container(
         color: Colors.transparent,
         alignment: Alignment.bottomCenter,
-        margin: new EdgeInsets.all(marginSize),
-        child: new ClipRect(
-          child: new BackdropFilter(
-            filter: new ui.ImageFilter.blur(
+        margin: EdgeInsets.all(marginSize),
+        child: ClipRect(
+          child: BackdropFilter(
+            filter: ui.ImageFilter.blur(
               sigmaX: 10.0,
               sigmaY: 10.0,
             ),
-            child: new Container(
+            child: Container(
               height: barHeight,
-              decoration: new BoxDecoration(
+              decoration: BoxDecoration(
                 color: backgroundColor,
-                borderRadius: new BorderRadius.all(
-                  new Radius.circular(10.0),
+                borderRadius: BorderRadius.all(
+                  Radius.circular(10.0),
                 ),
               ),
-              child: widget.isLive
+              child: chewieController.isLive
                   ? Row(
                       mainAxisAlignment: MainAxisAlignment.spaceBetween,
                       children: <Widget>[
@@ -130,7 +118,7 @@ class _CupertinoControlsState extends State<CupertinoControls> {
                         _buildLive(iconColor),
                       ],
                     )
-                  : new Row(
+                  : Row(
                       children: <Widget>[
                         _buildSkipBack(iconColor, barHeight),
                         _buildPlayPause(controller, iconColor, barHeight),
@@ -148,11 +136,11 @@ class _CupertinoControlsState extends State<CupertinoControls> {
   }
 
   Widget _buildLive(Color iconColor) {
-    return new Padding(
-      padding: new EdgeInsets.only(right: 12.0),
-      child: new Text(
+    return Padding(
+      padding: EdgeInsets.only(right: 12.0),
+      child: Text(
         'LIVE',
-        style: new TextStyle(color: iconColor, fontSize: 12.0),
+        style: TextStyle(color: iconColor, fontSize: 12.0),
       ),
     );
   }
@@ -163,29 +151,29 @@ class _CupertinoControlsState extends State<CupertinoControls> {
     double barHeight,
     double buttonPadding,
   ) {
-    return new GestureDetector(
+    return GestureDetector(
       onTap: _onExpandCollapse,
-      child: new AnimatedOpacity(
+      child: AnimatedOpacity(
         opacity: _hideStuff ? 0.0 : 1.0,
-        duration: new Duration(milliseconds: 300),
-        child: new ClipRect(
-          child: new BackdropFilter(
-            filter: new ui.ImageFilter.blur(sigmaX: 10.0),
-            child: new Container(
+        duration: Duration(milliseconds: 300),
+        child: ClipRect(
+          child: BackdropFilter(
+            filter: ui.ImageFilter.blur(sigmaX: 10.0),
+            child: Container(
               height: barHeight,
-              padding: new EdgeInsets.only(
+              padding: EdgeInsets.only(
                 left: buttonPadding,
                 right: buttonPadding,
               ),
-              decoration: new BoxDecoration(
+              decoration: BoxDecoration(
                 color: backgroundColor,
-                borderRadius: new BorderRadius.all(
-                  new Radius.circular(10.0),
+                borderRadius: BorderRadius.all(
+                  Radius.circular(10.0),
                 ),
               ),
-              child: new Center(
-                child: new Icon(
-                  widget.fullScreen
+              child: Center(
+                child: Icon(
+                  chewieController.isFullScreen
                       ? OpenIconicIcons.fullscreenExit
                       : OpenIconicIcons.fullscreenEnter,
                   color: iconColor,
@@ -200,8 +188,8 @@ class _CupertinoControlsState extends State<CupertinoControls> {
   }
 
   Expanded _buildHitArea() {
-    return new Expanded(
-      child: new GestureDetector(
+    return Expanded(
+      child: GestureDetector(
         onTap: _latestValue != null && _latestValue.isPlaying
             ? _cancelAndRestartTimer
             : () {
@@ -211,7 +199,7 @@ class _CupertinoControlsState extends State<CupertinoControls> {
                   _hideStuff = false;
                 });
               },
-        child: new Container(
+        child: Container(
           color: Colors.transparent,
         ),
       ),
@@ -225,7 +213,7 @@ class _CupertinoControlsState extends State<CupertinoControls> {
     double barHeight,
     double buttonPadding,
   ) {
-    return new GestureDetector(
+    return GestureDetector(
       onTap: () {
         _cancelAndRestartTimer();
 
@@ -236,26 +224,26 @@ class _CupertinoControlsState extends State<CupertinoControls> {
           controller.setVolume(0.0);
         }
       },
-      child: new AnimatedOpacity(
+      child: AnimatedOpacity(
         opacity: _hideStuff ? 0.0 : 1.0,
-        duration: new Duration(milliseconds: 300),
-        child: new ClipRect(
-          child: new BackdropFilter(
-            filter: new ui.ImageFilter.blur(sigmaX: 10.0),
-            child: new Container(
-              decoration: new BoxDecoration(
+        duration: Duration(milliseconds: 300),
+        child: ClipRect(
+          child: BackdropFilter(
+            filter: ui.ImageFilter.blur(sigmaX: 10.0),
+            child: Container(
+              decoration: BoxDecoration(
                 color: backgroundColor,
-                borderRadius: new BorderRadius.all(
-                  new Radius.circular(10.0),
+                borderRadius: BorderRadius.all(
+                  Radius.circular(10.0),
                 ),
               ),
-              child: new Container(
+              child: Container(
                 height: barHeight,
-                padding: new EdgeInsets.only(
+                padding: EdgeInsets.only(
                   left: buttonPadding,
                   right: buttonPadding,
                 ),
-                child: new Icon(
+                child: Icon(
                   (_latestValue != null && _latestValue.volume > 0)
                       ? Icons.volume_up
                       : Icons.volume_off,
@@ -275,16 +263,16 @@ class _CupertinoControlsState extends State<CupertinoControls> {
     Color iconColor,
     double barHeight,
   ) {
-    return new GestureDetector(
+    return GestureDetector(
       onTap: _playPause,
-      child: new Container(
+      child: Container(
         height: barHeight,
         color: Colors.transparent,
-        padding: new EdgeInsets.only(
+        padding: EdgeInsets.only(
           left: 6.0,
           right: 6.0,
         ),
-        child: new Icon(
+        child: Icon(
           controller.value.isPlaying
               ? OpenIconicIcons.mediaPause
               : OpenIconicIcons.mediaPlay,
@@ -297,13 +285,13 @@ class _CupertinoControlsState extends State<CupertinoControls> {
 
   Widget _buildPosition(Color iconColor) {
     final position =
-        _latestValue != null ? _latestValue.position : new Duration(seconds: 0);
+        _latestValue != null ? _latestValue.position : Duration(seconds: 0);
 
-    return new Padding(
-      padding: new EdgeInsets.only(right: 12.0),
-      child: new Text(
+    return Padding(
+      padding: EdgeInsets.only(right: 12.0),
+      child: Text(
         formatDuration(position),
-        style: new TextStyle(
+        style: TextStyle(
           color: iconColor,
           fontSize: 12.0,
         ),
@@ -314,34 +302,34 @@ class _CupertinoControlsState extends State<CupertinoControls> {
   Widget _buildRemaining(Color iconColor) {
     final position = _latestValue != null && _latestValue.duration != null
         ? _latestValue.duration - _latestValue.position
-        : new Duration(seconds: 0);
+        : Duration(seconds: 0);
 
-    return new Padding(
-      padding: new EdgeInsets.only(right: 12.0),
-      child: new Text(
+    return Padding(
+      padding: EdgeInsets.only(right: 12.0),
+      child: Text(
         '-${formatDuration(position)}',
-        style: new TextStyle(color: iconColor, fontSize: 12.0),
+        style: TextStyle(color: iconColor, fontSize: 12.0),
       ),
     );
   }
 
   GestureDetector _buildSkipBack(Color iconColor, double barHeight) {
-    return new GestureDetector(
+    return GestureDetector(
       onTap: _skipBack,
-      child: new Container(
+      child: Container(
         height: barHeight,
         color: Colors.transparent,
-        margin: new EdgeInsets.only(left: 10.0),
-        padding: new EdgeInsets.only(
+        margin: EdgeInsets.only(left: 10.0),
+        padding: EdgeInsets.only(
           left: 6.0,
           right: 6.0,
         ),
-        child: new Transform(
+        child: Transform(
           alignment: Alignment.center,
-          transform: new Matrix4.skewY(0.0)
+          transform: Matrix4.skewY(0.0)
             ..rotateX(math.pi)
             ..rotateZ(math.pi),
-          child: new Icon(
+          child: Icon(
             OpenIconicIcons.reload,
             color: iconColor,
             size: 12.0,
@@ -352,19 +340,19 @@ class _CupertinoControlsState extends State<CupertinoControls> {
   }
 
   GestureDetector _buildSkipForward(Color iconColor, double barHeight) {
-    return new GestureDetector(
+    return GestureDetector(
       onTap: _skipForward,
-      child: new Container(
+      child: Container(
         height: barHeight,
         color: Colors.transparent,
-        padding: new EdgeInsets.only(
+        padding: EdgeInsets.only(
           left: 6.0,
           right: 8.0,
         ),
-        margin: new EdgeInsets.only(
+        margin: EdgeInsets.only(
           right: 8.0,
         ),
-        child: new Icon(
+        child: Icon(
           OpenIconicIcons.reload,
           color: iconColor,
           size: 12.0,
@@ -376,22 +364,21 @@ class _CupertinoControlsState extends State<CupertinoControls> {
   Widget _buildTopBar(
     Color backgroundColor,
     Color iconColor,
-    VideoPlayerController controller,
     double barHeight,
     double buttonPadding,
   ) {
-    return new Container(
+    return Container(
       height: barHeight,
-      margin: new EdgeInsets.only(
+      margin: EdgeInsets.only(
         top: marginSize,
         right: marginSize,
         left: marginSize,
       ),
-      child: new Row(
+      child: Row(
         children: <Widget>[
           _buildExpandButton(
               backgroundColor, iconColor, barHeight, buttonPadding),
-          new Expanded(child: new Container()),
+          Expanded(child: Container()),
           _buildMuteButton(
               controller, backgroundColor, iconColor, barHeight, buttonPadding),
         ],
@@ -410,17 +397,16 @@ class _CupertinoControlsState extends State<CupertinoControls> {
   }
 
   Future<Null> _initialize() async {
-    widget.controller.addListener(_updateState);
+    controller.addListener(_updateState);
 
     _updateState();
 
-    if ((widget.controller.value != null &&
-            widget.controller.value.isPlaying) ||
-        widget.autoPlay) {
+    if ((controller.value != null && controller.value.isPlaying) ||
+        chewieController.autoPlay) {
       _startHideTimer();
     }
 
-    _initTimer = new Timer(new Duration(milliseconds: 200), () {
+    _initTimer = Timer(Duration(milliseconds: 200), () {
       setState(() {
         _hideStuff = false;
       });
@@ -431,49 +417,48 @@ class _CupertinoControlsState extends State<CupertinoControls> {
     setState(() {
       _hideStuff = true;
 
-      widget.onExpandCollapse().then((dynamic _) {
-        _expandCollapseTimer = new Timer(new Duration(milliseconds: 300), () {
-          setState(() {
-            _cancelAndRestartTimer();
-          });
+      chewieController.toggleFullScreen();
+      _expandCollapseTimer = Timer(Duration(milliseconds: 300), () {
+        setState(() {
+          _cancelAndRestartTimer();
         });
       });
     });
   }
 
   Widget _buildProgressBar() {
-    return new Expanded(
-      child: new Padding(
-        padding: new EdgeInsets.only(right: 12.0),
-        child: new CupertinoVideoProgressBar(
-          widget.controller,
+    return Expanded(
+      child: Padding(
+        padding: EdgeInsets.only(right: 12.0),
+        child: CupertinoVideoProgressBar(
+          controller,
           onDragStart: () {
             _hideTimer?.cancel();
           },
           onDragEnd: () {
             _startHideTimer();
           },
-          colors: widget.progressColors ??
-              new ChewieProgressColors(
-                playedColor: new Color.fromARGB(
+          colors: chewieController.cupertinoProgressColors ??
+              ChewieProgressColors(
+                playedColor: Color.fromARGB(
                   120,
                   255,
                   255,
                   255,
                 ),
-                handleColor: new Color.fromARGB(
+                handleColor: Color.fromARGB(
                   255,
                   255,
                   255,
                   255,
                 ),
-                bufferedColor: new Color.fromARGB(
+                bufferedColor: Color.fromARGB(
                   60,
                   255,
                   255,
                   255,
                 ),
-                backgroundColor: new Color.fromARGB(
+                backgroundColor: Color.fromARGB(
                   20,
                   255,
                   255,
@@ -487,19 +472,19 @@ class _CupertinoControlsState extends State<CupertinoControls> {
 
   void _playPause() {
     setState(() {
-      if (widget.controller.value.isPlaying) {
+      if (controller.value.isPlaying) {
         _hideStuff = false;
         _hideTimer?.cancel();
-        widget.controller.pause();
+        controller.pause();
       } else {
         _cancelAndRestartTimer();
 
-        if (!widget.controller.value.initialized) {
-          widget.controller.initialize().then((_) {
-            widget.controller.play();
+        if (!controller.value.initialized) {
+          controller.initialize().then((_) {
+            controller.play();
           });
         } else {
-          widget.controller.play();
+          controller.play();
         }
       }
     });
@@ -507,23 +492,20 @@ class _CupertinoControlsState extends State<CupertinoControls> {
 
   void _skipBack() {
     _cancelAndRestartTimer();
-    final beginning = new Duration(seconds: 0).inMilliseconds;
-    final skip =
-        (_latestValue.position - new Duration(seconds: 15)).inMilliseconds;
-    widget.controller
-        .seekTo(new Duration(milliseconds: math.max(skip, beginning)));
+    final beginning = Duration(seconds: 0).inMilliseconds;
+    final skip = (_latestValue.position - Duration(seconds: 15)).inMilliseconds;
+    controller.seekTo(Duration(milliseconds: math.max(skip, beginning)));
   }
 
   void _skipForward() {
     _cancelAndRestartTimer();
     final end = _latestValue.duration.inMilliseconds;
-    final skip =
-        (_latestValue.position + new Duration(seconds: 15)).inMilliseconds;
-    widget.controller.seekTo(new Duration(milliseconds: math.min(skip, end)));
+    final skip = (_latestValue.position + Duration(seconds: 15)).inMilliseconds;
+    controller.seekTo(Duration(milliseconds: math.min(skip, end)));
   }
 
   void _startHideTimer() {
-    _hideTimer = new Timer(const Duration(seconds: 3), () {
+    _hideTimer = Timer(const Duration(seconds: 3), () {
       setState(() {
         _hideStuff = true;
       });
@@ -532,7 +514,7 @@ class _CupertinoControlsState extends State<CupertinoControls> {
 
   void _updateState() {
     setState(() {
-      _latestValue = widget.controller.value;
+      _latestValue = controller.value;
     });
   }
 }

+ 38 - 39
lib/src/cupertino_progress_bar.dart

@@ -4,37 +4,36 @@ import 'package:flutter/widgets.dart';
 import 'package:video_player/video_player.dart';
 
 class CupertinoVideoProgressBar extends StatefulWidget {
-  final VideoPlayerController controller;
-  final ChewieProgressColors colors;
-  final Function() onDragStart;
-  final Function() onDragEnd;
-  final Function() onDragUpdate;
-
   CupertinoVideoProgressBar(
     this.controller, {
     ChewieProgressColors colors,
     this.onDragEnd,
     this.onDragStart,
     this.onDragUpdate,
-  }) : colors = colors ?? new ChewieProgressColors();
+  }) : colors = colors ?? ChewieProgressColors();
+
+  final VideoPlayerController controller;
+  final ChewieProgressColors colors;
+  final Function() onDragStart;
+  final Function() onDragEnd;
+  final Function() onDragUpdate;
 
   @override
   _VideoProgressBarState createState() {
-    return new _VideoProgressBarState();
+    return _VideoProgressBarState();
   }
 }
 
 class _VideoProgressBarState extends State<CupertinoVideoProgressBar> {
-  VoidCallback listener;
-
-  bool _controllerWasPlaying = false;
-
   _VideoProgressBarState() {
     listener = () {
       setState(() {});
     };
   }
 
+  VoidCallback listener;
+  bool _controllerWasPlaying = false;
+
   VideoPlayerController get controller => widget.controller;
 
   @override
@@ -59,16 +58,16 @@ class _VideoProgressBarState extends State<CupertinoVideoProgressBar> {
       controller.seekTo(position);
     }
 
-    return new GestureDetector(
+    return GestureDetector(
       child: (controller.value.hasError)
-          ? new Text(controller.value.errorDescription)
-          : new Center(
-              child: new Container(
+          ? Text(controller.value.errorDescription)
+          : Center(
+              child: Container(
                 height: MediaQuery.of(context).size.height,
                 width: MediaQuery.of(context).size.width,
                 color: Colors.transparent,
-                child: new CustomPaint(
-                  painter: new _ProgressBarPainter(
+                child: CustomPaint(
+                  painter: _ProgressBarPainter(
                     controller.value,
                     widget.colors,
                   ),
@@ -118,11 +117,11 @@ class _VideoProgressBarState extends State<CupertinoVideoProgressBar> {
 }
 
 class _ProgressBarPainter extends CustomPainter {
+  _ProgressBarPainter(this.value, this.colors);
+
   VideoPlayerValue value;
   ChewieProgressColors colors;
 
-  _ProgressBarPainter(this.value, this.colors);
-
   @override
   bool shouldRepaint(CustomPainter painter) {
     return true;
@@ -135,12 +134,12 @@ class _ProgressBarPainter extends CustomPainter {
     final baseOffset = size.height / 2 - barHeight / 2.0;
 
     canvas.drawRRect(
-      new RRect.fromRectAndRadius(
-        new Rect.fromPoints(
-          new Offset(0.0, baseOffset),
-          new Offset(size.width, baseOffset + barHeight),
+      RRect.fromRectAndRadius(
+        Rect.fromPoints(
+          Offset(0.0, baseOffset),
+          Offset(size.width, baseOffset + barHeight),
         ),
-        new Radius.circular(4.0),
+        Radius.circular(4.0),
       ),
       colors.backgroundPaint,
     );
@@ -154,35 +153,35 @@ class _ProgressBarPainter extends CustomPainter {
       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, baseOffset),
-            new Offset(end, baseOffset + barHeight),
+        RRect.fromRectAndRadius(
+          Rect.fromPoints(
+            Offset(start, baseOffset),
+            Offset(end, baseOffset + barHeight),
           ),
-          new Radius.circular(4.0),
+          Radius.circular(4.0),
         ),
         colors.bufferedPaint,
       );
     }
     canvas.drawRRect(
-      new RRect.fromRectAndRadius(
-        new Rect.fromPoints(
-          new Offset(0.0, baseOffset),
-          new Offset(playedPart, baseOffset + barHeight),
+      RRect.fromRectAndRadius(
+        Rect.fromPoints(
+          Offset(0.0, baseOffset),
+          Offset(playedPart, baseOffset + barHeight),
         ),
-        new Radius.circular(4.0),
+        Radius.circular(4.0),
       ),
       colors.playedPaint,
     );
 
-    final shadowPath = new Path()
-      ..addOval(new Rect.fromCircle(
-          center: new Offset(playedPart, baseOffset + barHeight / 2),
+    final shadowPath = Path()
+      ..addOval(Rect.fromCircle(
+          center: Offset(playedPart, baseOffset + barHeight / 2),
           radius: handleHeight));
 
     canvas.drawShadow(shadowPath, Colors.black, 0.2, false);
     canvas.drawCircle(
-      new Offset(playedPart, baseOffset + barHeight / 2),
+      Offset(playedPart, baseOffset + barHeight / 2),
       handleHeight,
       colors.handlePaint,
     );

+ 86 - 101
lib/src/material_controls.dart

@@ -1,32 +1,18 @@
 import 'dart:async';
 
+import 'package:chewie/src/chewie_player.dart';
 import 'package:chewie/src/chewie_progress_colors.dart';
 import 'package:chewie/src/material_progress_bar.dart';
 import 'package:chewie/src/utils.dart';
-import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:video_player/video_player.dart';
 
 class MaterialControls extends StatefulWidget {
-  final VideoPlayerController controller;
-  final bool fullScreen;
-  final Future<dynamic> Function() onExpandCollapse;
-  final ChewieProgressColors progressColors;
-  final bool autoPlay;
-  final bool isLive;
-
-  MaterialControls({
-    @required this.controller,
-    @required this.fullScreen,
-    @required this.onExpandCollapse,
-    @required this.progressColors,
-    @required this.autoPlay,
-    @required this.isLive,
-  });
+  MaterialControls({Key key}) : super(key: key);
 
   @override
   State<StatefulWidget> createState() {
-    return new _MaterialControlsState();
+    return _MaterialControlsState();
   }
 }
 
@@ -42,9 +28,12 @@ class _MaterialControlsState extends State<MaterialControls> {
   final barHeight = 48.0;
   final marginSize = 5.0;
 
+  VideoPlayerController controller;
+  ChewieController chewieController;
+
   @override
   Widget build(BuildContext context) {
-    return new Column(
+    return Column(
       children: <Widget>[
         _latestValue != null &&
                     !_latestValue.isPlaying &&
@@ -56,7 +45,7 @@ class _MaterialControlsState extends State<MaterialControls> {
                 ),
               )
             : _buildHitArea(),
-        _buildBottomBar(context, widget.controller),
+        _buildBottomBar(context),
       ],
     );
   }
@@ -68,47 +57,44 @@ class _MaterialControlsState extends State<MaterialControls> {
   }
 
   void _dispose() {
-    widget.controller.removeListener(_updateState);
+    controller.removeListener(_updateState);
     _hideTimer?.cancel();
     _showTimer?.cancel();
     _showAfterExpandCollapseTimer?.cancel();
   }
 
   @override
-  void initState() {
-    _initialize();
-
-    super.initState();
-  }
+  void didChangeDependencies() {
+    final _oldController = chewieController;
+    chewieController = ChewieController.of(context);
+    controller = chewieController.videoPlayerController;
 
-  @override
-  void didUpdateWidget(MaterialControls oldWidget) {
-    super.didUpdateWidget(oldWidget);
-    if (widget.controller.dataSource != oldWidget.controller.dataSource) {
+    if (_oldController != chewieController) {
       _dispose();
       _initialize();
     }
+
+    super.didChangeDependencies();
   }
 
   AnimatedOpacity _buildBottomBar(
     BuildContext context,
-    VideoPlayerController controller,
   ) {
     final iconColor = Theme.of(context).textTheme.button.color;
 
-    return new AnimatedOpacity(
+    return AnimatedOpacity(
       opacity: _hideStuff ? 0.0 : 1.0,
-      duration: new Duration(milliseconds: 300),
-      child: new Container(
+      duration: Duration(milliseconds: 300),
+      child: Container(
         height: barHeight,
         color: Theme.of(context).dialogBackgroundColor,
-        child: new Row(
+        child: Row(
           children: <Widget>[
             _buildPlayPause(controller),
-            widget.isLive
+            chewieController.isLive
                 ? Expanded(child: const Text('LIVE'))
                 : _buildPosition(iconColor),
-            widget.isLive ? const SizedBox() : _buildProgressBar(),
+            chewieController.isLive ? const SizedBox() : _buildProgressBar(),
             _buildMuteButton(controller),
             _buildExpandButton(),
           ],
@@ -118,21 +104,23 @@ class _MaterialControlsState extends State<MaterialControls> {
   }
 
   GestureDetector _buildExpandButton() {
-    return new GestureDetector(
+    return GestureDetector(
       onTap: _onExpandCollapse,
-      child: new AnimatedOpacity(
+      child: AnimatedOpacity(
         opacity: _hideStuff ? 0.0 : 1.0,
-        duration: new Duration(milliseconds: 300),
-        child: new Container(
+        duration: Duration(milliseconds: 300),
+        child: Container(
           height: barHeight,
-          margin: new EdgeInsets.only(right: 12.0),
-          padding: new EdgeInsets.only(
+          margin: EdgeInsets.only(right: 12.0),
+          padding: EdgeInsets.only(
             left: 8.0,
             right: 8.0,
           ),
-          child: new Center(
-            child: new Icon(
-              widget.fullScreen ? Icons.fullscreen_exit : Icons.fullscreen,
+          child: Center(
+            child: Icon(
+              chewieController.isFullScreen
+                  ? Icons.fullscreen_exit
+                  : Icons.fullscreen,
             ),
           ),
         ),
@@ -141,8 +129,8 @@ class _MaterialControlsState extends State<MaterialControls> {
   }
 
   Expanded _buildHitArea() {
-    return new Expanded(
-      child: new GestureDetector(
+    return Expanded(
+      child: GestureDetector(
         onTap: _latestValue != null && _latestValue.isPlaying
             ? _cancelAndRestartTimer
             : () {
@@ -152,24 +140,24 @@ class _MaterialControlsState extends State<MaterialControls> {
                   _hideStuff = true;
                 });
               },
-        child: new Container(
+        child: Container(
           color: Colors.transparent,
-          child: new Center(
-            child: new AnimatedOpacity(
+          child: Center(
+            child: 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(
+              duration: Duration(milliseconds: 300),
+              child: GestureDetector(
+                child: Container(
+                  decoration: BoxDecoration(
                     color: Theme.of(context).dialogBackgroundColor,
-                    borderRadius: new BorderRadius.circular(48.0),
+                    borderRadius: BorderRadius.circular(48.0),
                   ),
-                  child: new Padding(
-                    padding: new EdgeInsets.all(12.0),
-                    child: new Icon(Icons.play_arrow, size: 32.0),
+                  child: Padding(
+                    padding: EdgeInsets.all(12.0),
+                    child: Icon(Icons.play_arrow, size: 32.0),
                   ),
                 ),
               ),
@@ -183,7 +171,7 @@ class _MaterialControlsState extends State<MaterialControls> {
   GestureDetector _buildMuteButton(
     VideoPlayerController controller,
   ) {
-    return new GestureDetector(
+    return GestureDetector(
       onTap: () {
         _cancelAndRestartTimer();
 
@@ -194,18 +182,18 @@ class _MaterialControlsState extends State<MaterialControls> {
           controller.setVolume(0.0);
         }
       },
-      child: new AnimatedOpacity(
+      child: AnimatedOpacity(
         opacity: _hideStuff ? 0.0 : 1.0,
-        duration: new Duration(milliseconds: 300),
-        child: new ClipRect(
-          child: new Container(
-            child: new Container(
+        duration: Duration(milliseconds: 300),
+        child: ClipRect(
+          child: Container(
+            child: Container(
               height: barHeight,
-              padding: new EdgeInsets.only(
+              padding: EdgeInsets.only(
                 left: 8.0,
                 right: 8.0,
               ),
-              child: new Icon(
+              child: Icon(
                 (_latestValue != null && _latestValue.volume > 0)
                     ? Icons.volume_up
                     : Icons.volume_off,
@@ -218,17 +206,17 @@ class _MaterialControlsState extends State<MaterialControls> {
   }
 
   GestureDetector _buildPlayPause(VideoPlayerController controller) {
-    return new GestureDetector(
+    return GestureDetector(
       onTap: _playPause,
-      child: new Container(
+      child: Container(
         height: barHeight,
         color: Colors.transparent,
-        margin: new EdgeInsets.only(left: 8.0, right: 4.0),
-        padding: new EdgeInsets.only(
+        margin: EdgeInsets.only(left: 8.0, right: 4.0),
+        padding: EdgeInsets.only(
           left: 12.0,
           right: 12.0,
         ),
-        child: new Icon(
+        child: Icon(
           controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
         ),
       ),
@@ -243,11 +231,11 @@ class _MaterialControlsState extends State<MaterialControls> {
         ? _latestValue.duration
         : Duration.zero;
 
-    return new Padding(
-      padding: new EdgeInsets.only(right: 24.0),
-      child: new Text(
+    return Padding(
+      padding: EdgeInsets.only(right: 24.0),
+      child: Text(
         '${formatDuration(position)} / ${formatDuration(duration)}',
-        style: new TextStyle(
+        style: TextStyle(
           fontSize: 14.0,
         ),
       ),
@@ -264,17 +252,16 @@ class _MaterialControlsState extends State<MaterialControls> {
   }
 
   Future<Null> _initialize() async {
-    widget.controller.addListener(_updateState);
+    controller.addListener(_updateState);
 
     _updateState();
 
-    if ((widget.controller.value != null &&
-            widget.controller.value.isPlaying) ||
-        widget.autoPlay) {
+    if ((controller.value != null && controller.value.isPlaying) ||
+        chewieController.autoPlay) {
       _startHideTimer();
     }
 
-    _showTimer = new Timer(new Duration(milliseconds: 200), () {
+    _showTimer = Timer(Duration(milliseconds: 200), () {
       setState(() {
         _hideStuff = false;
       });
@@ -285,12 +272,10 @@ class _MaterialControlsState extends State<MaterialControls> {
     setState(() {
       _hideStuff = true;
 
-      widget.onExpandCollapse().then((dynamic _) {
-        _showAfterExpandCollapseTimer =
-            new Timer(new Duration(milliseconds: 300), () {
-          setState(() {
-            _cancelAndRestartTimer();
-          });
+      chewieController.toggleFullScreen();
+      _showAfterExpandCollapseTimer = Timer(Duration(milliseconds: 300), () {
+        setState(() {
+          _cancelAndRestartTimer();
         });
       });
     });
@@ -298,26 +283,26 @@ class _MaterialControlsState extends State<MaterialControls> {
 
   void _playPause() {
     setState(() {
-      if (widget.controller.value.isPlaying) {
+      if (controller.value.isPlaying) {
         _hideStuff = false;
         _hideTimer?.cancel();
-        widget.controller.pause();
+        controller.pause();
       } else {
         _cancelAndRestartTimer();
 
-        if (!widget.controller.value.initialized) {
-          widget.controller.initialize().then((_) {
-            widget.controller.play();
+        if (!controller.value.initialized) {
+          controller.initialize().then((_) {
+            controller.play();
           });
         } else {
-          widget.controller.play();
+          controller.play();
         }
       }
     });
   }
 
   void _startHideTimer() {
-    _hideTimer = new Timer(const Duration(seconds: 3), () {
+    _hideTimer = Timer(const Duration(seconds: 3), () {
       setState(() {
         _hideStuff = true;
       });
@@ -326,16 +311,16 @@ class _MaterialControlsState extends State<MaterialControls> {
 
   void _updateState() {
     setState(() {
-      _latestValue = widget.controller.value;
+      _latestValue = controller.value;
     });
   }
 
   Widget _buildProgressBar() {
-    return new Expanded(
-      child: new Padding(
-        padding: new EdgeInsets.only(right: 20.0),
-        child: new MaterialVideoProgressBar(
-          widget.controller,
+    return Expanded(
+      child: Padding(
+        padding: EdgeInsets.only(right: 20.0),
+        child: MaterialVideoProgressBar(
+          controller,
           onDragStart: () {
             setState(() {
               _dragging = true;
@@ -350,8 +335,8 @@ class _MaterialControlsState extends State<MaterialControls> {
 
             _startHideTimer();
           },
-          colors: widget.progressColors ??
-              new ChewieProgressColors(
+          colors: chewieController.materialProgressColors ??
+              ChewieProgressColors(
                   playedColor: Theme.of(context).accentColor,
                   handleColor: Theme.of(context).accentColor,
                   bufferedColor: Theme.of(context).backgroundColor,

+ 35 - 36
lib/src/material_progress_bar.dart

@@ -4,37 +4,36 @@ import 'package:flutter/widgets.dart';
 import 'package:video_player/video_player.dart';
 
 class MaterialVideoProgressBar extends StatefulWidget {
-  final VideoPlayerController controller;
-  final ChewieProgressColors colors;
-  final Function() onDragStart;
-  final Function() onDragEnd;
-  final Function() onDragUpdate;
-
   MaterialVideoProgressBar(
     this.controller, {
     ChewieProgressColors colors,
     this.onDragEnd,
     this.onDragStart,
     this.onDragUpdate,
-  }) : colors = colors ?? new ChewieProgressColors();
+  }) : colors = colors ?? ChewieProgressColors();
+
+  final VideoPlayerController controller;
+  final ChewieProgressColors colors;
+  final Function() onDragStart;
+  final Function() onDragEnd;
+  final Function() onDragUpdate;
 
   @override
   _VideoProgressBarState createState() {
-    return new _VideoProgressBarState();
+    return _VideoProgressBarState();
   }
 }
 
 class _VideoProgressBarState extends State<MaterialVideoProgressBar> {
-  VoidCallback listener;
-
-  bool _controllerWasPlaying = false;
-
   _VideoProgressBarState() {
     listener = () {
       setState(() {});
     };
   }
 
+  VoidCallback listener;
+  bool _controllerWasPlaying = false;
+
   VideoPlayerController get controller => widget.controller;
 
   @override
@@ -59,16 +58,16 @@ class _VideoProgressBarState extends State<MaterialVideoProgressBar> {
       controller.seekTo(position);
     }
 
-    return new GestureDetector(
+    return GestureDetector(
       child: (controller.value.hasError)
-          ? new Text(controller.value.errorDescription)
-          : new Center(
-              child: new Container(
+          ? Text(controller.value.errorDescription)
+          : Center(
+              child: Container(
                 height: MediaQuery.of(context).size.height / 2,
                 width: MediaQuery.of(context).size.width,
                 color: Colors.transparent,
-                child: new CustomPaint(
-                  painter: new _ProgressBarPainter(
+                child: CustomPaint(
+                  painter: _ProgressBarPainter(
                     controller.value,
                     widget.colors,
                   ),
@@ -118,11 +117,11 @@ class _VideoProgressBarState extends State<MaterialVideoProgressBar> {
 }
 
 class _ProgressBarPainter extends CustomPainter {
+  _ProgressBarPainter(this.value, this.colors);
+
   VideoPlayerValue value;
   ChewieProgressColors colors;
 
-  _ProgressBarPainter(this.value, this.colors);
-
   @override
   bool shouldRepaint(CustomPainter painter) {
     return true;
@@ -133,12 +132,12 @@ class _ProgressBarPainter extends CustomPainter {
     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),
+      RRect.fromRectAndRadius(
+        Rect.fromPoints(
+          Offset(0.0, size.height / 2),
+          Offset(size.width, size.height / 2 + height),
         ),
-        new Radius.circular(4.0),
+        Radius.circular(4.0),
       ),
       colors.backgroundPaint,
     );
@@ -152,28 +151,28 @@ class _ProgressBarPainter extends CustomPainter {
       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),
+        RRect.fromRectAndRadius(
+          Rect.fromPoints(
+            Offset(start, size.height / 2),
+            Offset(end, size.height / 2 + height),
           ),
-          new Radius.circular(4.0),
+          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),
+      RRect.fromRectAndRadius(
+        Rect.fromPoints(
+          Offset(0.0, size.height / 2),
+          Offset(playedPart, size.height / 2 + height),
         ),
-        new Radius.circular(4.0),
+        Radius.circular(4.0),
       ),
       colors.playedPaint,
     );
     canvas.drawCircle(
-      new Offset(playedPart, size.height / 2 + height / 2),
+      Offset(playedPart, size.height / 2 + height / 2),
       height * 3,
       colors.handlePaint,
     );

+ 38 - 102
lib/src/player_with_controls.dart

@@ -1,77 +1,48 @@
-import 'dart:async';
 import 'dart:ui';
 
-import 'package:chewie/src/chewie_progress_colors.dart';
+import 'package:chewie/src/chewie_player.dart';
 import 'package:chewie/src/cupertino_controls.dart';
 import 'package:chewie/src/material_controls.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:video_player/video_player.dart';
 
-class PlayerWithControls extends StatefulWidget {
-  final VideoPlayerController controller;
-  final Future<dynamic> Function() onExpandCollapse;
-  final bool fullScreen;
-  final ChewieProgressColors cupertinoProgressColors;
-  final ChewieProgressColors materialProgressColors;
-  final Widget placeholder;
-  final double aspectRatio;
-  final bool autoPlay;
-  final bool showControls;
-  final bool isLive;
+class PlayerWithControls extends StatelessWidget {
+  PlayerWithControls({Key key}) : super(key: key);
 
-  PlayerWithControls({
-    Key key,
-    @required this.controller,
-    @required this.onExpandCollapse,
-    @required this.aspectRatio,
-    this.fullScreen = false,
-    this.showControls = true,
-    this.cupertinoProgressColors,
-    this.materialProgressColors,
-    this.placeholder,
-    this.autoPlay,
-    this.isLive = false,
-  }) : super(key: key);
-
-  @override
-  State createState() {
-    return new _VideoPlayerWithControlsState();
-  }
-}
-
-class _VideoPlayerWithControlsState extends State<PlayerWithControls> {
   @override
   Widget build(BuildContext context) {
-    final controller = widget.controller;
+    final ChewieController chewieController = ChewieController.of(context);
 
-    return new Center(
-      child: new Container(
+    return Center(
+      child: Container(
         width: MediaQuery.of(context).size.width,
-        child: new AspectRatio(
-          aspectRatio: widget.aspectRatio,
-          child: _buildPlayerWithControls(controller, context),
+        child: AspectRatio(
+          aspectRatio:
+              chewieController.aspectRatio ?? _calculateAspectRatio(context),
+          child: _buildPlayerWithControls(chewieController, context),
         ),
       ),
     );
   }
 
   Container _buildPlayerWithControls(
-      VideoPlayerController controller, BuildContext context) {
-    return new Container(
-      child: new Stack(
+      ChewieController chewieController, BuildContext context) {
+    return Container(
+      child: Stack(
         children: <Widget>[
-          widget.placeholder ?? new Container(),
-          new Center(
-            child: new Hero(
-              tag: controller,
-              child: new AspectRatio(
-                aspectRatio: widget.aspectRatio,
-                child: new VideoPlayer(controller),
+          chewieController.placeholder ?? Container(),
+          Center(
+            child: Hero(
+              tag: chewieController.videoPlayerController,
+              child: AspectRatio(
+                aspectRatio: chewieController.aspectRatio ??
+                    _calculateAspectRatio(context),
+                child: VideoPlayer(chewieController.videoPlayerController),
               ),
             ),
           ),
-          _buildControls(context, controller),
+          _buildControls(context, chewieController),
         ],
       ),
     );
@@ -79,60 +50,25 @@ class _VideoPlayerWithControlsState extends State<PlayerWithControls> {
 
   Widget _buildControls(
     BuildContext context,
-    VideoPlayerController controller,
+    ChewieController chewieController,
   ) {
-    return widget.showControls
-        ? Theme.of(context).platform == TargetPlatform.android
-            ? new MaterialControls(
-                controller: controller,
-                onExpandCollapse: widget.onExpandCollapse,
-                fullScreen: widget.fullScreen,
-                progressColors: widget.materialProgressColors,
-                autoPlay: widget.autoPlay,
-                isLive: widget.isLive,
-              )
-            : 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,
-                progressColors: widget.cupertinoProgressColors,
-                autoPlay: widget.autoPlay,
-                isLive: widget.isLive,
-              )
-        : new Container();
+    return chewieController.showControls
+        ? chewieController.customControls != null
+            ? chewieController.customControls
+            : Theme.of(context).platform == TargetPlatform.android
+                ? MaterialControls()
+                : CupertinoControls(
+                    backgroundColor: Color.fromRGBO(41, 41, 41, 0.7),
+                    iconColor: Color.fromARGB(255, 200, 200, 200),
+                  )
+        : Container();
   }
 
-  @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();
-  }
-
-  @override
-  void didUpdateWidget(PlayerWithControls oldWidget) {
-    super.didUpdateWidget(oldWidget);
-
-    if (widget.controller.dataSource != oldWidget.controller.dataSource) {
-      widget.controller.addListener(_onPlay);
-    }
-  }
-
-  @override
-  dispose() {
-    widget.controller.removeListener(_onPlay);
-    super.dispose();
-  }
+  double _calculateAspectRatio(BuildContext context) {
+    final size = MediaQuery.of(context).size;
+    final width = size.width;
+    final height = size.height;
 
-  void _onPlay() {
-    if (widget.controller.value.isPlaying) {
-      setState(() {
-        widget.controller.removeListener(_onPlay);
-      });
-    }
+    return width > height ? width / height : height / width;
   }
 }

+ 4 - 41
pubspec.yaml

@@ -1,8 +1,10 @@
 name: chewie
 description: A video player for Flutter with Cupertino and Material play controls
-version: 0.8.0
+version: 0.9.0
 homepage: https://github.com/brianegan/chewie
-author: Brian Egan <brian@brianegan.com>
+authors:
+  - Brian Egan <brian@brianegan.com>
+  - Ben Hagen <ben@ottomatic.io>
 
 environment:
   sdk: ">=2.0.0-dev.28.0 <3.0.0"
@@ -18,44 +20,5 @@ dev_dependencies:
   flutter_test:
     sdk: flutter
 
-# For information on the generic Dart part of this file, see the
-# following page: https://www.dartlang.org/tools/pub/pubspec
-
-# The following section is specific to Flutter.
 flutter:
-
-  # The following line ensures that the Material Icons font is
-  # included with your application, so that you can use the icons in
-  # the material Icons class.
   uses-material-design: true
-
-  # To add assets to your application, add an assets section, like this:
-  # assets:
-  #  - images/a_dot_burr.jpeg
-  #  - images/a_dot_ham.jpeg
-
-  # An image asset can refer to one or more resolution-specific "variants", see
-  # https://flutter.io/assets-and-images/#resolution-aware.
-
-  # For details regarding adding assets from package dependencies, see
-  # https://flutter.io/assets-and-images/#from-packages
-
-  # To add custom fonts to your application, add a fonts section here,
-  # in this "flutter" section. Each entry in this list should have a
-  # "family" key with the font family name, and a "fonts" key with a
-  # list giving the asset and other descriptors for the font. For
-  # example:
-  # fonts:
-  #   - family: Schyler
-  #     fonts:
-  #       - asset: fonts/Schyler-Regular.ttf
-  #       - asset: fonts/Schyler-Italic.ttf
-  #         style: italic
-  #   - family: Trajan Pro
-  #     fonts:
-  #       - asset: fonts/TrajanPro.ttf
-  #       - asset: fonts/TrajanPro_Bold.ttf
-  #         weight: 700
-  #
-  # For details regarding fonts from package dependencies,
-  # see https://flutter.io/custom-fonts/#from-packages