'Flutter WebView plugin unable to play some YouTube videos

The webview_flutter plugin is unable to play some YouTube embed videos that do work if played from within a web app. The videos display "Video unavailable". Playing YouTube videos inline in a Flutter app has been an issue for at least a year.

https://github.com/flutter/flutter/issues/13756

I have tried various Flutter plugins to play YouTube video inline but either they only support Android or don't work with YouTube.

I have tried using HTML string (YouTube iframe) directly inside WebView plugin but the video is unavailable. Loading HTML from a webserver allows for some videos to be played which otherwise didn't directly in the flutter app e.g. Some music videos display "Video unavailable. This video contains content from Vevo, who has blocked it from display on the website or application".

But if I launch the same video using the YouTube iframe API (see code) from a web application, it works without any errors i.e. the embed is not disabled but these videos don't play in Flutter app. I have also read similar issues with playing YouTube videos in Android where suggestion was to use a WebChromeClient, that is unavailable in Flutter.

Flutter app that displays a YouTube video inside WebView plugin.

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

// Use IP instead of localhost to access local webserver
const _youTubeUrl = 'http://localhost:8080';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'YouTube Video',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'YouTube Video'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  final String title;

  MyHomePage({Key key, this.title}) : super(key: key);

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

class _MyHomePageState extends State<MyHomePage> {
  WebViewController _controller;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Column(
        children: <Widget>[
          Expanded(
            child: WebView(
              initialUrl: '$_youTubeUrl/videos/IyFZznAk69U',
              javascriptMode: JavascriptMode.unrestricted,
              onWebViewCreated: (WebViewController controller) =>
                  _controller = controller,
            ),
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.play_arrow),
        onPressed: () async {
          await _controller.evaluateJavascript('player.loadVideoById("d_m5csmrf7I")');
        },
      ),
    );
  }
}

Node.js server side code that returns index.html via express routing

const path = require("path");
const express = require("express");

const server = express();

// Define route
api.get("/", (req, res) =>
  res.sendFile(path.join(__dirname + "/index.html"))
);

const port = process.env.PORT || 8080;

server.listen(port, () => console.log(`Server listening on ${port}`));

process.on("exit", () => console.log("Server exited"));

index.html file that uses YouTube iframe API served to the Flutter app

<!DOCTYPE html>
<html>

<head>
    <style>
        #player {
            position: relative;
            padding-top: 56.25%;
            min-width: 240px;
            height: 0;
        }

        iframe {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
        }
    </style>
    <script>
        var player;

        function onYouTubeIframeAPIReady() {
            player = new YT.Player("yt", {
                videoId: "IyFZznAk69U",
                playerVars: {
                    autoplay: 1
                }
            });
        }
    </script>
    <script src="https://www.youtube.com/iframe_api" async></script>
</head>

<body>
    <div id="player">
        <div id="yt"></div>
    </div>
</body>

</html>


Solution 1:[1]

You can use my plugin flutter_inappwebview to play youtube videos inside a webview.

Full example with your youtube video id:

import 'dart:async';

import 'package:flutter/material.dart';

import 'package:flutter_inappwebview/flutter_inappwebview.dart';

Future main() async {
  runApp(new MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> {

  @override
  void initState() {
    super.initState();
  }

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: InAppWebViewPage()
    );
  }
}

class InAppWebViewPage extends StatefulWidget {
  @override
  _InAppWebViewPageState createState() => new _InAppWebViewPageState();
}

class _InAppWebViewPageState extends State<InAppWebViewPage> {
  InAppWebViewController webView;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
            title: Text("InAppWebView")
        ),
        body: Container(
            child: Column(children: <Widget>[
              Expanded(
                child: Container(
                  child: InAppWebView(
                    initialUrl: "https://www.youtube.com/embed/IyFZznAk69U",
                    initialHeaders: {},
                    initialOptions: InAppWebViewWidgetOptions(
                      inAppWebViewOptions: InAppWebViewOptions(
                        debuggingEnabled: true,
                      ),
                    ),
                    onWebViewCreated: (InAppWebViewController controller) {
                      webView = controller;
                    },
                    onLoadStart: (InAppWebViewController controller, String url) {

                    },
                    onLoadStop: (InAppWebViewController controller, String url) {

                    },
                  ),
                ),
              ),
            ]))
    );
  }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Lorenzo Pichilli