chewie_player.dart 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. import 'dart:async';
  2. import 'package:chewie/src/chewie_progress_colors.dart';
  3. import 'package:chewie/src/player_with_controls.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:flutter/services.dart';
  6. import 'package:flutter/widgets.dart';
  7. import 'package:video_player/video_player.dart';
  8. import 'package:wakelock/wakelock.dart';
  9. typedef Widget ChewieRoutePageBuilder(
  10. BuildContext context,
  11. Animation<double> animation,
  12. Animation<double> secondaryAnimation,
  13. _ChewieControllerProvider controllerProvider);
  14. /// A Video Player with Material and Cupertino skins.
  15. ///
  16. /// `video_player` is pretty low level. Chewie wraps it in a friendly skin to
  17. /// make it easy to use!
  18. class Chewie extends StatefulWidget {
  19. Chewie({
  20. Key key,
  21. this.controller,
  22. }) : assert(controller != null, 'You must provide a chewie controller'),
  23. super(key: key);
  24. /// The [ChewieController]
  25. final ChewieController controller;
  26. @override
  27. ChewieState createState() {
  28. return ChewieState();
  29. }
  30. }
  31. class ChewieState extends State<Chewie> {
  32. bool _isFullScreen = false;
  33. @override
  34. void initState() {
  35. super.initState();
  36. widget.controller.addListener(listener);
  37. }
  38. @override
  39. void dispose() {
  40. widget.controller.removeListener(listener);
  41. super.dispose();
  42. }
  43. @override
  44. void didUpdateWidget(Chewie oldWidget) {
  45. if (oldWidget.controller != widget.controller) {
  46. widget.controller.addListener(listener);
  47. }
  48. super.didUpdateWidget(oldWidget);
  49. }
  50. void listener() async {
  51. if (widget.controller.isFullScreen && !_isFullScreen) {
  52. _isFullScreen = true;
  53. await _pushFullScreenWidget(context);
  54. } else if (_isFullScreen) {
  55. Navigator.of(context, rootNavigator: true).pop();
  56. _isFullScreen = false;
  57. }
  58. }
  59. @override
  60. Widget build(BuildContext context) {
  61. return _ChewieControllerProvider(
  62. controller: widget.controller,
  63. child: PlayerWithControls(),
  64. );
  65. }
  66. Widget _buildFullScreenVideo(
  67. BuildContext context,
  68. Animation<double> animation,
  69. _ChewieControllerProvider controllerProvider) {
  70. return Scaffold(
  71. resizeToAvoidBottomPadding: false,
  72. body: Container(
  73. alignment: Alignment.center,
  74. color: Colors.black,
  75. child: controllerProvider,
  76. ),
  77. );
  78. }
  79. AnimatedWidget _defaultRoutePageBuilder(
  80. BuildContext context,
  81. Animation<double> animation,
  82. Animation<double> secondaryAnimation,
  83. _ChewieControllerProvider controllerProvider) {
  84. return AnimatedBuilder(
  85. animation: animation,
  86. builder: (BuildContext context, Widget child) {
  87. return _buildFullScreenVideo(context, animation, controllerProvider);
  88. },
  89. );
  90. }
  91. Widget _fullScreenRoutePageBuilder(
  92. BuildContext context,
  93. Animation<double> animation,
  94. Animation<double> secondaryAnimation,
  95. ) {
  96. var controllerProvider = _ChewieControllerProvider(
  97. controller: widget.controller,
  98. child: PlayerWithControls(),
  99. );
  100. if (widget.controller.routePageBuilder == null) {
  101. return _defaultRoutePageBuilder(
  102. context, animation, secondaryAnimation, controllerProvider);
  103. }
  104. return widget.controller.routePageBuilder(
  105. context, animation, secondaryAnimation, controllerProvider);
  106. }
  107. Future<dynamic> _pushFullScreenWidget(BuildContext context) async {
  108. final isAndroid = Theme.of(context).platform == TargetPlatform.android;
  109. final TransitionRoute<Null> route = PageRouteBuilder<Null>(
  110. settings: RouteSettings(isInitialRoute: false),
  111. pageBuilder: _fullScreenRoutePageBuilder,
  112. );
  113. SystemChrome.setEnabledSystemUIOverlays([]);
  114. if (isAndroid) {
  115. SystemChrome.setPreferredOrientations([
  116. DeviceOrientation.landscapeLeft,
  117. DeviceOrientation.landscapeRight,
  118. ]);
  119. }
  120. if (!widget.controller.allowedScreenSleep) {
  121. Wakelock.enable();
  122. }
  123. await Navigator.of(context, rootNavigator: true).push(route);
  124. _isFullScreen = false;
  125. widget.controller.exitFullScreen();
  126. // The wakelock plugins checks whether it needs to perform an action internally,
  127. // so we do not need to check Wakelock.isEnabled.
  128. Wakelock.disable();
  129. SystemChrome.setEnabledSystemUIOverlays(
  130. widget.controller.systemOverlaysAfterFullScreen);
  131. SystemChrome.setPreferredOrientations(
  132. widget.controller.deviceOrientationsAfterFullScreen);
  133. }
  134. }
  135. /// The ChewieController is used to configure and drive the Chewie Player
  136. /// Widgets. It provides methods to control playback, such as [pause] and
  137. /// [play], as well as methods that control the visual appearance of the player,
  138. /// such as [enterFullScreen] or [exitFullScreen].
  139. ///
  140. /// In addition, you can listen to the ChewieController for presentational
  141. /// changes, such as entering and exiting full screen mode. To listen for
  142. /// changes to the playback, such as a change to the seek position of the
  143. /// player, please use the standard information provided by the
  144. /// `VideoPlayerController`.
  145. class ChewieController extends ChangeNotifier {
  146. ChewieController({
  147. this.videoPlayerController,
  148. this.aspectRatio,
  149. this.autoInitialize = false,
  150. this.autoPlay = false,
  151. this.startAt,
  152. this.looping = false,
  153. this.fullScreenByDefault = false,
  154. this.cupertinoProgressColors,
  155. this.materialProgressColors,
  156. this.placeholder,
  157. this.overlay,
  158. this.showControlsOnInitialize = true,
  159. this.showControls = true,
  160. this.customControls,
  161. this.errorBuilder,
  162. this.allowedScreenSleep = true,
  163. this.isLive = false,
  164. this.allowFullScreen = true,
  165. this.allowMuting = true,
  166. this.systemOverlaysAfterFullScreen = SystemUiOverlay.values,
  167. this.deviceOrientationsAfterFullScreen = const [
  168. DeviceOrientation.portraitUp,
  169. DeviceOrientation.portraitDown,
  170. DeviceOrientation.landscapeLeft,
  171. DeviceOrientation.landscapeRight,
  172. ],
  173. this.routePageBuilder = null,
  174. }) : assert(videoPlayerController != null,
  175. 'You must provide a controller to play a video') {
  176. _initialize();
  177. }
  178. /// The controller for the video you want to play
  179. final VideoPlayerController videoPlayerController;
  180. /// Initialize the Video on Startup. This will prep the video for playback.
  181. final bool autoInitialize;
  182. /// Play the video as soon as it's displayed
  183. final bool autoPlay;
  184. /// Start video at a certain position
  185. final Duration startAt;
  186. /// Whether or not the video should loop
  187. final bool looping;
  188. /// Weather or not to show the controls when initializing the widget.
  189. final bool showControlsOnInitialize;
  190. /// Whether or not to show the controls at all
  191. final bool showControls;
  192. /// Defines customised controls. Check [MaterialControls] or
  193. /// [CupertinoControls] for reference.
  194. final Widget customControls;
  195. /// When the video playback runs into an error, you can build a custom
  196. /// error message.
  197. final Widget Function(BuildContext context, String errorMessage) errorBuilder;
  198. /// The Aspect Ratio of the Video. Important to get the correct size of the
  199. /// video!
  200. ///
  201. /// Will fallback to fitting within the space allowed.
  202. final double aspectRatio;
  203. /// The colors to use for controls on iOS. By default, the iOS player uses
  204. /// colors sampled from the original iOS 11 designs.
  205. final ChewieProgressColors cupertinoProgressColors;
  206. /// The colors to use for the Material Progress Bar. By default, the Material
  207. /// player uses the colors from your Theme.
  208. final ChewieProgressColors materialProgressColors;
  209. /// The placeholder is displayed underneath the Video before it is initialized
  210. /// or played.
  211. final Widget placeholder;
  212. /// A widget which is placed between the video and the controls
  213. final Widget overlay;
  214. /// Defines if the player will start in fullscreen when play is pressed
  215. final bool fullScreenByDefault;
  216. /// Defines if the player will sleep in fullscreen or not
  217. final bool allowedScreenSleep;
  218. /// Defines if the controls should be for live stream video
  219. final bool isLive;
  220. /// Defines if the fullscreen control should be shown
  221. final bool allowFullScreen;
  222. /// Defines if the mute control should be shown
  223. final bool allowMuting;
  224. /// Defines the system overlays visible after exiting fullscreen
  225. final List<SystemUiOverlay> systemOverlaysAfterFullScreen;
  226. /// Defines the set of allowed device orientations after exiting fullscreen
  227. final List<DeviceOrientation> deviceOrientationsAfterFullScreen;
  228. /// Defines a custom RoutePageBuilder for the fullscreen
  229. final ChewieRoutePageBuilder routePageBuilder;
  230. static ChewieController of(BuildContext context) {
  231. final chewieControllerProvider =
  232. context.inheritFromWidgetOfExactType(_ChewieControllerProvider)
  233. as _ChewieControllerProvider;
  234. return chewieControllerProvider.controller;
  235. }
  236. bool _isFullScreen = false;
  237. bool get isFullScreen => _isFullScreen;
  238. Future _initialize() async {
  239. await videoPlayerController.setLooping(looping);
  240. if ((autoInitialize || autoPlay) &&
  241. !videoPlayerController.value.initialized) {
  242. await videoPlayerController.initialize();
  243. }
  244. if (autoPlay) {
  245. if (fullScreenByDefault) {
  246. enterFullScreen();
  247. }
  248. await videoPlayerController.play();
  249. }
  250. if (startAt != null) {
  251. await videoPlayerController.seekTo(startAt);
  252. }
  253. if (fullScreenByDefault) {
  254. videoPlayerController.addListener(_fullScreenListener);
  255. }
  256. }
  257. void _fullScreenListener() async {
  258. if (videoPlayerController.value.isPlaying && !_isFullScreen) {
  259. enterFullScreen();
  260. videoPlayerController.removeListener(_fullScreenListener);
  261. }
  262. }
  263. void enterFullScreen() {
  264. _isFullScreen = true;
  265. notifyListeners();
  266. }
  267. void exitFullScreen() {
  268. _isFullScreen = false;
  269. notifyListeners();
  270. }
  271. void toggleFullScreen() {
  272. _isFullScreen = !_isFullScreen;
  273. notifyListeners();
  274. }
  275. Future<void> play() async {
  276. await videoPlayerController.play();
  277. }
  278. Future<void> setLooping(bool looping) async {
  279. await videoPlayerController.setLooping(looping);
  280. }
  281. Future<void> pause() async {
  282. await videoPlayerController.pause();
  283. }
  284. Future<void> seekTo(Duration moment) async {
  285. await videoPlayerController.seekTo(moment);
  286. }
  287. Future<void> setVolume(double volume) async {
  288. await videoPlayerController.setVolume(volume);
  289. }
  290. }
  291. class _ChewieControllerProvider extends InheritedWidget {
  292. const _ChewieControllerProvider({
  293. Key key,
  294. @required this.controller,
  295. @required Widget child,
  296. }) : assert(controller != null),
  297. assert(child != null),
  298. super(key: key, child: child);
  299. final ChewieController controller;
  300. @override
  301. bool updateShouldNotify(_ChewieControllerProvider old) =>
  302. controller != old.controller;
  303. }