Implementing Video Playback with video_player in Flutter

Flutter has revolutionized cross-platform app development, and its rich ecosystem of packages makes it easier than ever to add complex functionalities. Video playback is a common requirement for many apps, and the video_player package is a popular choice for implementing this in Flutter. In this comprehensive guide, we’ll explore how to effectively use the video_player package to add video playback capabilities to your Flutter applications.

Introduction to video_player Package

The video_player package is a Flutter plugin that provides access to native video players on Android and iOS. It supports playing videos from various sources, including local files and network URLs, and offers extensive customization options. Whether you’re building a social media app, an educational platform, or any app requiring video playback, the video_player package offers the necessary tools.

Why Use video_player in Flutter?

  • Cross-Platform Compatibility: Works seamlessly on both Android and iOS.
  • Various Video Sources: Supports videos from local files, assets, and network URLs.
  • Customizable: Provides extensive customization options, including controls, looping, and more.
  • Easy Integration: Simple to integrate and use with minimal setup.
  • Rich Features: Offers features like full-screen playback, adjustable playback speed, and subtitles.

How to Implement Video Playback with video_player

Follow these steps to integrate and use the video_player package in your Flutter application:

Step 1: Add the video_player Dependency

First, add the video_player package to your pubspec.yaml file:

dependencies:
  flutter:
    sdk: flutter
  video_player: ^2.8.2

After adding the dependency, run flutter pub get in your terminal to install the package.

Step 2: Import the Package

In your Dart file, import the video_player package:

import 'package:video_player/video_player.dart';

Step 3: Create a VideoPlayerController

The VideoPlayerController is the core class that manages the video playback. You need to create an instance of this controller, providing the video source.

late VideoPlayerController _controller;

@override
void initState() {
  super.initState();
  _controller = VideoPlayerController.networkUrl(
    Uri.parse('https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4'),
  )..initialize().then((_) {
    // Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
    setState(() {});
  });
}

@override
void dispose() {
  super.dispose();
  _controller.dispose();
}

In this example:

  • VideoPlayerController.networkUrl creates a controller that loads a video from a network URL.
  • initialize() initializes the video player, and then is used to rebuild the UI after initialization.
  • dispose() releases the resources when the widget is removed.

Step 4: Use the VideoPlayer Widget

Use the VideoPlayer widget to display the video. This widget takes the VideoPlayerController as a parameter.

import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';

class VideoApp extends StatefulWidget {
  const VideoApp({super.key});

  @override
  State createState() => _VideoAppState();
}

class _VideoAppState extends State {
  late VideoPlayerController _controller;

  @override
  void initState() {
    super.initState();
    _controller = VideoPlayerController.networkUrl(
      Uri.parse(
          'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4'),
    )..initialize().then((_) {
        // Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
        setState(() {});
      });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Video Demo',
      home: Scaffold(
        body: Center(
          child: _controller.value.isInitialized
              ? AspectRatio(
                  aspectRatio: _controller.value.aspectRatio,
                  child: VideoPlayer(_controller),
                )
              : Container(),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            setState(() {
              _controller.value.isPlaying
                  ? _controller.pause()
                  : _controller.play();
            });
          },
          child: Icon(
            _controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
          ),
        ),
      ),
    );
  }

  @override
  void dispose() {
    super.dispose();
    _controller.dispose();
  }
}

In this example:

  • AspectRatio ensures the video maintains its aspect ratio.
  • A FloatingActionButton toggles between play and pause.

Loading Video from Different Sources

The video_player package supports various video sources. Here’s how to load videos from different sources:

Loading from Assets

To load a video from your assets folder, ensure that the asset is declared in the pubspec.yaml file:

flutter:
  assets:
    - assets/videos/my_video.mp4

Then, use VideoPlayerController.asset:

_controller = VideoPlayerController.asset('assets/videos/my_video.mp4')
  ..initialize().then((_) {
    setState(() {});
  });

Loading from Local Files

To load a video from a local file, use VideoPlayerController.file:

import 'dart:io';

_controller = VideoPlayerController.file(File('/path/to/your/video.mp4'))
  ..initialize().then((_) {
    setState(() {});
  });

Customizing the Video Player

The video_player package provides various options for customizing the video player.

Looping

To enable looping, set the looping property to true:

@override
void initState() {
  super.initState();
  _controller = VideoPlayerController.networkUrl(
    Uri.parse('https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4'),
  )..initialize().then((_) {
      _controller.setLooping(true);
      setState(() {});
    });
}

Volume Control

To control the volume, use the setVolume method:

_controller.setVolume(1.0); // Sets the volume to maximum

Playback Speed

To adjust the playback speed, use the setPlaybackSpeed method:

_controller.setPlaybackSpeed(1.5); // Sets the playback speed to 1.5x

Advanced Customization with VideoProgressIndicator

The video_player package provides VideoProgressIndicator which helps to indicate the progress of the video along with providing controls like seek bar, play/pause etc. Create custom controls using the `VideoProgressIndicator` along with VideoPlayerController instance and listeners can be implemented:

import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';

class CustomVideoPlayer extends StatefulWidget {
  final VideoPlayerController videoPlayerController;

  const CustomVideoPlayer({Key? key, required this.videoPlayerController})
      : super(key: key);

  @override
  _CustomVideoPlayerState createState() => _CustomVideoPlayerState();
}

class _CustomVideoPlayerState extends State {
  bool _isShowingControls = true;
  late Future _initializeVideoPlayerFuture;

  @override
  void initState() {
    super.initState();
    _initializeVideoPlayerFuture = widget.videoPlayerController.initialize();

    // Show controls for 3 seconds initially, then hide them
    _startControlVisibilityTimer();
  }

  // Delayed control visibility update
  void _startControlVisibilityTimer() {
    Future.delayed(const Duration(seconds: 3), () {
      if (mounted) {
        setState(() {
          _isShowingControls = false;
        });
      }
    });
  }

  // Method to toggle the visibility of controls
  void _toggleControlsVisibility() {
    setState(() {
      _isShowingControls = !_isShowingControls;
    });
    if (_isShowingControls) {
      _startControlVisibilityTimer(); // Restart timer only when showing controls
    }
  }

  // Builds custom video controls
  Widget _buildControls() {
    return AnimatedOpacity(
      opacity: _isShowingControls ? 1.0 : 0.0,
      duration: const Duration(milliseconds: 300),
      child: Container(
        color: Colors.black54,
        padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            // Play/Pause button
            IconButton(
              icon: Icon(widget.videoPlayerController.value.isPlaying
                  ? Icons.pause
                  : Icons.play_arrow, color: Colors.white),
              onPressed: () {
                setState(() {
                  if (widget.videoPlayerController.value.isPlaying) {
                    widget.videoPlayerController.pause();
                  } else {
                    widget.videoPlayerController.play();
                  }
                });
                _startControlVisibilityTimer();
              },
            ),
            // Seek bar
            Expanded(
              child: VideoProgressIndicator(
                widget.videoPlayerController,
                allowScrubbing: true,
                colors: const VideoProgressColors(playedColor: Colors.amber),
              ),
            ),
            // Fullscreen button (Placeholder)
            IconButton(
              icon: const Icon(Icons.fullscreen, color: Colors.white),
              onPressed: () {
                // Handle fullscreen toggle here
              },
            ),
          ],
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _toggleControlsVisibility, // Toggle controls on tap
      child: FutureBuilder(
        future: _initializeVideoPlayerFuture,
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.done) {
            return AspectRatio(
              aspectRatio: widget.videoPlayerController.value.aspectRatio,
              child: Stack(
                alignment: Alignment.bottomCenter,
                children: [
                  VideoPlayer(widget.videoPlayerController),
                  _buildControls(), // Show controls conditionally
                ],
              ),
            );
          } else {
            return const Center(
              child: CircularProgressIndicator(),
            );
          }
        },
      ),
    );
  }

  @override
  void dispose() {
    // Ensure disposing of the VideoPlayerController to free up resources.
    super.dispose();
  }
}

Error Handling

When working with videos, it’s essential to handle potential errors gracefully.

@override
void initState() {
  super.initState();
  _controller = VideoPlayerController.networkUrl(
    Uri.parse('https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4'),
  )
    ..initialize().then((_) {
      setState(() {});
    }).catchError((error) {
      print('Error initializing video: $error');
    });
}

Conclusion

The video_player package is a powerful and flexible tool for implementing video playback in Flutter. Whether you need to load videos from network URLs, local files, or assets, the video_player package provides the necessary features and customization options. By following the steps outlined in this guide, you can seamlessly integrate video playback into your Flutter applications and provide an engaging user experience.