10

I basically want to open a specific youtube video from my app, when a button is pressed. If the youtube app is installed on the user's device, then the video should be opened in the youtube app (and not in the browser or a separate webview).

I used the url_launcher package for that, and it works fine on android. However on iOS the youtube app is not opened even if it is installed, instead a separate web window is opened, where the corresponding youtube url is shown as a webpage.

I thought, that I could override this behaviour like so:

_launchURL() async {
  if (Platform.isIOS) {
    if (await canLaunch('youtube://www.youtube.com/channel/UCwXdFgeE9KYzlDdR7TG9cMw')) {
      await launch('youtube://www.youtube.com/channel/UCwXdFgeE9KYzlDdR7TG9cMw');
    } else {
      if (await canLaunch('https://www.youtube.com/channel/UCwXdFgeE9KYzlDdR7TG9cMw')) {
        await launch('https://www.youtube.com/channel/UCwXdFgeE9KYzlDdR7TG9cMw');
      } else {
        throw 'Could not launch https://www.youtube.com/channel/UCwXdFgeE9KYzlDdR7TG9cMw';
      }
    }
  } else {
    const url = 'https://www.youtube.com/channel/UCwXdFgeE9KYzlDdR7TG9cMw';
    if (await canLaunch(url)) {
      await launch(url);
    } else {
      throw 'Could not launch $url';
    }
  }
}

but it didn’t work. In case you wonder, I use the following imports:

import 'dart:io' show Platform;
import 'package:url_launcher/url_launcher.dart';

I am pretty sure, the youtube:// URL-Scheme works (launches the YouTube app), because I tested it on third party apps (Launch Center Pro and Pythonista).

The last thing I was not able to test, is if the Platform.isIOS is really true on my IPhone.

Is there a working way, to open the YouTube App from flutter?

frankenapps
  • 5,800
  • 6
  • 28
  • 69

5 Answers5

19

I fixed it. I had to set forceSafariVC: false, because it is true on default, which causes the url to be opened inside a sort of webview inside the app.

_launchURL() async {
  if (Platform.isIOS) {
    if (await canLaunch('youtube://www.youtube.com/channel/UCwXdFgeE9KYzlDdR7TG9cMw')) {
      await launch('youtube://www.youtube.com/channel/UCwXdFgeE9KYzlDdR7TG9cMw', forceSafariVC: false);
    } else {
      if (await canLaunch('https://www.youtube.com/channel/UCwXdFgeE9KYzlDdR7TG9cMw')) {
        await launch('https://www.youtube.com/channel/UCwXdFgeE9KYzlDdR7TG9cMw');
      } else {
        throw 'Could not launch https://www.youtube.com/channel/UCwXdFgeE9KYzlDdR7TG9cMw';
      }
    }
  } else {
    const url = 'https://www.youtube.com/channel/UCwXdFgeE9KYzlDdR7TG9cMw';
    if (await canLaunch(url)) {
      await launch(url);
    } else {
      throw 'Could not launch $url';
    }
}

This is actually documented in the url_launcher docs, but somewhat hidden...

frankenapps
  • 5,800
  • 6
  • 28
  • 69
  • 1
    canLaunch('youtube://www.youtube…') gives a false but launch('youtube://www.youtube…') works forceSafariVC seems false on default? I’m using flutter 1.7.8 & url_launcher: ^5.0.2 on iOS – Suyon Won Aug 25 '19 at 11:23
  • 1
    LSApplicationQueriesSchemes              youtube      solved the problem – Suyon Won Aug 25 '19 at 12:05
  • 1
    As of iOS 9, you also need the LSApplicationQueriesSchemes set in the info.plist to an array containing youtube: https://stackoverflow.com/a/31086148/6024667 – punjabi4life Oct 05 '19 at 18:50
  • New version of url_launcher doesn't have forceSafariVC option :( – msalihbindak Aug 11 '22 at 09:05
6

You don't have to have all that if/else clauses. The most important thing to take into consideration is that whether the device has the YouTube app or not, regardless of the O.S (and remember to define your function as Future because of the async):

Future<void> _launchYoutubeVideo(String _youtubeUrl) async {
if (_youtubeUrl != null && _youtubeUrl.isNotEmpty) {
  if (await canLaunch(_youtubeUrl)) {
    final bool _nativeAppLaunchSucceeded = await launch(
      _youtubeUrl,
      forceSafariVC: false,
      universalLinksOnly: true,
    );
    if (!_nativeAppLaunchSucceeded) {
      await launch(_youtubeUrl, forceSafariVC: true);
    }
  }
 }
}

The thing to highlight here to avoid several if/else si the attribute universalLinksOnly set to true.

Phillip Schulze
  • 118
  • 2
  • 8
Carlos Daniel
  • 2,459
  • 25
  • 30
1

I have solved the issue. You can try the below code:

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

    void main() => runApp(const HomePage());  

    class HomePage extends StatelessWidget {  
      const HomePage({Key? key}) : super(key: key);  
      @override  
      Widget build(BuildContext context) {  
        return const MaterialApp(  
          debugShowCheckedModeBanner: false,  
          home: MyApp(),  
        );  
      }  
    }  

    class MyApp extends StatefulWidget {  
      const MyApp({Key? key}) : super(key: key);  
      @override  
      _MyAppState createState() => _MyAppState();
    }  

    class _MyAppState extends State<MyApp> {  
      Future<void>? _launched;  
      @override  
      Widget build(BuildContext context) {  
        const String toLaunch = 'https://www.youtube.com/watch?v=WcZ8lTRTNM0';  
        return Scaffold(  
          appBar: AppBar(title: const Text('Flutter Demo')),  
          body: Center(  
            child: ElevatedButton(  
              onPressed: () => setState(() {  
                _launched = _launchInBrowser(toLaunch);  
              }),  
              child: const Text('Launch in browser'),  
            ),  
          ), );  
      }  
      Future<void> _launchInBrowser(String url) async {  
        if (!await launch(  
          url,  
          forceSafariVC: true,  
          forceWebView: false,  
          headers: <String, String>{'my_header_key': 'my_header_value'},  
        )) {  
          throw 'Could not launch $url';  
        }  
      }  
    }
Er1
  • 2,559
  • 1
  • 12
  • 24
Ke1212
  • 89
  • 9
0

Using same package: https://pub.dev/packages/url_launcher

Here is latest working example. Most answers above is outdated or using deprecated package. Default mode is LaunchMode.platformDefault. Change to LaunchMode.externalApplication will open youtube app. Hope this helps

Future<dynamic> openUrl(String url, callback) async {
  try {
    if (await launchUrl(Uri.parse(url), mode: LaunchMode.externalApplication)) {
      callback(true);
    } else {
      toastMessage('#1: Could not launch $url');
      callback(false);
    }
  } catch (e) {
    toastMessage('#2: Could not launch $url');
    callback(false);
  }
}
Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
codetinker
  • 754
  • 10
  • 9
0

For some reason, canLaunch returns False for me, even with the YouTube app installed. You also cannot try-catch the launch, because failure to launch does not raise an exception.

Instead, you can pass in the success / fail status of the _launch() call into .then() and check the value. If it did not launch the app successfully, you can then launch via web browser.

This works for me:

void _launchYouTubeVideo(youtubeURL) async {
  String _appURL = youtubeURL;

  // URLs sent to YouTube app can't start with https

  if (_appURL.startsWith("https://")) {
    _appURL = _appURL.replaceFirst("https://", ""); // remove "https://" if it exists
  }

  try {
    await launch('youtube://$_appURL', forceSafariVC: false).then((bool isLaunch) async {
      print('isLaunch: $isLaunch');
      if (!isLaunch) {
        // Launch Success
        print("Launching in browser now ...");
        await launch(youtubeURL);
      } else {
        // Launch Fail
        print("YouTube app Launched!");
      }
    });
  } catch (e) {
    print("An error occurred: $e");
    await launch(youtubeURL);
  }
}