HomeAbout

YouTube API - channel & playlists part 1 of 2

Published: Sun Feb 07 2021 17:00:00
Flutter 1.22.6

This article is divided into two separate articles. In the first are you able to read about how you get API key for YouTube requests and display playlists from a channel. In the second how to display playable YouTube videos from each playlist.

YouTube logo

Before we get started I want you to know that you can find the complete app on GitHub

Activate YouTube Data API v3

To make API requests via the YouTube API you must have a valid API key and it must be activated to use the YouTube Data API v3. We're going to start with getting this API key from Google Developer Console. If you don't have a project, you'll have to create a new (it's super easy).

API key first step

Activate the API key for use of YouTube API. You'll have to activate the API called "Data API v3". From the Dashboard of the project, click the button "ENABLE APIS AND SERVICES".

API key second step

Search for YouTube, select YouTube Data API v3 and enable API calls.

API key third step

Create a new flutter project

Alright, we got the API key, almost time to start building the actual app. Before we get into coding let's take a look at the project structure. It's important to get a feeling for the structure of the app. We're going to work with the following folders:

  • Models - to hold object data
  • Screens - screens to navigate from → to
  • Widgets - components used within the screens to i.e. display data, a YouTube video or playlist information.

Project structure

Go ahead and create the folders right away.

Create models

Within the models folder, we're creating Playlist model. If you didn't do it in the previous step, create a new folder models within lib. Add a new dart file playlist.dart.

We'll keep track of id, title, image (URL) and description from each of the playlist within the channel. There's many more fields you can make use of, but for this tutorial we're sticking with only these. The factory Playlist.fromJson is based upon the data we will receive from the GET response when requesting channel data. The PlaylistDetail model class:

1class Playlist {
2  final String id;
3  final String title;
4  final String image;
5  final String description;
6
7  Playlist({
8    this.id,
9    this.title,
10    this.image,
11    this.description,
12  });
13
14  factory Playlist.fromJson(Map<String, dynamic> json) => Playlist(
15    id: json['id'],
16    title: json['snippet']['title'],
17    image: json['snippet']['thumbnails']['standard']['url'],
18    description: json['snippet']['description'],
19  );
20}

Hide credentials with .env file

It's important not to commit any credentials or secret keys. We're going to use a .env file which we do not want to commit to any repository. Open .gitignore and add the following line somewhere in the file.

1.env*

Once you've updated the .gitignore file, create a new folder by either the file system or in terminal (cd to your project) and create a .env file in which we'll add the API key.

1mkdir assets
2touch assets/.env

Open the newly created .env and add the api key

1API_KEY=<YOUR KEY GOES HERE>

Now we've made sure the API key won't get added to a git repository as well as creating a .env file with our YouTube API key.

Install dependencies

There are three packages we'll use to complete the app. The youtube_player_flutter will be used in the second article, but for the simplicity, we install all dependencies at once. The packages we're installing is flutter_dotenv, http and youtube_player_flutter. Open pubspec.yaml and add the three packages as a dependency. Also further down under flutter add assets and the file .env,

1dependencies:
2  flutter_dotenv: ^3.1.0
3  http: ^0.12.2
4  youtube_player_flutter: ^7.0.0+7
5
6flutter:
7  assets:
8    - .env

Run flutter pub get. Now the packages are installed and the API key in .env file will be accessible within the app (and any other key value you add to the file).

Building our app

Time to create some code (finally!). Open lib/main.dart and in the top import:

  • dotenv - to hide our API key
  • playlists.dart - the first screen, to show a channel playlists
1import 'package:flutter/material.dart';
2import 'package:flutter_dotenv/flutter_dotenv.dart' as DotEnv;
3import 'screens/playlists.dart';

Change the main() method to be async and load the .env file from the assets folder. This will allow us to use the parameters within the .env file throughout the app.

1void main() async {
2  await DotEnv.load(fileName: "assets/.env");
3  runApp(MyApp());
4}

Below main() create MyApp as a StatelessWidget widget and set home: parameter to call Playlists sreen widget, which we'll create in the next step. The playlists screen widget will make a request to get and display playlists by a specified channel.

1class MyApp extends StatelessWidget {
2  @override
3  Widget build(BuildContext context) {
4    return MaterialApp(
5      title: 'Hands-on Flutter YouTube playlist',
6      theme: ThemeData(
7        primarySwatch: Colors.green,
8        visualDensity: VisualDensity.adaptivePlatformDensity,
9      ),
10      home: Playlists(),
11    );
12  }
13}

Playlists screen widget

It's time to make our first request by YouTube channel and display the playlists. We need a screen widget to display the playlists for this. Each playlist has a title, image and description which we'll use. Create a new file within lib/screens named playlists.dart

Import the following in the top of the file:

1import 'package:flutter/material.dart';
2import 'package:flutter_dotenv/flutter_dotenv.dart' as DotEnv;
3import 'package:http/http.dart' as http;
4import 'dart:convert' as convert;
5import '../models/playlist.dart';
6import '../widgets/playlist-row.dart';
  • http - to make http request and get a YouTube channel playlist and videos per playlist
  • convert - convert the http response from json to map

Create a Stateful widget, the data within this widget will change once we make API call to get a specific playlist (or multiple playlists). We will use the widgets Scaffold, SafeArea and ListView separated.

1class Playlists extends StatefulWidget {
2  @override
3  _PlaylistsState createState() => _PlaylistsState();
4}
5
6class _PlaylistsState extends State<Playlists> {
7  @override
8  Widget build(BuildContext context) {
9    return Scaffold(
10      body: SafeArea(
11          child: ListView.separated(
12            separatorBuilder: null,
13            itemCount: null,
14            itemBuilder: null,
15          )
16      ),
17    );
18  }
19}

Next up is to

  • Add playlist variable of type List to hold the playlist details we fetch from a specific channel.
  • Add a String url where channel id is UCwXdFgeE9KYzlDdR7TG9cMw (the Flutter channel) and DotEnv to get our API_KEY.
1class _PlaylistsState extends State<Playlists> {
2  List<Playlist> playlists = [];
3  final String url = 'https://www.googleapis.com/youtube/v3/playlists?part=snippet&channelId=UCwXdFgeE9KYzlDdR7TG9cMw&key=${DotEnv.env['API_KEY']}';
4  ...

Override the initState() and call getPlaylists method within.

The actual getPlayLists method will contain our first http request. We store the jsonResponse['items'] which contains a list of playlist details.

1@override
2  void initState() {
3    super.initState();
4    getPlaylists();
5  }
6
7  Future<void> getPlaylists() async {
8    var response = await http.get(url);
9    if (response.statusCode == 200) {
10      var jsonResponse = convert.jsonDecode(response.body);
11      setState(() {
12        playlists = jsonResponse['items'].map<Playlist>((item) {
13          return Playlist.fromJson(item);
14        }).toList();
15      });
16    } else {
17      print('I should handle this error better: ${response.statusCode}.');
18    }
19  }

We're going to update the code within SafeArea in the build method of class _PlaylistsState.

separatorBuilder is a flexible way to add custom separator between each items. We're using the Divider in this example with a thickness of 2.0.

itemCount to determine number of items to be displayed in the list. We count items in our List of playlists since we do not wish to render any more or less rows than this.

itemBuilder a custom builder to generate each of the row from itemCount.

1separatorBuilder: (context, index) => Divider(
2  thickness: 2.0,
3),
4itemCount: playlists.length,
5itemBuilder: (context, index) {
6  return PlaylistRow(
7    playlist: playlists[index],
8  );
9},

We're going to create the widget PlaylistRow in order to finalise the display of the channels playlists. Create a new file within lib/widgets called playlist-row.dart. Create a StatelessWidget with name PlaylistRow

1class PlaylistRow extends StatelessWidget {
2  @override
3  Widget build(BuildContext context) {
4    return Container();
5  }
6}

This widget will be the presentation of a playlist. Add a final variable of type Playlist and add it in the constructor of the class.

1class PlaylistRow extends StatelessWidget {
2  final Playlist playlist;
3
4  const PlaylistRow({
5    Key key,
6    this.playlist
7  }):super(key: key);

I'll separate the build method of the widget in minor steps. Add InkWell and we're setting null as onTap parameter, in the second post we're going to navigate to a Playlist screen, displaying the videos within a specific playlist. We're using the Column widget as child parameter to hold the playlist title, image and description.

1@override
2Widget build(BuildContext context) {
3  return InkWell(
4    onTap: null,
5    child: Column(
6      crossAxisAlignment: CrossAxisAlignment.center,
7      children: [],
8    ),
9  );
10}

The first child in the Column will be Padding with the title. Padding makes it easy to add space to the title in the Text widget. Give the title a bigger font (20) by using TextStyle widget. For the image use Image.network to playlist image by URL. For the description text we make use of Padding, giving it some nice space. Notice EdgeInsets as padding parameter, both specific sides with the .only .only(bottom: ..., top: ...) and for all sides with .all .all(...).

1children: [
2  Padding(
3    padding: EdgeInsets.only(bottom: 15.0, top: 10.0),
4    child: Text(
5      playlist.title,
6      style: TextStyle(fontSize: 20.0),
7    ),
8  ),
9  Image.network(playlist.image),
10  Padding(
11    padding: EdgeInsets.all(10.0),
12    child: Text(playlist.description),
13  ),
14],

Start the app and enjoy! The app should look something like the image below. We'll dive into displaying the videos of a playlist in the next article!

You can find the complete app on GitHub

Playlists

Tomas Sjösten
Project manager with a passion for coding (especially Flutter)! I set aside several hours a week for various coding projects or writing articles about coding.