Did you know that Flutter extends beyond just creating visually stunning and responsive UIs? It also enables developers to integrate dynamic features like video previews, a key element in today’s app experiences. With video content being consumed by over 85% of internet users, incorporating video features can significantly enhance user engagement and satisfaction.
In this guide, we’ll focus on video previews within Flutter. Video previews are invaluable for giving users a glimpse of the content, making your app more interactive and appealing. Using Flutter’s capabilities alongside Cloudinary’s SDK for seamless video integration, we’ll walk you through the steps of fetching video content and displaying it effectively within your app.
In this article:
- How do Flutter Videos Work?
- Creating a Flutter Video App
- Insights into Various Flutter Video Formats
How do Flutter Videos Work?
Flutter’s video playback functionality is primarily achieved using the video_player package. This provides a high-level API for controlling video playback, including starting, pausing, seeking, and setting volume.
To play a video in Flutter, you first need to specify the video source (local file, network stream, etc.). Then, you create a VideoPlayerController instance and initialize it. Finally, you use the VideoPlayer widget to display the video content and control playback using the VideoPlayerController’s methods.
Creating a Flutter Video App
For this tutorial, we will be using Android Studio to build out the app. If you don’t have it installed, you can download it from the Android Studio website. Next, you will need to install Flutter, which you can download from the Flutter website. Follow the guide to install the latest version of Flutter.
Initializing our VideoPlayer App in Flutter
Once you have everything set up, open Android Studio and click on the New Flutter Project option.
Next, open the Flutter page from the left panel and click the … button to configure your Flutter SDK path. Once you have it configured, click on Next to go to the next page. Here, we will define our project. Start by giving your project a name. For this tutorial, we will be naming our project as ‘videoapp’.
Now set the project path, and once you are finished setting up the app, click on the sb button to create a blank Flutter project. Here is what our project looks like:
Now head over to your pubspec.yaml
file and add the following dependencies:
dependencies: http: ^0.13.5 video_player: ^2.8.6
With this, our setup is complete, and we can begin building our Flutter Video Player app.
Playing Flutter Video With VideoPlayerController
Now open up your main.dart
file and start by importing the necessary modules:
// Import necessary Flutter packages import 'package:flutter/material.dart'; import 'package:video_player/video_player.dart';
Next, you’ll need to initialize the Flutter app using the MaterialApp widget. In the main()
function, we ensure the Flutter engine is initialized, and then we call runApp()
to load our main widget. Here, MyApp
is a stateless widget that simply renders the VideoApp
widget (which we will define later) and contains our main video player logic:
void main() { WidgetsFlutterBinding.ensureInitialized(); // Ensure the Flutter engine is initialized runApp(MyApp()); } // Main App widget class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: VideoApp(), ); } }
Next, we use a stateful widget, VideoApp
, to handle video playback and UI updates. Stateful widgets are used for elements that change over time, like a video player. While the VideoApp
class doesn’t directly manage playback, it sets up an internal state (_VideoAppState
) responsible for handling the video player and refreshing the UI accordingly.
Here’s how we define the VideoApp
class:
// Stateful widget to manage video playback class VideoApp extends StatefulWidget { @override _VideoAppState createState() => _VideoAppState(); }
Now, we will create our _VideoAppState
class, which will contain the main logic of our project and will run our videos. We begin by defining three variables: VideoPlayerController
for running our Videos, a Future
array for video initialization, and a double
variable storing our playback speed:
class _VideoAppState extends State<VideoApp> { late VideoPlayerController _controller; // Late initialization of video controller late Future<void> _initializeVideoPlayerFuture; // Future for video initialization double _playbackSpeed = 1.0; // Variable to store the playback speed
Next, we will define the initState
method, which is called once when the widget is inserted into the widget tree. This method starts by calling super.initState()
to handle the essential setup. We then initialize the video controller with the URL of our video.
For this example, we use Cloudinary, a cloud-based service that simplifies hosting and managing media assets. Our video, dance-2.mp4
, is stored on Cloudinary’s demo cloud, which allows us to easily serve videos via a secure and reliable URL.
Cloudinary is especially helpful for video management. It provides adaptive streaming, automatic optimizations, and different transformations to fit various devices and network conditions. Using it ensures that the video loads quickly and efficiently, even on slower networks.
To further streamline the process, we store the initialization process in a Future
object (_initializeVideoPlayerFuture
). This ensures that the video is fully loaded and ready to play before it is displayed, which is handled asynchronously:
@override void initState() { super.initState(); // Initialize the video controller with a network video _controller = VideoPlayerController.networkUrl( Uri.parse('https://res.cloudinary.com/demo/video/upload/samples/dance-2.mp4'), ); // Store the initialization Future _initializeVideoPlayerFuture = _controller.initialize().then((_) { setState(() {}); // Update UI once the video is initialized }); }
How to Display and Style Video Player in Flutter
Now that the video player is set up, we need a way to control its playback. Thankfully, the VideoPlayerController
provides methods to play, pause, and stop the video, so we will need to create a UI that includes play, pause, and stop functionality through buttons. The buttons should be placed inside a row and trigger actions on the video player controller.
To do this, we will begin by defining our Widget class inside the _VideoAppState
class:
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Video Player'), ), ...
Here, we are defining a simple Widget that displays the title of our app.
Next, we will define the body of our app, which will contain all of the elements of our app. We create a Column
widget containing a Center
widget to center our video player. We then use the FutureBuilder
widget to initialize our video player and add it to the app once the video is loaded:
body: Column( children: [ // Center the video player Center( child: FutureBuilder( future: _initializeVideoPlayerFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { return AspectRatio( aspectRatio: _controller.value.aspectRatio, // Set aspect ratio child: VideoPlayer(_controller), // Show the video player ); } else { // Show loading indicator while the video is initializing return const Center(child: CircularProgressIndicator()); } }, ), ),
Next, we will add two buttons to our app: play/pause and stop. The play/pause button, as the name implies, will be used to play/pause our video, while the stop button pauses the video and resets the time passed to 0:
... const SizedBox(height: 20), // Space between video and buttons Row( mainAxisAlignment: MainAxisAlignment.center, children: [ // Play/Pause button ElevatedButton( onPressed: () { setState(() { if (_controller.value.isPlaying) { _controller.pause(); // Pause the video if it's playing } else { _controller.play(); // Play the video if it's paused } }); }, // Update button text based on the playing state child: Text(_controller.value.isPlaying ? 'Pause' : 'Play'), ), const SizedBox(width: 10), ElevatedButton( onPressed: () { setState(() { _controller.seekTo(Duration.zero); // Stop the video (reset position) _controller.pause(); // Pause after resetting to the beginning }); }, child: const Text('Stop'), ), ], ), ...
Finally, we will define a dropdown that will contain multiple values and allow us to set the playback speed of our video:
const SizedBox(height: 20), // Space between buttons and dropdown // Dropdown to select the playback speed Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text('Playback Speed: '), DropdownButton<double>( value: _playbackSpeed, items: [ const DropdownMenuItem(value: 0.5, child: Text('0.5x')), const DropdownMenuItem(value: 1.0, child: Text('1x')), const DropdownMenuItem(value: 1.5, child: Text('1.5x')), const DropdownMenuItem(value: 2.0, child: Text('2x')), ], onChanged: (value) { setState(() { _playbackSpeed = value!; _controller.setPlaybackSpeed(_playbackSpeed); // Set the playback speed }); }, ), ], ),
With this, our app is now complete, and all we need to do is create a dispose()
function that frees up the resources once the app is closed:
@override void dispose() { _controller.dispose(); // Dispose of the controller to free resources super.dispose(); }
Here is what our main.dart
file looks like:
// Import necessary Flutter packages import 'package:flutter/material.dart'; import 'package:video_player/video_player.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); // Ensure the Flutter engine is initialized runApp(MyApp()); } // Main App widget class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: VideoApp(), ); } } // Stateful widget to manage video playback class VideoApp extends StatefulWidget { @override _VideoAppState createState() => _VideoAppState(); } class _VideoAppState extends State<VideoApp> { late VideoPlayerController _controller; // Late initialization of video controller late Future<void> _initializeVideoPlayerFuture; // Future for video initialization double _playbackSpeed = 1.0; // Variable to store the playback speed @override void initState() { super.initState(); // Initialize the video controller with a network video _controller = VideoPlayerController.networkUrl( Uri.parse('https://res.cloudinary.com/demo/video/upload/samples/dance-2.mp4'), ); // Store the initialization Future _initializeVideoPlayerFuture = _controller.initialize().then((_) { setState(() {}); // Update UI once the video is initialized }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Video Player'), ), body: Column( children: [ // Center the video player Center( child: FutureBuilder( future: _initializeVideoPlayerFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { return AspectRatio( aspectRatio: _controller.value.aspectRatio, // Set aspect ratio child: VideoPlayer(_controller), // Show the video player ); } else { // Show loading indicator while the video is initializing return const Center(child: CircularProgressIndicator()); } }, ), ), const SizedBox(height: 20), // Space between video and buttons Row( mainAxisAlignment: MainAxisAlignment.center, children: [ // Play/Pause button ElevatedButton( onPressed: () { setState(() { if (_controller.value.isPlaying) { _controller.pause(); // Pause the video if it's playing } else { _controller.play(); // Play the video if it's paused } }); }, // Update button text based on the playing state child: Text(_controller.value.isPlaying ? 'Pause' : 'Play'), ), const SizedBox(width: 10), ElevatedButton( onPressed: () { setState(() { _controller.seekTo(Duration.zero); // Stop the video (reset position) _controller.pause(); // Pause after resetting to the beginning }); }, child: const Text('Stop'), ), ], ), const SizedBox(height: 20), // Space between buttons and dropdown // Dropdown to select the playback speed Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text('Playback Speed: '), DropdownButton<double>( value: _playbackSpeed, items: [ const DropdownMenuItem(value: 0.5, child: Text('0.5x')), const DropdownMenuItem(value: 1.0, child: Text('1x')), const DropdownMenuItem(value: 1.5, child: Text('1.5x')), const DropdownMenuItem(value: 2.0, child: Text('2x')), ], onChanged: (value) { setState(() { _playbackSpeed = value!; _controller.setPlaybackSpeed(_playbackSpeed); // Set the playback speed }); }, ), ], ), ], ), ); } @override void dispose() { _controller.dispose(); // Dispose of the controller to free resources super.dispose(); } }
Running the Flutter Video Player App on an Android Emulator
Now, all we need to do is to test our app. To do this, open up the device manager by clicking on the icon in the image below. Next, run your Android emulator by clicking the Play button. Finally, in the Flutter Device Selection drop-down, select your Android device and launch your app by clicking the Play button in the top navigation bar.
This will launch your mobile emulator and run your Flutter video app:
Here is what our app looks like:
Now click on the Play button to start playing the video. As you can see, once the video starts running, the Play button will turn into the Pause button. You can even click on Stop to stop the video:
You can also select the playback speed by selecting an option from the drop-down menu:
Insights into Various Flutter Video Formats
Understanding the supported video formats is crucial when working with video content in Flutter, as it ensures compatibility and seamless playback within your applications.
Here’s an overview of the key video formats supported by Flutter:
- MP4: The most widely used video format, MP4 (MPEG-4 Part 14), is highly compatible with various devices and platforms. It provides good quality at relatively low file sizes, making it a popular choice for web and mobile video content. Flutter’s video_player package supports MP4 files, allowing for smooth playback and integration.
- WebM: This format, developed by Google, is designed for web use and offers efficient video compression. WebM provides high-quality video playback while maintaining smaller file sizes. Flutter supports WebM videos, though support may depend on the underlying platform and player implementation.
- AVI: Although less common in modern applications, AVI (Audio Video Interleave) is still supported by many video players. It is known for its flexibility and quality but can result in larger file sizes. Compatibility with AVI in Flutter may require additional handling or conversion to more optimized formats.
- MOV: The MOV format, developed by Apple, is often used for high-quality video recordings. While it is supported by many video players and platforms, MOV files can be larger and may require conversion to MP4 for better compatibility across all devices in Flutter apps.
- MKV: Matroska (MKV) is an open-source container format that supports a wide range of video and audio codecs. It is known for its high-quality video and flexibility. However, native support for MKV in Flutter might require additional configuration or third-party libraries.
Wrapping Up
From understanding how Flutter handles video playback to knowing the supported formats, you can use these capabilities to enhance user experiences with dynamic video content. Incorporating video features effectively can significantly boost user engagement and app functionality. Tools like Cloudinary play a crucial role in this by providing seamless video management, including easy integration, transformation, and optimization. Cloudinary’s powerful SDK simplifies video handling, ensuring smooth playback and efficient delivery across various platforms.
Ready to take your Flutter video features to the next level? Sign up to Cloudinary today to streamline your video content management for a more engaging and polished app experience.
More from Cloudinary:
Mobile Video Revolution: How-to Guide for Using Cloudinary’s iOS Native Video Player