| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // ignore_for_file: public_member_api_docs |
| |
| /// 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'; |
| |
| void main() { |
| runApp( |
| MaterialApp( |
| home: _App(), |
| ), |
| ); |
| } |
| |
| class _App extends StatelessWidget { |
| @override |
| Widget build(BuildContext context) { |
| return DefaultTabController( |
| length: 3, |
| child: Scaffold( |
| key: const ValueKey<String>('home_page'), |
| appBar: AppBar( |
| title: const Text('Video player example'), |
| actions: <Widget>[ |
| IconButton( |
| key: const ValueKey<String>('push_tab'), |
| icon: const Icon(Icons.navigation), |
| onPressed: () { |
| Navigator.push<_PlayerVideoAndPopPage>( |
| context, |
| MaterialPageRoute<_PlayerVideoAndPopPage>( |
| builder: (BuildContext context) => _PlayerVideoAndPopPage(), |
| ), |
| ); |
| }, |
| ) |
| ], |
| bottom: const TabBar( |
| isScrollable: true, |
| tabs: <Widget>[ |
| Tab( |
| icon: Icon(Icons.cloud), |
| text: "Remote", |
| ), |
| Tab(icon: Icon(Icons.insert_drive_file), text: "Asset"), |
| Tab(icon: Icon(Icons.list), text: "List example"), |
| ], |
| ), |
| ), |
| body: TabBarView( |
| children: <Widget>[ |
| _BumbleBeeRemoteVideo(), |
| _ButterFlyAssetVideo(), |
| _ButterFlyAssetVideoInList(), |
| ], |
| ), |
| ), |
| ); |
| } |
| } |
| |
| class _ButterFlyAssetVideoInList extends StatelessWidget { |
| @override |
| Widget build(BuildContext context) { |
| return ListView( |
| children: <Widget>[ |
| _ExampleCard(title: "Item a"), |
| _ExampleCard(title: "Item b"), |
| _ExampleCard(title: "Item c"), |
| _ExampleCard(title: "Item d"), |
| _ExampleCard(title: "Item e"), |
| _ExampleCard(title: "Item f"), |
| _ExampleCard(title: "Item g"), |
| Card( |
| child: Column(children: <Widget>[ |
| Column( |
| children: <Widget>[ |
| const ListTile( |
| leading: Icon(Icons.cake), |
| title: Text("Video video"), |
| ), |
| Stack( |
| alignment: FractionalOffset.bottomRight + |
| const FractionalOffset(-0.1, -0.1), |
| children: <Widget>[ |
| _ButterFlyAssetVideo(), |
| Image.asset('assets/flutter-mark-square-64.png'), |
| ]), |
| ], |
| ), |
| ])), |
| _ExampleCard(title: "Item h"), |
| _ExampleCard(title: "Item i"), |
| _ExampleCard(title: "Item j"), |
| _ExampleCard(title: "Item k"), |
| _ExampleCard(title: "Item l"), |
| ], |
| ); |
| } |
| } |
| |
| /// A filler card to show the video in a list of scrolling contents. |
| class _ExampleCard extends StatelessWidget { |
| const _ExampleCard({Key key, this.title}) : super(key: key); |
| |
| final String title; |
| |
| @override |
| Widget build(BuildContext context) { |
| return Card( |
| child: Column( |
| mainAxisSize: MainAxisSize.min, |
| children: <Widget>[ |
| ListTile( |
| leading: const Icon(Icons.airline_seat_flat_angled), |
| title: Text(title), |
| ), |
| ButtonBar( |
| children: <Widget>[ |
| FlatButton( |
| child: const Text('BUY TICKETS'), |
| onPressed: () { |
| /* ... */ |
| }, |
| ), |
| FlatButton( |
| child: const Text('SELL TICKETS'), |
| onPressed: () { |
| /* ... */ |
| }, |
| ), |
| ], |
| ), |
| ], |
| ), |
| ); |
| } |
| } |
| |
| class _ButterFlyAssetVideo extends StatefulWidget { |
| @override |
| _ButterFlyAssetVideoState createState() => _ButterFlyAssetVideoState(); |
| } |
| |
| class _ButterFlyAssetVideoState extends State<_ButterFlyAssetVideo> { |
| VideoPlayerController _controller; |
| |
| @override |
| void initState() { |
| super.initState(); |
| _controller = VideoPlayerController.asset('assets/Butterfly-209.mp4'); |
| |
| _controller.addListener(() { |
| setState(() {}); |
| }); |
| _controller.setLooping(true); |
| _controller.initialize().then((_) => setState(() {})); |
| _controller.play(); |
| } |
| |
| @override |
| void dispose() { |
| _controller.dispose(); |
| super.dispose(); |
| } |
| |
| @override |
| Widget build(BuildContext context) { |
| return SingleChildScrollView( |
| child: Column( |
| children: <Widget>[ |
| Container( |
| padding: const EdgeInsets.only(top: 20.0), |
| ), |
| const Text('With assets mp4'), |
| Container( |
| padding: const EdgeInsets.all(20), |
| child: AspectRatio( |
| aspectRatio: _controller.value.aspectRatio, |
| child: Stack( |
| alignment: Alignment.bottomCenter, |
| children: <Widget>[ |
| VideoPlayer(_controller), |
| _ControlsOverlay(controller: _controller), |
| VideoProgressIndicator(_controller, allowScrubbing: true), |
| ], |
| ), |
| ), |
| ), |
| ], |
| ), |
| ); |
| } |
| } |
| |
| class _BumbleBeeRemoteVideo extends StatefulWidget { |
| @override |
| _BumbleBeeRemoteVideoState createState() => _BumbleBeeRemoteVideoState(); |
| } |
| |
| class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { |
| VideoPlayerController _controller; |
| |
| Future<ClosedCaptionFile> _loadCaptions() async { |
| final String fileContents = await DefaultAssetBundle.of(context) |
| .loadString('assets/bumble_bee_captions.srt'); |
| return SubRipCaptionFile(fileContents); |
| } |
| |
| @override |
| void initState() { |
| super.initState(); |
| _controller = VideoPlayerController.network( |
| 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4', |
| closedCaptionFile: _loadCaptions(), |
| videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true), |
| ); |
| |
| _controller.addListener(() { |
| setState(() {}); |
| }); |
| _controller.setLooping(true); |
| _controller.initialize(); |
| } |
| |
| @override |
| void dispose() { |
| _controller.dispose(); |
| super.dispose(); |
| } |
| |
| @override |
| Widget build(BuildContext context) { |
| return SingleChildScrollView( |
| child: Column( |
| children: <Widget>[ |
| Container(padding: const EdgeInsets.only(top: 20.0)), |
| const Text('With remote mp4'), |
| Container( |
| padding: const EdgeInsets.all(20), |
| child: AspectRatio( |
| aspectRatio: _controller.value.aspectRatio, |
| child: Stack( |
| alignment: Alignment.bottomCenter, |
| children: <Widget>[ |
| VideoPlayer(_controller), |
| ClosedCaption(text: _controller.value.caption.text), |
| _ControlsOverlay(controller: _controller), |
| VideoProgressIndicator(_controller, allowScrubbing: true), |
| ], |
| ), |
| ), |
| ), |
| ], |
| ), |
| ); |
| } |
| } |
| |
| class _ControlsOverlay extends StatelessWidget { |
| const _ControlsOverlay({Key key, this.controller}) : super(key: key); |
| |
| static const _examplePlaybackRates = [ |
| 0.25, |
| 0.5, |
| 1.0, |
| 1.5, |
| 2.0, |
| 3.0, |
| 5.0, |
| 10.0, |
| ]; |
| |
| final VideoPlayerController controller; |
| |
| @override |
| Widget build(BuildContext context) { |
| return Stack( |
| children: <Widget>[ |
| AnimatedSwitcher( |
| duration: Duration(milliseconds: 50), |
| reverseDuration: Duration(milliseconds: 200), |
| child: controller.value.isPlaying |
| ? SizedBox.shrink() |
| : Container( |
| color: Colors.black26, |
| child: Center( |
| child: Icon( |
| Icons.play_arrow, |
| color: Colors.white, |
| size: 100.0, |
| ), |
| ), |
| ), |
| ), |
| GestureDetector( |
| onTap: () { |
| controller.value.isPlaying ? controller.pause() : controller.play(); |
| }, |
| ), |
| Align( |
| alignment: Alignment.topRight, |
| child: PopupMenuButton<double>( |
| initialValue: controller.value.playbackSpeed, |
| tooltip: 'Playback speed', |
| onSelected: (speed) { |
| controller.setPlaybackSpeed(speed); |
| }, |
| itemBuilder: (context) { |
| return [ |
| for (final speed in _examplePlaybackRates) |
| PopupMenuItem( |
| value: speed, |
| child: Text('${speed}x'), |
| ) |
| ]; |
| }, |
| child: Padding( |
| padding: const EdgeInsets.symmetric( |
| // Using less vertical padding as the text is also longer |
| // horizontally, so it feels like it would need more spacing |
| // horizontally (matching the aspect ratio of the video). |
| vertical: 12, |
| horizontal: 16, |
| ), |
| child: Text('${controller.value.playbackSpeed}x'), |
| ), |
| ), |
| ), |
| ], |
| ); |
| } |
| } |
| |
| class _PlayerVideoAndPopPage extends StatefulWidget { |
| @override |
| _PlayerVideoAndPopPageState createState() => _PlayerVideoAndPopPageState(); |
| } |
| |
| class _PlayerVideoAndPopPageState extends State<_PlayerVideoAndPopPage> { |
| VideoPlayerController _videoPlayerController; |
| bool startedPlaying = false; |
| |
| @override |
| void initState() { |
| super.initState(); |
| |
| _videoPlayerController = |
| VideoPlayerController.asset('assets/Butterfly-209.mp4'); |
| _videoPlayerController.addListener(() { |
| if (startedPlaying && !_videoPlayerController.value.isPlaying) { |
| Navigator.pop(context); |
| } |
| }); |
| } |
| |
| @override |
| void dispose() { |
| _videoPlayerController.dispose(); |
| super.dispose(); |
| } |
| |
| Future<bool> started() async { |
| await _videoPlayerController.initialize(); |
| await _videoPlayerController.play(); |
| startedPlaying = true; |
| return true; |
| } |
| |
| @override |
| Widget build(BuildContext context) { |
| return Material( |
| elevation: 0, |
| child: Center( |
| child: FutureBuilder<bool>( |
| future: started(), |
| builder: (BuildContext context, AsyncSnapshot<bool> snapshot) { |
| if (snapshot.data == true) { |
| return AspectRatio( |
| aspectRatio: _videoPlayerController.value.aspectRatio, |
| child: VideoPlayer(_videoPlayerController), |
| ); |
| } else { |
| return const Text('waiting for video to load'); |
| } |
| }, |
| ), |
| ), |
| ); |
| } |
| } |