How to add a YouTube video to your Flutter app?

Walnut Software
7 min readSep 12, 2023

--

In this article, I will explain how to display YouTube videos in Flutter using the youtube_player_flutter package. This article will consist of two parts. The first part allows you to display a YouTube video on the screen simply. In contrast, the second part enables you to add multiple videos, switch between them, view the videos in a list format, and open the YouTube videos using their links.

Setting Up the Project

Open a new terminal. Navigate to the directory where you want to create a new Flutter project using terminal commands. Then, use the following command to create a new Flutter project:

flutter create example_project

You can replace “example_project” with the name of the project you want to create. In my case project name is youtube_player.

Add necessary dependencies

To define dependencies, first open the pubspec.yaml file, which is located at the bottom of your project. Afterward, add the youtube_player_flutter package under the "dependencies" section. You can find the latest youtube_player_flutter package at this link.

https://pub.dev/packages/youtube_player_flutter

The “dependencies” section should look like the following

dependencies:
flutter:
sdk: flutter
youtube_player_flutter: ^latest_version

Please replace “^latest_version” with the actual latest version of the http package that you intend to use. In my case it’s ^8.1.2. Don’t forget to save the package using the Ctrl+S keys.

PART - 1

Using this part, you can add a YouTube video to your application. You can also incorporate features such as fast-forwarding, adjusting the playback speed, and making the video fullscreen for the added YouTube video. The visual representation will look like the following:

● Add the code below to the main.dart file (located in the lib folder).

import 'package:flutter/material.dart';
void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: App(),
);
}
}

● Create a dashboard.dart file inside the lib folder and add the following code to it.

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:youtube_player_flutter/youtube_player_flutter.dart';

class App extends StatefulWidget {
static String videoID = 'egMWlD3fLJ8';
// YouTube Video Full URL : https://www.youtube.com/watch?v=egMWlD3fLJ8
@override
_AppState createState() => _AppState();
}

class _AppState extends State<App> {
late YoutubePlayerController _controller;
bool isFullScreen = false;

@override
void initState() {
super.initState();
_controller = YoutubePlayerController(
initialVideoId: App.videoID,
flags: YoutubePlayerFlags(
autoPlay: false,
mute: false,
),
);
_controller.addListener(() {
if (_controller.value.isFullScreen != isFullScreen) {
setState(() {
isFullScreen = _controller.value.isFullScreen;
});
}
});
}

@override
Widget build(BuildContext context) {
return YoutubePlayerBuilder(
onExitFullScreen: () {
SystemChrome.setPreferredOrientations(DeviceOrientation.values);
setState(() {
isFullScreen = false;
});
},
player: YoutubePlayer(
controller: _controller,
liveUIColor: Colors.amber,
showVideoProgressIndicator: true,

),
builder: (context, player) => Scaffold(
body: ListView(
children: [
player,
],
),
)
);
}
}

⚠️ Warning:Do not forget to import this dashboard.dart file you have created into main.dart as shown below.

import 'package:youtube_player/main copy.dart';

You should replace “youtube_player” with the name of your project. I imported it as shown above because the name of the project I created is “youtube_player.”

PART - 2

Using this part, you can add a YouTube video to your application. You can also incorporate features such as fast-forwarding, adjusting the playback speed, making the video fullscreen, and controlling the volume. After adding the IDs of multiple videos to the _ids list in the code, you can switch between videos by clicking the relevant button. In addition, with the video_list.dart we will create, you can display the videos in a card view, one below the other, and open the desired video. You can also open a YouTube video by adding its link or ID. The visual representation will look like the following:

● Add the code below to the main.dart file (located in the lib folder).

⚠️ Warning: Do not debug after adding this code; you will encounter errors. As I mentioned above, we need the video_list.dart file to display the videos we added IDs for in a card view one below the other. You can find that code below.

import 'dart:developer';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:youtube_player_flutter/youtube_player_flutter.dart';

import 'package:youtube_player/video_list.dart';

void main() {
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(
statusBarColor: Color.fromARGB(255, 46, 45, 45),
),
);
runApp(YoutubePlayerDemoApp());
}

/// Creates [YoutubePlayerDemoApp] widget.
class YoutubePlayerDemoApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Youtube Player Flutter',
theme: ThemeData(
scaffoldBackgroundColor: Colors.black,
primarySwatch: Colors.red,
appBarTheme: const AppBarTheme(
color: Colors.black12,
titleTextStyle: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w300,
fontSize: 20,
),
),
iconTheme: const IconThemeData(
color: Colors.white,
),
),
home: MyHomePage(),
);
}
}

/// Homepage
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
late YoutubePlayerController _controller;
late TextEditingController _idController;
late TextEditingController _seekToController;

late PlayerState _playerState;
late YoutubeMetaData _videoMetaData;
double _volume = 100;
bool _muted = false;
bool _isPlayerReady = false;

final List<String> _ids = [
'rMbATaj7Il8',
'DPL_SV3n7IU',
'jhdFe3evXpk',
'tH2w6Oxx0kQ',
'Bl4dEAtxo0M',
'OMOGaugKpzs',
];

@override
void initState() {
super.initState();
_controller = YoutubePlayerController(
initialVideoId: _ids.first,
flags: const YoutubePlayerFlags(
mute: false,
autoPlay: true,
disableDragSeek: false,
loop: false,
isLive: false,
forceHD: false,
enableCaption: true,
),
)..addListener(listener);
_idController = TextEditingController();
_seekToController = TextEditingController();
_videoMetaData = const YoutubeMetaData();
_playerState = PlayerState.unknown;
}

void listener() {
if (_isPlayerReady && mounted && !_controller.value.isFullScreen) {
setState(() {
_playerState = _controller.value.playerState;
_videoMetaData = _controller.metadata;
});
}
}

@override
void deactivate() {
// Pauses video while navigating to next page.
_controller.pause();
super.deactivate();
}

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

@override
Widget build(BuildContext context) {
return YoutubePlayerBuilder(
onExitFullScreen: () {
// The player forces portraitUp after exiting fullscreen. This overrides the behaviour.
SystemChrome.setPreferredOrientations(DeviceOrientation.values);
},
player: YoutubePlayer(
controller: _controller,
showVideoProgressIndicator: true,
progressIndicatorColor: Colors.blueAccent,
topActions: <Widget>[
const SizedBox(width: 8.0),
Expanded(
child: Text(
_controller.metadata.title,
style: const TextStyle(
color: Colors.white,
fontSize: 18.0,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
IconButton(
icon: const Icon(
Icons.settings,
color: Colors.white,
size: 25.0,
),
onPressed: () {
log('Settings Tapped!');
},
),
],
onReady: () {
_isPlayerReady = true;
},
onEnded: (data) {
_controller
.load(_ids[(_ids.indexOf(data.videoId) + 1) % _ids.length]);
_showSnackBar('Next Video Started!');
},
),
builder: (context, player) => Scaffold(
appBar: AppBar(
leading: Padding(
padding: const EdgeInsets.only(left: 30.0),

),
title: const Text(
'Youtube Player Flutter',
style: TextStyle(color: Colors.white),
),
actions: [
IconButton(
icon: const Icon(Icons.video_library),
onPressed: () => Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => VideoList(),
),
),
),
],
),
body: ListView(
children: [
player,
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_space,
_text('Title', _videoMetaData.title),
_space,
_text('Channel', _videoMetaData.author),
_space,
_text('Video Id', _videoMetaData.videoId),
_space,
Row(
children: [
_text(
'Playback Quality',
_controller.value.playbackQuality ?? '',
),
const Spacer(),
_text(
'Playback Rate',
'${_controller.value.playbackRate}x ',
),
],
),
_space,
TextField(
style: TextStyle(
color: Colors.white,
),
enabled: _isPlayerReady,
controller: _idController,
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Enter youtube \<video id\> or \<link\>',
fillColor: Color.fromARGB(31, 250, 248, 248).withAlpha(20),
filled: true,
hintStyle: const TextStyle(
fontWeight: FontWeight.w300,
color: Colors.white60,
),
suffixIcon: IconButton(
icon: const Icon(Icons.clear),
onPressed: () => _idController.clear(),
),
),
),
_space,
Row(
children: [
_loadCueButton('LOAD'),
const SizedBox(width: 10.0),
_loadCueButton('CUE'),
],
),
_space,
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(
icon: const Icon(Icons.skip_previous),
onPressed: _isPlayerReady
? () => _controller.load(_ids[
(_ids.indexOf(_controller.metadata.videoId) -
1) %
_ids.length])
: null,
),
IconButton(
icon: Icon(
_controller.value.isPlaying
? Icons.pause
: Icons.play_arrow,
),
onPressed: _isPlayerReady
? () {
_controller.value.isPlaying
? _controller.pause()
: _controller.play();
setState(() {});
}
: null,
),
IconButton(
icon: Icon(_muted ? Icons.volume_off : Icons.volume_up),
onPressed: _isPlayerReady
? () {
_muted
? _controller.unMute()
: _controller.mute();
setState(() {
_muted = !_muted;
});
}
: null,
),
FullScreenButton(
controller: _controller,
color: Colors.white,
),
IconButton(
icon: const Icon(Icons.skip_next),
onPressed: _isPlayerReady
? () => _controller.load(_ids[
(_ids.indexOf(_controller.metadata.videoId) +
1) %
_ids.length])
: null,
),
],
),
_space,
Row(
children: <Widget>[
const Text(
"Volume",
style: TextStyle(
fontWeight: FontWeight.w300,
color: Colors.white,
),
),
Expanded(
child: Slider(
inactiveColor: Colors.transparent,
value: _volume,
min: 0.0,
max: 100.0,
divisions: 10,
label: '${(_volume).round()}',
onChanged: _isPlayerReady
? (value) {
setState(() {
_volume = value;
});
_controller.setVolume(_volume.round());
}
: null,
),
),
],
),
_space,
AnimatedContainer(
duration: const Duration(milliseconds: 800),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.0),
color: _getStateColor(_playerState),
),
padding: const EdgeInsets.all(8.0),
child: Text(
_playerState.name,
style: const TextStyle(
fontWeight: FontWeight.w300,
color: Colors.white,
),
textAlign: TextAlign.center,
),
),
],
),
),
],
),
),
);
}

Widget _text(String title, String value) {
return RichText(
text: TextSpan(
text: '$title : ',
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
children: [
TextSpan(
text: value,
style: const TextStyle(
color: Colors.white70,
fontWeight: FontWeight.w300,
),
),
],
),
);
}

Color _getStateColor(PlayerState state) {
switch (state) {
case PlayerState.unknown:
return Colors.grey[700]!;
case PlayerState.unStarted:
return Colors.pink;
case PlayerState.ended:
return Colors.red;
case PlayerState.playing:
return Colors.white38;
case PlayerState.paused:
return Colors.blue;
case PlayerState.buffering:
return Colors.yellow;
case PlayerState.cued:
return Colors.blue[900]!;
default:
return Colors.blue;
}
}

Widget get _space => const SizedBox(height: 10);

Widget _loadCueButton(String action) {
return Expanded(
child: MaterialButton(
color: Colors.white38,
onPressed: _isPlayerReady
? () {
if (_idController.text.isNotEmpty) {
var id = YoutubePlayer.convertUrlToId(
_idController.text,
) ??
'';
if (action == 'LOAD') _controller.load(id);
if (action == 'CUE') _controller.cue(id);
FocusScope.of(context).requestFocus(FocusNode());
} else {
_showSnackBar('Source can\'t be empty!');
}
}
: null,
disabledColor: Colors.white,
disabledTextColor: Colors.white,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 14.0),
child: Text(
action,
style: const TextStyle(
fontSize: 18.0,
color: Colors.white,
fontWeight: FontWeight.w300,
),
textAlign: TextAlign.center,
),
),
),
);
}

void _showSnackBar(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
message,
textAlign: TextAlign.center,
style: const TextStyle(
fontWeight: FontWeight.w300,
fontSize: 16.0,
),
),
backgroundColor: Colors.blueAccent,
behavior: SnackBarBehavior.floating,
elevation: 1.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50.0),
),
),
);
}
}

Since you have added this code to main.dart, you can now create the video_list.dart file. Right-click on the lib folder, write video_list.dart, and save it.

After adding the following code to this file, you can run the application.

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

/// Creates list of video players
class VideoList extends StatefulWidget {
@override
_VideoListState createState() => _VideoListState();
}

class _VideoListState extends State<VideoList> {
final List<YoutubePlayerController> _controllers = [
'rMbATaj7Il8',
'DPL_SV3n7IU',
'jhdFe3evXpk',
'tH2w6Oxx0kQ',
'Bl4dEAtxo0M',
'OMOGaugKpzs',
]
.map<YoutubePlayerController>(
(videoId) => YoutubePlayerController(
initialVideoId: videoId,
flags: const YoutubePlayerFlags(
autoPlay: false,
),
),
)
.toList();

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Video List Demo'),
),
body: ListView.separated(
itemBuilder: (context, index) {
return YoutubePlayer(
key: ObjectKey(_controllers[index]),
controller: _controllers[index],
actionsPadding: const EdgeInsets.only(left: 16.0),
bottomActions: [
CurrentPosition(),
const SizedBox(width: 10.0),
ProgressBar(isExpanded: true),
const SizedBox(width: 10.0),
RemainingDuration(),
FullScreenButton(),
],
);
},
itemCount: _controllers.length,
separatorBuilder: (context, _) => const SizedBox(height: 10.0),
),
);
}
}

What’s Next

In the future, it can be ensured that the videos opened after entering a link are automatically added to the video_list.

Happy coding!

--

--