Bläddra i källkod

ChewieController accessed by an InheritedWidget

Ben Hagen 6 år sedan
förälder
incheckning
5ee660827c

+ 35 - 23
example/lib/main.dart

@@ -1,4 +1,5 @@
 import 'package:chewie/chewie.dart';
+import 'package:chewie/src/chewie_controller.dart';
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
 import 'package:video_player/video_player.dart';
@@ -22,14 +23,33 @@ class ChewieDemo extends StatefulWidget {
 
 class _ChewieDemoState extends State<ChewieDemo> {
   TargetPlatform _platform;
-  VideoPlayerController _controller;
+  VideoPlayerController _videoPlayerController;
+  ChewieController _chewieController;
 
   @override
   void initState() {
     super.initState();
-    _controller = VideoPlayerController.network(
+    _videoPlayerController = VideoPlayerController.network(
       'https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4',
     );
+    _chewieController = ChewieController(
+      _videoPlayerController, 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
@@ -48,34 +68,24 @@ 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(
+                        _chewieController.videoPlayerController =
+                            VideoPlayerController.network(
                           'https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4',
                         );
                       });
@@ -90,8 +100,10 @@ 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.videoPlayerController =
+                            VideoPlayerController.network(
+                              'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4',
+//                          'https://www.sample-videos.com/video123/mp4/480/big_buck_bunny_480p_20mb.mp4',
                         );
                       });
                     },

+ 182 - 0
lib/src/chewie_controller.dart

@@ -0,0 +1,182 @@
+import 'package:chewie/src/chewie_progress_colors.dart';
+import 'package:flutter/material.dart';
+import 'package:video_player/video_player.dart';
+
+/// The state of the [ChewieController].
+@immutable
+class ChewieValue {
+  ChewieValue(
+    this.videoPlayerController, {
+    this.isFullScreen = false,
+  });
+
+  /// True if the video is currently playing fullscreen
+  final bool isFullScreen;
+
+  /// The controller for the video you want to play
+  final VideoPlayerController videoPlayerController;
+
+  ChewieValue copyWith({
+    VideoPlayerController videoPlayerController,
+    bool isFullScreen,
+  }) {
+    return ChewieValue(
+      videoPlayerController ?? this.videoPlayerController,
+      isFullScreen: isFullScreen ?? this.isFullScreen,
+    );
+  }
+
+  @override
+  String toString() {
+    return '$runtimeType('
+        'isFullscreen: $isFullScreen, '
+        'videoPlayerController: $videoPlayerController, ';
+  }
+}
+
+class ChewieController extends ValueNotifier<ChewieValue> {
+  ChewieController(
+    VideoPlayerController 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.allowedScreenSleep = true,
+    this.isLive = false,
+  })  : assert(videoPlayerController != null,
+            'You must provide a controller to play a video'),
+        super(ChewieValue(videoPlayerController)) {
+    _initialize();
+  }
+
+  /// Initialize the Video on Startup. This will prep the video for playback.
+  final bool autoInitialize;
+
+  /// Play the video as soon as it's displayed
+  final bool autoPlay;
+
+  /// Start video at a certain position
+  final Duration startAt;
+
+  /// Whether or not the video should loop
+  final bool looping;
+
+  /// Whether or not to show the controls
+  final bool showControls;
+
+  /// The Aspect Ratio of the Video. Important to get the correct size of the
+  /// video!
+  ///
+  /// Will fallback to fitting within the space allowed.
+  final double aspectRatio;
+
+  /// The colors to use for controls on iOS. By default, the iOS player uses
+  /// colors sampled from the original iOS 11 designs.
+  final ChewieProgressColors cupertinoProgressColors;
+
+  /// The colors to use for the Material Progress Bar. By default, the Material
+  /// player uses the colors from your Theme.
+  final ChewieProgressColors materialProgressColors;
+
+  /// The placeholder is displayed underneath the Video before it is initialized
+  /// or played.
+  final Widget placeholder;
+
+  /// Defines if the player will start in fullscreen when play is pressed
+  final bool fullScreenByDefault;
+
+  /// Defines if the player will sleep in fullscreen or not
+  final bool allowedScreenSleep;
+
+  /// Defines if the controls should be for live stream video
+  final bool isLive;
+
+  Future _initialize() async {
+    await value.videoPlayerController.setLooping(looping);
+
+    if (autoInitialize || autoPlay) {
+      await value.videoPlayerController.initialize();
+    }
+
+    if (autoPlay) {
+      if (fullScreenByDefault) {
+        enterFullscreen();
+      }
+
+      await value.videoPlayerController.play();
+    }
+
+    if (startAt != null) {
+      await value.videoPlayerController.seekTo(startAt);
+    }
+
+    if (fullScreenByDefault) {
+      value.videoPlayerController.addListener(() async {
+        if (await value.videoPlayerController.value.isPlaying &&
+            !value.isFullScreen) {
+          enterFullscreen();
+        }
+      });
+    }
+  }
+
+  void enterFullscreen() {
+    value = value.copyWith(isFullScreen: true);
+  }
+
+  void exitFullscreen() {
+    value = value.copyWith(isFullScreen: false);
+  }
+
+  void toggleFullscreen() {
+    value = value.copyWith(isFullScreen: !value.isFullScreen);
+  }
+
+  void play() {
+    value.videoPlayerController.play();
+  }
+
+  void pause() {
+    value.videoPlayerController.pause();
+  }
+
+  // TODO: Do we really need the ability to change the controller?
+  set videoPlayerController(VideoPlayerController controller) {
+    if (value.videoPlayerController.dataSource != controller.dataSource) {
+      // FIXME: The VideoPlayer widget still tries to access the controller
+      value.videoPlayerController.dispose();
+      value = value.copyWith(videoPlayerController: controller);
+      exitFullscreen();
+      _initialize();
+    }
+  }
+}
+
+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);
+
+  final ChewieController controller;
+
+  static ChewieController of(BuildContext context) {
+    final ChewieControllerProvider chewieControllerProvider =
+        context.inheritFromWidgetOfExactType(ChewieControllerProvider);
+
+    return chewieControllerProvider.controller;
+  }
+
+  @override
+  bool updateShouldNotify(ChewieControllerProvider old) =>
+      controller != old.controller;
+}

+ 38 - 143
lib/src/chewie_player.dart

@@ -1,112 +1,63 @@
 import 'dart:async';
 
-import 'package:chewie/src/chewie_progress_colors.dart';
+import 'package:chewie/src/chewie_controller.dart';
 import 'package:chewie/src/player_with_controls.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter/widgets.dart';
 import 'package:screen/screen.dart';
-import 'package:video_player/video_player.dart';
 
 /// A Video Player with Material and Cupertino skins.
 ///
 /// `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;
-
-  /// Initialize the Video on Startup. This will prep the video for playback.
-  final bool autoInitialize;
-
-  /// Play the video as soon as it's displayed
-  final bool autoPlay;
-
-  /// Start video at a certain position
-  final Duration startAt;
-
-  /// Whether or not the video should loop
-  final bool looping;
-
-  /// Whether or not to show the controls
-  final bool showControls;
-
-  /// The Aspect Ratio of the Video. Important to get the correct size of the
-  /// video!
-  ///
-  /// Will fallback to fitting within the space allowed.
-  final double aspectRatio;
-
-  /// The colors to use for controls on iOS. By default, the iOS player uses
-  /// colors sampled from the original iOS 11 designs.
-  final ChewieProgressColors cupertinoProgressColors;
-
-  /// The colors to use for the Material Progress Bar. By default, the Material
-  /// player uses the colors from your Theme.
-  final ChewieProgressColors materialProgressColors;
-
-  /// The placeholder is displayed underneath the Video before it is initialized
-  /// or played.
-  final Widget placeholder;
-
-  /// Defines if the player will start in fullscreen when play is pressed
-  final bool fullScreenByDefault;
-
-  /// Defines if the player will sleep in fullscreen or not
-  final bool allowedScreenSleep;
-
-  /// Defines if the controls should be for live stream video
-  final bool isLive;
-
-  Chewie(
-    this.controller, {
+  Chewie({
     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'),
+    this.controller,
+  })  : assert(controller != null, 'You must provide a chewie controller'),
         super(key: key);
 
+  /// The [ChewieController]
+  final ChewieController controller;
+
   @override
-  State<StatefulWidget> createState() {
-    return _ChewiePlayerState();
+  ChewieState createState() {
+    return ChewieState();
   }
 }
 
-class _ChewiePlayerState extends State<Chewie> {
-  VideoPlayerController _controller;
+class ChewieState extends State<Chewie> {
   bool _isFullScreen = false;
 
   @override
-  Widget build(BuildContext context) {
-    return 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,
-    );
+  void initState() {
+    super.initState();
+    widget.controller.addListener(listener);
   }
 
   @override
-  void initState() {
-    super.initState();
-    _controller = widget.controller;
-    _initialize();
+  void dispose() {
+    widget.controller.removeListener(listener);
+    super.dispose();
+  }
+
+  void listener() async {
+    if (widget.controller.value.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(
@@ -116,15 +67,9 @@ class _ChewiePlayerState extends State<Chewie> {
       body: Container(
         alignment: Alignment.center,
         color: Colors.black,
-        child: PlayerWithControls(
-          controller: _controller,
-          onExpandCollapse: () =>
-              Future<dynamic>.value(Navigator.of(context).pop()),
-          aspectRatio: widget.aspectRatio ?? _calculateAspectRatio(context),
-          fullScreen: true,
-          isLive: widget.isLive,
-          cupertinoProgressColors: widget.cupertinoProgressColors,
-          materialProgressColors: widget.materialProgressColors,
+        child: ChewieControllerProvider(
+          controller: widget.controller,
+          child: PlayerWithControls(),
         ),
       ),
     );
@@ -143,48 +88,6 @@ class _ChewiePlayerState extends State<Chewie> {
     );
   }
 
-  Future _initialize() async {
-    await _controller.setLooping(widget.looping);
-
-    if (widget.autoInitialize || widget.autoPlay) {
-      await _controller.initialize();
-    }
-
-    if (widget.autoPlay) {
-      if (widget.fullScreenByDefault) {
-        _isFullScreen = true;
-        await _pushFullScreenWidget(context);
-      }
-
-      await _controller.play();
-    }
-
-    if (widget.startAt != null) {
-      await _controller.seekTo(widget.startAt);
-    }
-
-    if (widget.fullScreenByDefault) {
-      widget.controller.addListener(() async {
-        if (await widget.controller.value.isPlaying && !_isFullScreen) {
-          _isFullScreen = true;
-          await _pushFullScreenWidget(context);
-        }
-      });
-    }
-  }
-
-  @override
-  void didUpdateWidget(Chewie oldWidget) {
-    super.didUpdateWidget(oldWidget);
-
-    if (widget.controller.dataSource != _controller.dataSource) {
-      _controller.dispose();
-      _controller = widget.controller;
-      _isFullScreen = false;
-      _initialize();
-    }
-  }
-
   Future<dynamic> _pushFullScreenWidget(BuildContext context) async {
     final isAndroid = Theme.of(context).platform == TargetPlatform.android;
     final TransitionRoute<Null> route = PageRouteBuilder<Null>(
@@ -200,7 +103,7 @@ class _ChewiePlayerState extends State<Chewie> {
       ]);
     }
 
-    if (!widget.allowedScreenSleep) {
+    if (!widget.controller.allowedScreenSleep) {
       Screen.keepOn(true);
     }
 
@@ -219,12 +122,4 @@ class _ChewiePlayerState extends State<Chewie> {
       DeviceOrientation.landscapeRight,
     ]);
   }
-
-  double _calculateAspectRatio(BuildContext context) {
-    final size = MediaQuery.of(context).size;
-    final width = size.width;
-    final height = size.height;
-
-    return width > height ? width / height : height / width;
-  }
 }

+ 35 - 53
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_controller.dart';
 import 'package:chewie/src/chewie_progress_colors.dart';
 import 'package:chewie/src/cupertino_progress_bar.dart';
 import 'package:chewie/src/utils.dart';
@@ -13,22 +14,10 @@ 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,
   });
 
   @override
@@ -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 = ChewieControllerProvider.of(context);
+    controller = chewieController.value.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 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,33 +64,26 @@ class _CupertinoControlsState extends State<CupertinoControls> {
   }
 
   void _dispose() {
-    widget.controller.removeListener(_updateState);
+    controller.removeListener(_updateState);
     _hideTimer?.cancel();
     _expandCollapseTimer?.cancel();
     _initTimer?.cancel();
   }
 
   @override
-  void initState() {
-    _initialize();
-
-    super.initState();
-  }
+  void didChangeDependencies() {
+    chewieController = ChewieControllerProvider.of(context);
+    controller = chewieController.value.videoPlayerController;
 
-  @override
-  void didUpdateWidget(CupertinoControls oldWidget) {
-    super.didUpdateWidget(oldWidget);
+    _dispose();
+    _initialize();
 
-    if (widget.controller.dataSource != oldWidget.controller.dataSource) {
-      _dispose();
-      _initialize();
-    }
+    super.didChangeDependencies();
   }
 
   AnimatedOpacity _buildBottomBar(
     Color backgroundColor,
     Color iconColor,
-    VideoPlayerController controller,
     double barHeight,
   ) {
     return AnimatedOpacity(
@@ -122,7 +107,7 @@ class _CupertinoControlsState extends State<CupertinoControls> {
                   Radius.circular(10.0),
                 ),
               ),
-              child: widget.isLive
+              child: chewieController.isLive
                   ? Row(
                       mainAxisAlignment: MainAxisAlignment.spaceBetween,
                       children: <Widget>[
@@ -185,7 +170,7 @@ class _CupertinoControlsState extends State<CupertinoControls> {
               ),
               child: Center(
                 child: Icon(
-                  widget.fullScreen
+                  chewieController.value.isFullScreen
                       ? OpenIconicIcons.fullscreenExit
                       : OpenIconicIcons.fullscreenEnter,
                   color: iconColor,
@@ -376,7 +361,6 @@ class _CupertinoControlsState extends State<CupertinoControls> {
   Widget _buildTopBar(
     Color backgroundColor,
     Color iconColor,
-    VideoPlayerController controller,
     double barHeight,
     double buttonPadding,
   ) {
@@ -410,13 +394,12 @@ 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();
     }
 
@@ -431,11 +414,10 @@ class _CupertinoControlsState extends State<CupertinoControls> {
     setState(() {
       _hideStuff = true;
 
-      widget.onExpandCollapse().then((dynamic _) {
-        _expandCollapseTimer = Timer(Duration(milliseconds: 300), () {
-          setState(() {
-            _cancelAndRestartTimer();
-          });
+      chewieController.toggleFullscreen();
+      _expandCollapseTimer = Timer(Duration(milliseconds: 300), () {
+        setState(() {
+          _cancelAndRestartTimer();
         });
       });
     });
@@ -446,14 +428,14 @@ class _CupertinoControlsState extends State<CupertinoControls> {
       child: Padding(
         padding: EdgeInsets.only(right: 12.0),
         child: CupertinoVideoProgressBar(
-          widget.controller,
+          controller,
           onDragStart: () {
             _hideTimer?.cancel();
           },
           onDragEnd: () {
             _startHideTimer();
           },
-          colors: widget.progressColors ??
+          colors: chewieController.cupertinoProgressColors ??
               ChewieProgressColors(
                 playedColor: Color.fromARGB(
                   120,
@@ -487,19 +469,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();
         }
       }
     });
@@ -509,14 +491,14 @@ class _CupertinoControlsState extends State<CupertinoControls> {
     _cancelAndRestartTimer();
     final beginning = Duration(seconds: 0).inMilliseconds;
     final skip = (_latestValue.position - Duration(seconds: 15)).inMilliseconds;
-    widget.controller.seekTo(Duration(milliseconds: math.max(skip, beginning)));
+    controller.seekTo(Duration(milliseconds: math.max(skip, beginning)));
   }
 
   void _skipForward() {
     _cancelAndRestartTimer();
     final end = _latestValue.duration.inMilliseconds;
     final skip = (_latestValue.position + Duration(seconds: 15)).inMilliseconds;
-    widget.controller.seekTo(Duration(milliseconds: math.min(skip, end)));
+    controller.seekTo(Duration(milliseconds: math.min(skip, end)));
   }
 
   void _startHideTimer() {
@@ -529,7 +511,7 @@ class _CupertinoControlsState extends State<CupertinoControls> {
 
   void _updateState() {
     setState(() {
-      _latestValue = widget.controller.value;
+      _latestValue = controller.value;
     });
   }
 }

+ 34 - 51
lib/src/material_controls.dart

@@ -1,28 +1,14 @@
 import 'dart:async';
 
+import 'package:chewie/src/chewie_controller.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() {
@@ -42,6 +28,9 @@ class _MaterialControlsState extends State<MaterialControls> {
   final barHeight = 48.0;
   final marginSize = 5.0;
 
+  VideoPlayerController controller;
+  ChewieController chewieController;
+
   @override
   Widget build(BuildContext context) {
     return Column(
@@ -56,7 +45,7 @@ class _MaterialControlsState extends State<MaterialControls> {
                 ),
               )
             : _buildHitArea(),
-        _buildBottomBar(context, widget.controller),
+        _buildBottomBar(context),
       ],
     );
   }
@@ -68,31 +57,25 @@ class _MaterialControlsState extends State<MaterialControls> {
   }
 
   void _dispose() {
-    widget.controller.removeListener(_updateState);
+    controller.removeListener(_updateState);
     _hideTimer?.cancel();
     _showTimer?.cancel();
     _showAfterExpandCollapseTimer?.cancel();
   }
 
   @override
-  void initState() {
+  void didChangeDependencies() {
+    chewieController = ChewieControllerProvider.of(context);
+    controller = chewieController.value.videoPlayerController;
+
+    _dispose();
     _initialize();
 
-    super.initState();
-  }
-
-  @override
-  void didUpdateWidget(MaterialControls oldWidget) {
-    super.didUpdateWidget(oldWidget);
-    if (widget.controller.dataSource != oldWidget.controller.dataSource) {
-      _dispose();
-      _initialize();
-    }
+    super.didChangeDependencies();
   }
 
   AnimatedOpacity _buildBottomBar(
     BuildContext context,
-    VideoPlayerController controller,
   ) {
     final iconColor = Theme.of(context).textTheme.button.color;
 
@@ -105,10 +88,10 @@ class _MaterialControlsState extends State<MaterialControls> {
         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(),
           ],
@@ -132,7 +115,9 @@ class _MaterialControlsState extends State<MaterialControls> {
           ),
           child: Center(
             child: Icon(
-              widget.fullScreen ? Icons.fullscreen_exit : Icons.fullscreen,
+              chewieController.value.isFullScreen
+                  ? Icons.fullscreen_exit
+                  : Icons.fullscreen,
             ),
           ),
         ),
@@ -264,13 +249,12 @@ 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();
     }
 
@@ -285,11 +269,10 @@ class _MaterialControlsState extends State<MaterialControls> {
     setState(() {
       _hideStuff = true;
 
-      widget.onExpandCollapse().then((dynamic _) {
-        _showAfterExpandCollapseTimer = Timer(Duration(milliseconds: 300), () {
-          setState(() {
-            _cancelAndRestartTimer();
-          });
+      chewieController.toggleFullscreen();
+      _showAfterExpandCollapseTimer = Timer(Duration(milliseconds: 300), () {
+        setState(() {
+          _cancelAndRestartTimer();
         });
       });
     });
@@ -297,19 +280,19 @@ 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();
         }
       }
     });
@@ -325,7 +308,7 @@ class _MaterialControlsState extends State<MaterialControls> {
 
   void _updateState() {
     setState(() {
-      _latestValue = widget.controller.value;
+      _latestValue = controller.value;
     });
   }
 
@@ -334,7 +317,7 @@ class _MaterialControlsState extends State<MaterialControls> {
       child: Padding(
         padding: EdgeInsets.only(right: 20.0),
         child: MaterialVideoProgressBar(
-          widget.controller,
+          controller,
           onDragStart: () {
             setState(() {
               _dragging = true;
@@ -349,7 +332,7 @@ class _MaterialControlsState extends State<MaterialControls> {
 
             _startHideTimer();
           },
-          colors: widget.progressColors ??
+          colors: chewieController.materialProgressColors ??
               ChewieProgressColors(
                   playedColor: Theme.of(context).accentColor,
                   handleColor: Theme.of(context).accentColor,

+ 56 - 87
lib/src/player_with_controls.dart

@@ -1,77 +1,50 @@
-import 'dart:async';
 import 'dart:ui';
 
-import 'package:chewie/src/chewie_progress_colors.dart';
+import 'package:chewie/src/chewie_controller.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 _VideoPlayerWithControlsState();
-  }
-}
-
-class _VideoPlayerWithControlsState extends State<PlayerWithControls> {
   @override
   Widget build(BuildContext context) {
-    final controller = widget.controller;
+    final ChewieController chewieController =
+        ChewieControllerProvider.of(context);
 
     return Center(
       child: Container(
         width: MediaQuery.of(context).size.width,
         child: AspectRatio(
-          aspectRatio: widget.aspectRatio,
-          child: _buildPlayerWithControls(controller, context),
+          aspectRatio:
+              chewieController.aspectRatio ?? _calculateAspectRatio(context),
+          child: _buildPlayerWithControls(chewieController, context),
         ),
       ),
     );
   }
 
   Container _buildPlayerWithControls(
-      VideoPlayerController controller, BuildContext context) {
+      ChewieController chewieController, BuildContext context) {
     return Container(
       child: Stack(
         children: <Widget>[
-          widget.placeholder ?? Container(),
+          chewieController.placeholder ?? Container(),
           Center(
             child: Hero(
-              tag: controller,
+              tag: chewieController.value.videoPlayerController,
               child: AspectRatio(
-                aspectRatio: widget.aspectRatio,
-                child: VideoPlayer(controller),
+                aspectRatio: chewieController.aspectRatio ??
+                    _calculateAspectRatio(context),
+                child:
+                    VideoPlayer(chewieController.value.videoPlayerController),
               ),
             ),
           ),
-          _buildControls(context, controller),
+          _buildControls(context, chewieController),
         ],
       ),
     );
@@ -79,60 +52,56 @@ class _VideoPlayerWithControlsState extends State<PlayerWithControls> {
 
   Widget _buildControls(
     BuildContext context,
-    VideoPlayerController controller,
+    ChewieController chewieController,
   ) {
-    return widget.showControls
+    return chewieController.showControls
         ? Theme.of(context).platform == TargetPlatform.android
-            ? MaterialControls(
-                controller: controller,
-                onExpandCollapse: widget.onExpandCollapse,
-                fullScreen: widget.fullScreen,
-                progressColors: widget.materialProgressColors,
-                autoPlay: widget.autoPlay,
-                isLive: widget.isLive,
-              )
+            ? MaterialControls()
             : CupertinoControls(
                 backgroundColor: Color.fromRGBO(41, 41, 41, 0.7),
                 iconColor: Color.fromARGB(255, 200, 200, 200),
-                controller: controller,
-                onExpandCollapse: widget.onExpandCollapse,
-                fullScreen: widget.fullScreen,
-                progressColors: widget.cupertinoProgressColors,
-                autoPlay: widget.autoPlay,
-                isLive: widget.isLive,
               )
         : Container();
   }
 
-  @override
-  void initState() {
-    // Hack to show the video when it starts playing. Should be fixed by the
-    // Plugin IMO.
-    widget.controller.addListener(_onPlay);
+  double _calculateAspectRatio(BuildContext context) {
+    final size = MediaQuery.of(context).size;
+    final width = size.width;
+    final height = size.height;
 
-    super.initState();
+    return width > height ? width / height : height / width;
   }
 
-  @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();
-  }
-
-  void _onPlay() {
-    if (widget.controller.value.isPlaying) {
-      setState(() {
-        widget.controller.removeListener(_onPlay);
-      });
-    }
-  }
+// TODO: Add playback hack somewhere or better: fix in the VideoPlayer plugin
+//  @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();
+//  }
+//
+//  void _onPlay() {
+//    if (widget.controller.value.isPlaying) {
+//      setState(() {
+//        widget.controller.removeListener(_onPlay);
+//      });
+//    }
+//  }
 }