// Copyright 2013 The Flutter 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: 1, child: Scaffold( key: const ValueKey('home_page'), appBar: AppBar( title: const Text('Video player example'), bottom: const TabBar( isScrollable: true, tabs: [ Tab( icon: Icon(Icons.cloud), text: 'Remote', ), ], ), ), body: TabBarView( children: [ _BumbleBeeRemoteVideo(), ], ), ), ); } } class _BumbleBeeRemoteVideo extends StatefulWidget { @override _BumbleBeeRemoteVideoState createState() => _BumbleBeeRemoteVideoState(); } class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { late VideoPlayerController _controller; @override void initState() { super.initState(); _controller = VideoPlayerController.network( 'https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_cropped_multilingual.webm', 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: [ Container(padding: const EdgeInsets.only(top: 20.0)), const Text('With remote video'), Container( padding: const EdgeInsets.all(20), child: AspectRatio( aspectRatio: _controller.value.aspectRatio, child: Stack( alignment: Alignment.bottomCenter, children: [ VideoPlayer(_controller), ClosedCaption(text: _controller.value.caption.text), _ControlsOverlay(controller: _controller), VideoProgressIndicator(_controller, allowScrubbing: true), ], ), ), ), ], ), ); } } class _ControlsOverlay extends StatelessWidget { const _ControlsOverlay({Key? key, required this.controller}) : super(key: key); static const List _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: [ AnimatedSwitcher( duration: const Duration(milliseconds: 50), reverseDuration: const Duration(milliseconds: 200), child: controller.value.isPlaying ? const SizedBox.shrink() : Container( color: Colors.black26, child: const 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( initialValue: controller.value.playbackSpeed, tooltip: 'Playback speed', onSelected: (speed) { controller.setPlaybackSpeed(speed); }, itemBuilder: (context) { return [ for (final double 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'), ), ), ), ], ); } }