From da99e338b5f3f7d3adfeec2d067a55003ca3c1e8 Mon Sep 17 00:00:00 2001 From: ianko Date: Wed, 10 Jul 2019 14:21:49 -0400 Subject: [PATCH] refactor example app --- packages/video_player/CHANGELOG.md | 9 + packages/video_player/example/lib/main.dart | 426 ++---------------- packages/video_player/example/lib/player.dart | 221 +++++++++ .../example/lib/player_values.dart | 75 +++ .../example/lib/tabs/asset_tab.dart | 26 ++ .../example/lib/tabs/list_tab.dart | 84 ++++ .../example/lib/tabs/live_tab.dart | 23 + .../example/lib/tabs/remote_tab.dart | 26 ++ .../example/lib/video_provider.dart | 100 ++++ packages/video_player/pubspec.yaml | 2 +- 10 files changed, 595 insertions(+), 397 deletions(-) create mode 100644 packages/video_player/example/lib/player.dart create mode 100644 packages/video_player/example/lib/player_values.dart create mode 100644 packages/video_player/example/lib/tabs/asset_tab.dart create mode 100644 packages/video_player/example/lib/tabs/list_tab.dart create mode 100644 packages/video_player/example/lib/tabs/live_tab.dart create mode 100644 packages/video_player/example/lib/tabs/remote_tab.dart create mode 100644 packages/video_player/example/lib/video_provider.dart diff --git a/packages/video_player/CHANGELOG.md b/packages/video_player/CHANGELOG.md index 559456c14908..ec31280d6d3c 100644 --- a/packages/video_player/CHANGELOG.md +++ b/packages/video_player/CHANGELOG.md @@ -1,3 +1,12 @@ +## 0.10.2+1 + +* Refactor the example app. +* Add the Live Stream tab to the example app. + +## 0.10.2 + +* iOS: Revert the changes made in version `0.10.1` that made live streams not able to initialize on the absence of `duration`. + ## 0.10.1+3 * Add missing template type parameter to `invokeMethod` calls. diff --git a/packages/video_player/example/lib/main.dart b/packages/video_player/example/lib/main.dart index 320df27c8e3e..b89e85290074 100644 --- a/packages/video_player/example/lib/main.dart +++ b/packages/video_player/example/lib/main.dart @@ -5,422 +5,56 @@ /// An example of using the plugin, controlling lifecycle and playback of the /// video. -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:video_player/video_player.dart'; - -/// Controls play and pause of [controller]. -/// -/// Toggles play/pause on tap (accompanied by a fading status icon). -/// -/// Plays (looping) on initialization, and mutes on deactivation. -class VideoPlayPause extends StatefulWidget { - VideoPlayPause(this.controller); - - final VideoPlayerController controller; - - @override - State createState() { - return _VideoPlayPauseState(); - } -} - -class _VideoPlayPauseState extends State { - _VideoPlayPauseState() { - listener = () { - setState(() {}); - }; - } - - FadeAnimation imageFadeAnim = - FadeAnimation(child: const Icon(Icons.play_arrow, size: 100.0)); - VoidCallback listener; - - VideoPlayerController get controller => widget.controller; - - @override - void initState() { - super.initState(); - controller.addListener(listener); - controller.setVolume(1.0); - controller.play(); - } - - @override - void deactivate() { - controller.setVolume(0.0); - controller.removeListener(listener); - super.deactivate(); - } - - @override - Widget build(BuildContext context) { - final List children = [ - GestureDetector( - child: VideoPlayer(controller), - onTap: () { - if (!controller.value.initialized) { - return; - } - if (controller.value.isPlaying) { - imageFadeAnim = - FadeAnimation(child: const Icon(Icons.pause, size: 100.0)); - controller.pause(); - } else { - imageFadeAnim = - FadeAnimation(child: const Icon(Icons.play_arrow, size: 100.0)); - controller.play(); - } - }, - ), - Align( - alignment: Alignment.bottomCenter, - child: VideoProgressIndicator( - controller, - allowScrubbing: true, - ), - ), - Center(child: imageFadeAnim), - Center( - child: controller.value.isBuffering - ? const CircularProgressIndicator() - : null), - ]; - - return Stack( - fit: StackFit.passthrough, - children: children, - ); - } -} - -class FadeAnimation extends StatefulWidget { - FadeAnimation( - {this.child, this.duration = const Duration(milliseconds: 500)}); - - final Widget child; - final Duration duration; - - @override - _FadeAnimationState createState() => _FadeAnimationState(); -} - -class _FadeAnimationState extends State - with SingleTickerProviderStateMixin { - AnimationController animationController; - - @override - void initState() { - super.initState(); - animationController = - AnimationController(duration: widget.duration, vsync: this); - animationController.addListener(() { - if (mounted) { - setState(() {}); - } - }); - animationController.forward(from: 0.0); - } - - @override - void deactivate() { - animationController.stop(); - super.deactivate(); - } - - @override - void didUpdateWidget(FadeAnimation oldWidget) { - super.didUpdateWidget(oldWidget); - if (oldWidget.child != widget.child) { - animationController.forward(from: 0.0); - } - } - - @override - void dispose() { - animationController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return animationController.isAnimating - ? Opacity( - opacity: 1.0 - animationController.value, - child: widget.child, - ) - : Container(); - } -} - -typedef Widget VideoWidgetBuilder( - BuildContext context, VideoPlayerController controller); - -abstract class PlayerLifeCycle extends StatefulWidget { - PlayerLifeCycle(this.dataSource, this.childBuilder); - - final VideoWidgetBuilder childBuilder; - final String dataSource; -} - -/// A widget connecting its life cycle to a [VideoPlayerController] using -/// a data source from the network. -class NetworkPlayerLifeCycle extends PlayerLifeCycle { - NetworkPlayerLifeCycle(String dataSource, VideoWidgetBuilder childBuilder) - : super(dataSource, childBuilder); - - @override - _NetworkPlayerLifeCycleState createState() => _NetworkPlayerLifeCycleState(); -} - -/// A widget connecting its life cycle to a [VideoPlayerController] using -/// an asset as data source -class AssetPlayerLifeCycle extends PlayerLifeCycle { - AssetPlayerLifeCycle(String dataSource, VideoWidgetBuilder childBuilder) - : super(dataSource, childBuilder); - - @override - _AssetPlayerLifeCycleState createState() => _AssetPlayerLifeCycleState(); -} - -abstract class _PlayerLifeCycleState extends State { - VideoPlayerController controller; - - @override - - /// Subclasses should implement [createVideoPlayerController], which is used - /// by this method. - void initState() { - super.initState(); - controller = createVideoPlayerController(); - controller.addListener(() { - if (controller.value.hasError) { - print(controller.value.errorDescription); - } - }); - controller.initialize(); - controller.setLooping(true); - controller.play(); - } - - @override - void deactivate() { - super.deactivate(); - } - - @override - void dispose() { - controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return widget.childBuilder(context, controller); - } - - VideoPlayerController createVideoPlayerController(); -} - -class _NetworkPlayerLifeCycleState extends _PlayerLifeCycleState { - @override - VideoPlayerController createVideoPlayerController() { - return VideoPlayerController.network(widget.dataSource); - } -} - -class _AssetPlayerLifeCycleState extends _PlayerLifeCycleState { - @override - VideoPlayerController createVideoPlayerController() { - return VideoPlayerController.asset(widget.dataSource); - } -} - -/// A filler card to show the video in a list of scrolling contents. -Widget buildCard(String title) { - return Card( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - ListTile( - leading: const Icon(Icons.airline_seat_flat_angled), - title: Text(title), - ), - ButtonTheme.bar( - child: ButtonBar( - children: [ - FlatButton( - child: const Text('BUY TICKETS'), - onPressed: () { - /* ... */ - }, - ), - FlatButton( - child: const Text('SELL TICKETS'), - onPressed: () { - /* ... */ - }, - ), - ], - ), - ), - ], - ), - ); -} - -class VideoInListOfCards extends StatelessWidget { - VideoInListOfCards(this.controller); - - final VideoPlayerController controller; - - @override - Widget build(BuildContext context) { - return ListView( - children: [ - buildCard("Item a"), - buildCard("Item b"), - buildCard("Item c"), - buildCard("Item d"), - buildCard("Item e"), - buildCard("Item f"), - buildCard("Item g"), - Card( - child: Column(children: [ - Column( - children: [ - const ListTile( - leading: Icon(Icons.cake), - title: Text("Video video"), - ), - Stack( - alignment: FractionalOffset.bottomRight + - const FractionalOffset(-0.1, -0.1), - children: [ - AspectRatioVideo(controller), - Image.asset('assets/flutter-mark-square-64.png'), - ]), - ], - ), - ])), - buildCard("Item h"), - buildCard("Item i"), - buildCard("Item j"), - buildCard("Item k"), - buildCard("Item l"), - ], - ); - } -} - -class AspectRatioVideo extends StatefulWidget { - AspectRatioVideo(this.controller); - - final VideoPlayerController controller; - - @override - AspectRatioVideoState createState() => AspectRatioVideoState(); -} - -class AspectRatioVideoState extends State { - VideoPlayerController get controller => widget.controller; - bool initialized = false; - - VoidCallback listener; - - @override - void initState() { - super.initState(); - listener = () { - if (!mounted) { - return; - } - if (initialized != controller.value.initialized) { - initialized = controller.value.initialized; - setState(() {}); - } - }; - controller.addListener(listener); - } - - @override - Widget build(BuildContext context) { - if (initialized) { - return Center( - child: AspectRatio( - aspectRatio: controller.value.aspectRatio, - child: VideoPlayPause(controller), - ), - ); - } else { - return Container(); - } - } -} +import './tabs/asset_tab.dart'; +import './tabs/list_tab.dart'; +import './tabs/live_tab.dart'; +import './tabs/remote_tab.dart'; +import './video_provider.dart'; + +// video urls +const String _kAssetPath = 'assets/Butterfly-209.mp4'; +const String _kRemoteUrl = + 'http://184.72.239.149/vod/smil:BigBuckBunny.smil/playlist.m3u8'; +const String _kLiveUrl = + 'https://videos3.earthcam.com/fecnetwork/16560.flv/playlist.m3u8'; void main() { runApp( MaterialApp( home: DefaultTabController( - length: 3, + length: 4, child: Scaffold( appBar: AppBar( title: const Text('Video player example'), bottom: const TabBar( isScrollable: true, tabs: [ - Tab( - icon: Icon(Icons.cloud), - text: "Remote", - ), + Tab(icon: Icon(Icons.cloud), text: "Remote"), + Tab(icon: Icon(Icons.live_tv), text: "Live"), Tab(icon: Icon(Icons.insert_drive_file), text: "Asset"), Tab(icon: Icon(Icons.list), text: "List example"), ], ), ), body: TabBarView( - children: [ - SingleChildScrollView( - child: Column( - children: [ - Container( - padding: const EdgeInsets.only(top: 20.0), - ), - const Text('With remote m3u8'), - Container( - padding: const EdgeInsets.all(20), - child: NetworkPlayerLifeCycle( - 'http://184.72.239.149/vod/smil:BigBuckBunny.smil/playlist.m3u8', - (BuildContext context, - VideoPlayerController controller) => - AspectRatioVideo(controller), - ), - ), - ], - ), + children: const [ + VideoControllerProvider.network( + source: _kRemoteUrl, + child: RemoteTab(), + ), + VideoControllerProvider.network( + source: _kLiveUrl, + child: LiveTab(), + ), + VideoControllerProvider.asset( + source: _kAssetPath, + child: AssetTab(), ), - SingleChildScrollView( - child: Column( - children: [ - Container( - padding: const EdgeInsets.only(top: 20.0), - ), - const Text('With assets mp4'), - Container( - padding: const EdgeInsets.all(20), - child: AssetPlayerLifeCycle( - 'assets/Butterfly-209.mp4', - (BuildContext context, - VideoPlayerController controller) => - AspectRatioVideo(controller)), - ), - ], - ), + VideoControllerProvider.asset( + source: _kAssetPath, + child: ListTab(), ), - AssetPlayerLifeCycle( - 'assets/Butterfly-209.mp4', - (BuildContext context, VideoPlayerController controller) => - VideoInListOfCards(controller)), ], ), ), diff --git a/packages/video_player/example/lib/player.dart b/packages/video_player/example/lib/player.dart new file mode 100644 index 000000000000..f90c87fd381b --- /dev/null +++ b/packages/video_player/example/lib/player.dart @@ -0,0 +1,221 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import './video_provider.dart'; + +class Player extends StatefulWidget { + const Player({this.isLive = false}); + + final bool isLive; + + @override + _PlayerState createState() => _PlayerState(); +} + +class _PlayerState extends State { + VideoPlayerController controller; + bool initialized = false; + bool playing = false; + bool buffering = true; + bool active = true; + PlayPauseButton playPauseButton; + + @override + void initState() { + super.initState(); + controller = VideoControllerProvider.of(context); + _initialize(); + } + + @override + void dispose() async { + controller?.removeListener(_listener); + super.dispose(); + } + + Future _initialize() async { + controller.addListener(_listener); + await controller.initialize(); + await controller.play(); + } + + Future _playPause() async { + if (!controller.value.initialized) { + return; + } + + if (controller.value.isPlaying) { + await controller.pause(); + } else { + await controller.play(); + } + } + + void _listener() { + if (!mounted) { + return; + } + + if (!initialized && controller.value.initialized) { + return _refresh(); + } + + if (playing != controller.value.isPlaying) { + return _refresh(); + } + + if (buffering != controller.value.isBuffering) { + return _refresh(); + } + } + + void _refresh() { + if (!active) { + return; + } + + setState(() { + initialized = controller.value.initialized; + buffering = controller.value.isBuffering; + playing = controller.value.isPlaying; + playPauseButton = + playing ? PlayPauseButton.playing() : PlayPauseButton.paused(); + }); + } + + @override + Widget build(BuildContext context) { + if (!initialized) { + return const AspectRatio( + aspectRatio: 16.0 / 9.0, + child: Center(child: CircularProgressIndicator()), + ); + } + + final List children = [ + GestureDetector( + onTap: _playPause, + child: VideoPlayer(controller), + ), + widget.isLive ? _buildLiveIndicator() : _buildProgressIndicator(), + ]; + + if (playPauseButton != null) { + children.add(Center(child: playPauseButton)); + } + + if (controller.value.isBuffering) { + children.add(const Center(child: CircularProgressIndicator())); + } + + return Container( + child: AspectRatio( + aspectRatio: controller.value.aspectRatio, + child: Stack( + fit: StackFit.passthrough, + children: children, + ), + ), + ); + } + + Widget _buildLiveIndicator() { + return Align( + alignment: Alignment.topLeft, + child: Container( + margin: const EdgeInsets.all(8.0), + padding: const EdgeInsets.symmetric(vertical: 3.0, horizontal: 8.0), + decoration: const BoxDecoration( + color: Colors.red, + borderRadius: BorderRadius.all(Radius.circular(4.0)), + ), + child: const Text( + 'LIVE', + style: TextStyle( + color: Colors.white, + fontSize: 12.0, + fontWeight: FontWeight.w600, + ), + ), + ), + ); + } + + Widget _buildProgressIndicator() { + return Align( + alignment: Alignment.bottomCenter, + child: VideoProgressIndicator(controller, allowScrubbing: true), + ); + } +} + +class PlayPauseButton extends StatefulWidget { + PlayPauseButton({ + this.child, + this.duration = const Duration(milliseconds: 500), + }); + + PlayPauseButton.playing() + : child = const Icon(Icons.play_arrow, size: 100.0), + duration = const Duration(milliseconds: 500); + + PlayPauseButton.paused() + : child = const Icon(Icons.pause, size: 100.0), + duration = const Duration(milliseconds: 500); + + final Widget child; + final Duration duration; + + @override + _PlayPauseButtonState createState() => _PlayPauseButtonState(); +} + +class _PlayPauseButtonState extends State + with SingleTickerProviderStateMixin { + AnimationController animationController; + + @override + void initState() { + super.initState(); + animationController = + AnimationController(duration: widget.duration, vsync: this); + + animationController.addListener(() { + if (mounted) { + setState(() {}); + } + }); + + animationController.forward(from: 0.0); + } + + @override + void deactivate() { + animationController.stop(); + super.deactivate(); + } + + @override + void didUpdateWidget(PlayPauseButton oldWidget) { + super.didUpdateWidget(oldWidget); + + if (oldWidget.child != widget.child) { + animationController.forward(from: 0.0); + } + } + + @override + void dispose() { + animationController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return animationController.isAnimating + ? Opacity( + opacity: 1.0 - animationController.value, + child: widget.child, + ) + : Container(); + } +} diff --git a/packages/video_player/example/lib/player_values.dart b/packages/video_player/example/lib/player_values.dart new file mode 100644 index 000000000000..450e2089b427 --- /dev/null +++ b/packages/video_player/example/lib/player_values.dart @@ -0,0 +1,75 @@ +import 'package:flutter/material.dart'; +import './video_provider.dart'; + +class PlayerValues extends StatefulWidget { + const PlayerValues({Key key}); + + @override + _PlayerValuesState createState() => _PlayerValuesState(); +} + +class _PlayerValuesState extends State { + VideoPlayerController controller; + + @override + void initState() { + super.initState(); + controller = VideoControllerProvider.of(context); + controller.addListener(_listener); + } + + @override + void dispose() { + controller.removeListener(_listener); + super.dispose(); + } + + void _listener() { + if (!mounted) { + return; + } + + setState(() {}); + } + + @override + Widget build(BuildContext context) { + final VideoPlayerValue v = controller.value; + + if (!v.initialized) { + return const SizedBox(); + } + + if (v.errorDescription != null) { + return Text(v.errorDescription, style: TextStyle(color: Colors.red)); + } + + final List children = [ + _row('Status:', Icon(v.isPlaying ? Icons.play_arrow : Icons.pause)), + _row('Size: ', Text('${v.size.width} x ${v.size.height}')), + _row('Volume: ', Text(v.volume.toString())), + _row('Looping? ', Text(v.isLooping.toString())), + _row('Buffering? ', Text(v.isBuffering.toString())), + _row('Position: ', Text(v.position.toString())), + _row('Duration: ', Text(v.duration.toString())), + ]; + + if (v.buffered.isEmpty) { + children.add(_row('Buffered: ', const Text('[]'))); + } else { + children.add(_row('Buffered: ', + Text('[${v.buffered.last.start}, ${v.buffered.last.end}]'))); + } + + return Column(children: children); + } + + Widget _row(String title, Widget child) { + return Row( + children: [ + Text(title, style: TextStyle(fontWeight: FontWeight.w600)), + child, + ], + ); + } +} diff --git a/packages/video_player/example/lib/tabs/asset_tab.dart b/packages/video_player/example/lib/tabs/asset_tab.dart new file mode 100644 index 000000000000..5c555aa78987 --- /dev/null +++ b/packages/video_player/example/lib/tabs/asset_tab.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import '../player.dart'; +import '../player_values.dart'; +import '../video_provider.dart'; + +class AssetTab extends StatelessWidget { + const AssetTab({Key key}) : super(key: key); + + @override + Widget build(BuildContext context) { + VideoControllerProvider.of(context)..setLooping(true); + + return SingleChildScrollView( + padding: const EdgeInsets.all(20.0), + child: Column( + children: const [ + Text('With assets mp4'), + SizedBox(height: 20.0), + Player(), + SizedBox(height: 20.0), + PlayerValues(), + ], + ), + ); + } +} diff --git a/packages/video_player/example/lib/tabs/list_tab.dart b/packages/video_player/example/lib/tabs/list_tab.dart new file mode 100644 index 000000000000..d938d3ea2023 --- /dev/null +++ b/packages/video_player/example/lib/tabs/list_tab.dart @@ -0,0 +1,84 @@ +import 'package:flutter/material.dart'; +import '../player.dart'; +import '../video_provider.dart'; + +class ListTab extends StatelessWidget { + const ListTab({Key key}) : super(key: key); + + @override + Widget build(BuildContext context) { + VideoControllerProvider.of(context)..setLooping(true); + + return ListView( + children: [ + _buildCard("Item a"), + _buildCard("Item b"), + _buildCard("Item c"), + _buildCard("Item d"), + _buildCard("Item e"), + _buildCard("Item f"), + _buildCard("Item g"), + Card( + child: Column( + children: [ + Column( + children: [ + const ListTile( + leading: Icon(Icons.cake), + title: Text("Video video"), + ), + Stack( + alignment: FractionalOffset.bottomRight + + const FractionalOffset(-0.1, -0.1), + children: [ + const Player(), + Image.asset('assets/flutter-mark-square-64.png'), + ], + ), + ], + ), + ], + ), + ), + _buildCard("Item h"), + _buildCard("Item i"), + _buildCard("Item j"), + _buildCard("Item k"), + _buildCard("Item l"), + ], + ); + } + + /// A filler card to show the video in a list of scrolling contents. + Widget _buildCard(String title) { + return Card( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + leading: const Icon(Icons.airline_seat_flat_angled), + title: Text(title), + ), + ButtonTheme.bar( + child: ButtonBar( + children: [ + FlatButton( + child: const Text('BUY TICKETS'), + onPressed: () { + /* ... */ + }, + ), + FlatButton( + child: const Text('SELL TICKETS'), + onPressed: () { + /* ... */ + }, + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/packages/video_player/example/lib/tabs/live_tab.dart b/packages/video_player/example/lib/tabs/live_tab.dart new file mode 100644 index 000000000000..42a06fe52432 --- /dev/null +++ b/packages/video_player/example/lib/tabs/live_tab.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import '../player.dart'; +import '../player_values.dart'; + +class LiveTab extends StatelessWidget { + const LiveTab({Key key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + padding: const EdgeInsets.all(20.0), + child: Column( + children: const [ + Text('HLS Live stream'), + SizedBox(height: 20.0), + Player(isLive: true), + SizedBox(height: 20.0), + PlayerValues(), + ], + ), + ); + } +} diff --git a/packages/video_player/example/lib/tabs/remote_tab.dart b/packages/video_player/example/lib/tabs/remote_tab.dart new file mode 100644 index 000000000000..07112affad4d --- /dev/null +++ b/packages/video_player/example/lib/tabs/remote_tab.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import '../player.dart'; +import '../player_values.dart'; +import '../video_provider.dart'; + +class RemoteTab extends StatelessWidget { + const RemoteTab({Key key}) : super(key: key); + + @override + Widget build(BuildContext context) { + VideoControllerProvider.of(context)..setLooping(true); + + return SingleChildScrollView( + padding: const EdgeInsets.all(20.0), + child: Column( + children: const [ + Text('With remote m3u8'), + SizedBox(height: 20.0), + Player(), + SizedBox(height: 20.0), + PlayerValues(), + ], + ), + ); + } +} diff --git a/packages/video_player/example/lib/video_provider.dart b/packages/video_player/example/lib/video_provider.dart new file mode 100644 index 000000000000..38264c1e70b1 --- /dev/null +++ b/packages/video_player/example/lib/video_provider.dart @@ -0,0 +1,100 @@ +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:video_player/video_player.dart'; + +export 'package:video_player/video_player.dart'; + +class VideoControllerProvider extends StatefulWidget { + const VideoControllerProvider.asset({ + Key key, + @required this.source, + @required this.child, + }) : type = DataSourceType.asset, + _file = null, + super(key: key); + + const VideoControllerProvider.network({ + Key key, + @required this.source, + @required this.child, + }) : type = DataSourceType.network, + _file = null, + super(key: key); + + const VideoControllerProvider.file({ + Key key, + File file, + @required this.child, + }) : _file = file, + type = DataSourceType.file, + source = null, + super(key: key); + + final Widget child; + final String source; + final File _file; + final DataSourceType type; + + @override + _VideoProviderState createState() => _VideoProviderState(); + + static VideoPlayerController of(BuildContext context) { + final _VideoProviderInherited provider = context + .ancestorInheritedElementForWidgetOfExactType(_VideoProviderInherited) + ?.widget; + return provider?.controller; + } +} + +class _VideoProviderState extends State { + VideoPlayerController controller; + + @override + void initState() { + super.initState(); + + switch (widget.type) { + case DataSourceType.asset: + controller = VideoPlayerController.asset(widget.source); + break; + case DataSourceType.network: + controller = VideoPlayerController.network(widget.source); + break; + case DataSourceType.file: + controller = VideoPlayerController.file(widget._file); + break; + default: + throw Exception('Could not create the VideoPlayerController.'); + } + + controller?.setVolume(1.0); + } + + @override + void dispose() { + controller?.setVolume(0.0); + controller?.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return _VideoProviderInherited( + controller: controller, + child: widget.child, + ); + } +} + +class _VideoProviderInherited extends InheritedWidget { + const _VideoProviderInherited({ + Key key, + @required Widget child, + @required this.controller, + }) : super(key: key, child: child); + + final VideoPlayerController controller; + + @override + bool updateShouldNotify(_VideoProviderInherited oldWidget) => false; +} diff --git a/packages/video_player/pubspec.yaml b/packages/video_player/pubspec.yaml index 8ed4025501d0..96374a133405 100644 --- a/packages/video_player/pubspec.yaml +++ b/packages/video_player/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player description: Flutter plugin for displaying inline video with other Flutter widgets on Android and iOS. author: Flutter Team -version: 0.10.1+3 +version: 0.10.2+1 homepage: https://github.com/flutter/plugins/tree/master/packages/video_player flutter: