I needed a way to get the latest video from a particular YouTube channel for an app which I am working on in FlutterFlow. I thought it would be pretty trivial. I had no idea how far down the YouTube, XML and Dart custom action rabbit hole I would have to go. But I persevered and cracked it and thought it might be worth writing up in case anyone else is trying to do something similar.
This detail page in my app is, on the face of it, very simple. Part of it comprises a container, which is populated with data from Google Firebase, in order to make the elements of the container engaging and useful. You can tap the YouTube Video to play it, tap the buttons to invoke the relevant engagement methods with the entity for which the information has been populated from Firebase. The YouTube Player component from FlutterFlow is very nice, and all it needs is the URL to the video to work with.

Container for YouTube Player and engagement method buttons
A FlutterFlow container based widget comprising a YouTube player, and button based links in a row to the engagement methods of Twitter, YouTube, Facebook and phone call.
Screenshot from FlutterFlow designer

FlutterFlow Widget tree for YouTube Player and engagement method buttons
The widget comprises a ListView which gets a document from a collection in Firebase, and a container with a column and text widgets to display it. The buttons have actions which are populated by data from Firebase
Screenshot from FlutterFlow designer
In the end this became a bit of a journey into FlutterFlow custom actions, pubspec dependencies, dart programming and XML. It took me far too long to do because I needed to learn each thing sequentially and then go back and iterate again adjusting my bad understanding of things until finally I cracked it and it works reliably and automatically.
Contents
- Container for YouTube Player and engagement method buttons
- FlutterFlow Widget tree for YouTube Player and engagement method buttons
- YouTube RSS feeds
- YouTube Channel ID
- YouTube RSS
- The YouTube video player in FlutterFlow
- FlutterFlow Custom Action to populate YouTube URL
- The secret to having the results of a custom action available in a details page in FlutterFlow
- On Tap Action
- Page parameters
- Why build the custom Action code, in Dart first?
- Dart ytxml
- pubspec.yaml
- ytxml.dart
- FlutterFlow Custom action
- Debugging with FlutterFlow Flutter based apps with VSCodium
- See also
YouTube RSS feeds
Did you know YouTube make an RSS feed available? Neither did I. That is because I suppose they want you to use their player and their website to play videos. The RSS feed has been part of YouTube since they began operations and though I expect the powers that be would like to deprecate RSS, it must be far do difficult to disentangle it from the site now. So lets look at it. But how do you get to it?
YouTube Channel ID
Turns out you need to know the YouTube Channel ID in order to get the feed link. This can be buried deep in the YouTube site. An example is this one https://www.youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw
the channel ID is the last part of the link. In this case UCuAXFkgsw1L7xaCfnd5JJOw
.
Vanity YouTube channel links don't contain the channel ID, and neither do specific TouTube video links but theres a script (the link is at the bottom of this page) which can determine the channel from any given YouTube video link. Simply go to the video, click share, and paste the link as a parameter for the script. You'll get back the full RSS feed URL which can now be used for our purposes.
Here's an example, you just need to run the script with the link to a YouTube Video and it will tell you the correct feed link which contains the channel ID.
% ./youtube-rss.sh https://youtu.be/dQw4w9WgXcQ
https://www.youtube.com/feeds/videos.xml?channel_id=UCuAXFkgsw1L7xaCfnd5JJOw
YouTube RSS
The RSS feed link is in a standard format with the channel_id
as the parameter. This link contains the information needed for my FlutterFlow YouTube player. It returns structured data from which it is possible to determine the unique video Identifier for all the YouTube videos in the feed.
Inside the <entry>
tags you can see a video identifier inside the <yt:videoId>
tags. For example <yt:videoId>LLFhKaqnWwk</yt:videoId>
. This means that we can find video identifiers programatically, and if we can find them, we can find the one we want, in this case the latest one. Remember, the reason for doing this work at all is to place it in the right place as a variable so that the FlutterFlow player can play it!
Here an excerpt of the entry section of a YouTube RSS (XML) feed.
<entry>
<id>yt:video:LLFhKaqnWwk</id>
<yt:videoId>LLFhKaqnWwk</yt:videoId>
<yt:channelId>UCuAXFkgsw1L7xaCfnd5JJOw</yt:channelId>
<title>Rick Astley - Never Gonna Give You Up (Official Animated Video)</title>
<link rel="alternate" href="https://www.youtube.com/watch?v=LLFhKaqnWwk"/>
<author>
<name>Rick Astley</name>
<uri>https://www.youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw</uri>
</entry>
The YouTube video player in FlutterFlow
The FlutterFlow YoutubePlayer widget is a 'base element' in FlutterFlow indicting that it is an imporant key feature for FlutterFlow and is maintained as part of the FlutterFlow project. You can find it in the Base Elements section of the App Designer. (in the Widget Panel) or add it directly from the widget tree.

In addition to the usual FlutterFlow app designer controls, te YouTube Player widget allows you to set the URL, whether to loop, Mute, allow the YouTube controls, and allow full screen. You can see how I have implemented it in FlutterFlow, to use a 'Text Combination' variable as the input. This is beause the URL from my custom action (more abut that later) is not quite in the right format for the control, and adding this wrapper to it fixed the issue.

You can see the Combine Texts has just one value, from the variable ytVid
and this is the variable that is created by my custom action.
FlutterFlow Custom Action to populate YouTube URL
The whole point of this exercise is to get the latest YouTube video each time you visit this page. It becomes a far more engaging page if it is right up to date. Even with a small number of these detail pages managing the latest Video would quickly become impossible. I thought initially about writing a server side action in Firebase but it seemed to me to be far easier to do it on demand on the device when the page is loaded. This works well, is reliable and quite fast enough, and will not consume any cost in Firebase. It does however require the device to be online. I used to worry about this a lot for mobile apps but cellular or wifi data on phones is ubiquitous now and I try to govern it with an overall internet connection checker for each page rather than and inside each individual widget.
The secret to having the results of a custom action available in a details page in FlutterFlow
It may seem like the opposite approach to you, but the FlutterFlow action that creates the latest video is not on this page at all. It is initated by the calling page, as an On Tap action, just before the Navigate To action that opens this details page. Heres the custom action in the Actions Editor.
On Tap Action

The action has an single argument idChannel
which is a variable from our firebase document containing the Youtube Channel iD for the entity to which the detail page is about and a single Action Output variable name ytVid
which will contain the latest video URL for the YouTube Player on the next screen.
Page parameters
The second action, which navigates to our destination details page, contains the page parameters including the ytVid
for the YouTube player to use.

Why build the custom Action code, in Dart first?
The FlutterFlow app development and design environment is not a no-code tool. No code tools are almost always too inflexible to be useful apart from in very limited enterprise scenarios and should generally be avoided.
This means that you can, if you wish, harness the power of any published component or write any functionality you care to provide. I'm not afraid to admit I am a novice with Dart, and FlutterFlow allows me to limit my use of custom code to very specific areas of my app, so that I dont spend hours debugging missing semi-colons far away from the task I am attempting to accomplish and wondering about the placement and wrapping in my app structure to best implement my code. I find that to be a tremendous freedom. It hugely accelerates my productivity but it is not without its own particular issues.
I think it is worth approaching building custom actions for your FluuterFlow apps in the same way as you would go about building a Flutter or Dart based app. I found it helpful to test out my app as a plain old Dart app from the command line so as to make sure of my logic. This perhaps isn't completely necessary, but for me it removed all the moving parts of Flutter while I got to grips with learning enough of Dart to make my YouTube video link ID extraction work. YMMV.
Dart ytxml
ytxml is a simple command-line application which gets the RSS feed from a given YouTube Channel ID and finds the latest video!
pubspec.yaml
name: ytxml
description: A simple command-line application.
version: 1.0.0
# homepage: https://www.ezone.co.uk/flutter/youtube-xml-flutter-latest.html
dependencies:
xml: ^5.3.0
ytxml.dart
//
// Copyright 2022 Multizone Ltd. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Multizone Ltd. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import 'dart:convert';
import 'dart:io';
import 'package:xml/xml.dart';
// To run in Dart from the command line an app needs a 'main' so this Future is uncommented.
Future<String> main(String idChannel) async {
if (idChannel.isEmpty) {
print('Was expecting a YouTube Channel ID as a parameter');
return ("No YouTube Channel ID");
}
// A FlutterFlow Custom Action cannot have a 'main' as one already exists
// comment out the dart Future and if sttement and uncomment this to use in FlutterFlow
// Future<String> latestVideo(String idChannel) async {
// Get latest YouTube Video Id from Youtube RSS api
var idString = idChannel.toString();
print (idString); //Check we have the parameter correctly set. Remove for production.
final HttpClient httpClient = HttpClient();
final url = 'https://www.youtube.com/feeds/videos.xml?channel_id=$idString';
final request = await httpClient.getUrl(Uri.parse(url));
final response = await request.close();
final stream = response.transform(utf8.decoder);
final contents = await stream.join();
final document = XmlDocument.parse(contents);
var vids =
document.findAllElements('yt:videoId').map((node) => node.text).toList();
var firstVideo = 'https://youtu.be/' + (vids.first);
print (firstVideo); //Check we have got the video linkr correctly. Remove for production.
return (firstVideo);
}
This little app is asynchrous using a Dart Future. You can find the codelab on Asynchronous programming: futures in the link at the bottom of this article. It allows the XML dart package to parse the feed for the provided YouTube channel ID, extracts the YouTube video identifiers and then it uses the dart .first
element property to get the first video ID (which I think is always the latest). It becomes the firstVideo
variable which is printed to the console (or handed back to FlutterFlow as the return string from the custom action).
Dart and FlutterFlow can use the print();
function for logging, but this is discouraged in production apps and although you could call the debugPrint();
function to display things in the console in FlutterFlow this relies on flutter/foundation.dart import 'package:flutter/foundation.dart';
to function and this is not available for a pure dart program.
Our Dart app requires a command line parameter - the YouTube Channel Identifier. Flutter has this parameter passed automatically to the FlutterFlow custom action.
FlutterFlow Custom action
Paste the working Dart code into your custom action. Comment out the Dart main Future, and uncomment the Flutter Future. Set the FlutterFlow Action name to latestVideo
and set it to have a return value, a String. Define an argument idChannel
and set your depencencies. we need convert: ^3.0.2
and xml: ^5.3.0
due to other dependencies in FlutterFlow apps.

You'll need to compile your code inside FlutterFlow. Ignore the dependencies errors. FlutterFlow doesn't know what they are and doesnt count them as errors. Once you clear all errors, you can download and run and it will 'just work'. It took me a fair while to debug. If it doesnt work first time, you can load up your local copy of the FlutterFlow app source code and see where it fails. The main issues I faced (other than not understanding dart), were matching the dependencies to allow FlutterFlow to work. As of this writing xml: ^5.3.0
is the maximum FlutterFlow can support. This is easy to spot in a regular Flutter debutting tool (I use VSCodium which is a community-driven, freely-licensed binary distribution of VSCode and you can find out more about it in the links below). In VSCodium you can also see any print();
statements you have added in the developer logs.
Debugging with FlutterFlow Flutter based apps with VSCodium
Here you can see my app running on a real Google Android device attached over USB. The debug console shows me the idChannel
I passed to my custom code, and the latestVideo
URL I returned safely to FlutterFlow to put into its YouTube player.

I/flutter
and the Dart development tools (on the right the two grey stdout
entries show the same information.
See also
A script to get the RSS feed for a YouTube page by Timothy J. Luoma.
The FlutterFlow YoutubePlayer widget which is used to play a video from Youtube.
Dart.Dev - Get started: Command-line and server apps
Dart.Dev - Codelab on Asynchronous programming: futures, async, await
VSCodium and Flutter on this website