Flutter url_launcher 未处理的异常:无法启动 youtube url(由 canLaunch 引起)

问题描述 投票:0回答:14

我正在尝试使用

url_launcher
插件通过链接打开 YouTube 视频,但
canLaunch
函数不断抛出错误。我只能通过完全删除
canLaunch
函数来绕过此错误,但无法找出问题所在。

代码不起作用:

_goToVideo(YoutubeVideoData video) async {
  if (await canLaunch(video.url)) {
    await launch(video.url);
  } else {
    throw 'Could not launch ${video.url}';
  }
}

代码工作:

_goToVideo(YoutubeVideoData video) async {
  await launch(video.url);
}

我不太清楚为什么我不能使用

canLaunch
中写的方法README示例

错误:

E/flutter (12574): [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: Could not launch https://www.youtube.com/watch?v=-3g5WlqJtIo
E/flutter (12574): #0      _goToVideo (package:esfandapp/widgets/newsList/videoCard.dart:71:5)
E/flutter (12574): <asynchronous suspension>
E/flutter (12574): #1      VideoCard.build.<anonymous closure> (package:esfandapp/widgets/newsList/videoCard.dart:13:20)
E/flutter (12574): #2      _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:992:19)
E/flutter (12574): #3      _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:1098:38)
E/flutter (12574): #4      GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:184:24)
E/flutter (12574): #5      TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:524:11)
E/flutter (12574): #6      BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:284:5)
E/flutter (12574): #7      BaseTapGestureRecognizer.acceptGesture (package:flutter/src/gestures/tap.dart:256:7)
E/flutter (12574): #8      GestureArenaManager.sweep (package:flutter/src/gestures/arena.dart:158:27)
E/flutter (12574): #9      GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:224:20)
E/flutter (12574): #10     GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:200:22)
E/flutter (12574): #11     GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:158:7)
E/flutter (12574): #12     GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:104:7)
E/flutter (12574): #13     GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:88:7)
E/flutter (12574): #14     _rootRunUnary (dart:async/zone.dart:1206:13)
E/flutter (12574): #15     _CustomZone.runUnary (dart:async/zone.dart:1100:19)
E/flutter (12574): #16     _CustomZone.runUnaryGuarded (dart:async/zone.dart:1005:7)
E/flutter (12574): #17     _invoke1 (dart:ui/hooks.dart:267:10)
E/flutter (12574): #18     _dispatchPointerDataPacket (dart:ui/hooks.dart:176:5)

小部件使用功能:

class VideoCard extends StatelessWidget {
  final YoutubeVideoData video;
  VideoCard({this.video});

  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: () => _goToVideo(video),
      child: Container(
        child: Card(
          child: Container(
            child: Column(
              children: [
                Align(
                  child: Padding(
                    child: Text(
                      video.title,
                      style: TextStyle(
                        fontFamily: 'Roboto Condensed',
                        fontSize: 16,
                      ),
                    ),
                    padding: EdgeInsets.fromLTRB(15, 0, 15, 10),
                  ),
                  alignment: Alignment.centerLeft,
                ),
                Container(
                    child: Image.network(video.thumbnails[1], fit: BoxFit.cover,),
                  width: MediaQuery.of(context).size.width,
                ),
                Align(
                  child: Container(
                    child: Text(
                      video.date.toString() + "",
                      style: TextStyle(
                        fontFamily: 'Roboto Condensed',
                        fontSize: 14,
                        fontWeight: FontWeight.w300,
                      ),
                    ),
                    padding: EdgeInsets.fromLTRB(15, 5, 15, 0),
                  ),
                  alignment: Alignment.centerLeft,
                ),
              ],
            ),
            width: MediaQuery.of(context).size.width - 32,
            padding: EdgeInsets.symmetric(
              horizontal: 0,
              vertical: 10,
            ),
            alignment: Alignment.center,
          ),
          shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.all(Radius.circular(25))),
        ),
      ),
    );
  }
}
flutter
14个回答
79
投票

从 API30 (Android 11) 开始,您的 Android 应用程序必须列出与其交互的所有应用程序。

您可以添加:

<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>

在您的 Android 清单中绕过它或专门列出它们。

欲了解更多信息: https://developer.android.com/about/versions/11/privacy/package-visibility


75
投票

就我个人而言,我不喜欢使用 QUERY_ALL_PACKAGES 权限带来的不确定性(因为 Google 将来可能会停止让人们使用它)。因此,我做了一些调查,发现将以下内容添加到我的 AndroidManifest.xml 中可以让我的应用程序在 API 30 上打开浏览器、手机应用程序和电子邮件应用程序:

<manifest>

    <!-- Nest within the manifest element, not the application element-->
    <queries>
        <intent>
            <action android:name="android.intent.action.VIEW" />
            <data android:scheme="https" />
        </intent>
        <intent>
            <action android:name="android.intent.action.DIAL" />
            <data android:scheme="tel" />
        </intent>
        <intent>
            <action android:name="android.intent.action.SEND" />
            <data android:mimeType="*/*" />
        </intent>
    </queries>

    <application>
        ....
    </application>
</manifest>

要在 iOS 上获得相同的功能,可能需要将以下内容添加到您的 info.plist 文件中:

<key>LSApplicationQueriesSchemes</key>
<array>
    <string>https</string>
    <string>http</string>
    <string>tel</string>
    <string>mailto</string>
</array> 

只是想分享,以防对其他人有所帮助。


10
投票

即使在尝试接受的答案后,如果它不适合您,请尝试以下代码。

准备步骤:按照建议接受的答案进行操作。

AndroidManifest.xml中在

<application/>

之前添加以下标签
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>

<queries>
    <intent>
        <action android:name="android.intent.action.VIEW" />
        <data android:scheme="https" />
    </intent>
    <intent>
        <action android:name="android.intent.action.DIAL" />
        <data android:scheme="tel" />
    </intent>
    <intent>
        <action android:name="android.intent.action.SEND" />
        <data android:mimeType="*/*" />
    </intent>
</queries>

现在我做了什么

创建一个方法:

Future<void> _makeSocialMediaRequest(String url) async {
    if (await canLaunch(url)) {
      await launch(url);
    } else {
      throw 'Could not launch $url';
    }
  }

并通过以下方式调用它:

  //FOR EMAIL
  final Uri _emailLaunchUri = Uri(
      scheme: 'mailto',
      path: '[email protected]',
      queryParameters: {'subject': 'Pratik Butani'});
  _makeSocialMediaRequest(_emailLaunchUri.toString());

  //FOR PHONE NUMBER:
  final Uri _phoneLaunchUri =
      Uri(scheme: 'tel', path: postOffice.mobileNo);
  _makeSocialMediaRequest(_phoneLaunchUri.toString());

  //FOR ANY URL.. YOU CAN PASS DIRECT URL..
  _makeSocialMediaRequest("http://pratikbutani.com");

它对我有用。希望它也对你有用。谢谢你。


7
投票

兄弟只放了'!'在“if (!await canLaunch(url))”之前 使用这个-->

 if (!await canLaunch(url)){
  await launch(
    url,
    forceSafariVC: false,
    forceWebView: false,
    headers: <String, String>{'my_header_key': 'my_header_value'},
  );
} else {
  
  throw 'Could not launch $url';
}

4
投票

对于 Android

Android/main/res/AndroidManifest.xml
中使用以下代码,如图所示

enter image description here

这是代码

<intent>
    <action android:name="android.intent.action.VIEW" />
    <data android:scheme="https" />
</intent>
<intent>
    <action android:name="android.intent.action.DIAL" />
    <data android:scheme="tel" />
</intent>
<intent>
    <action android:name="android.intent.action.SEND" />
    <data android:mimeType="*/*" />
</intent>

适用于 iPhone

ios/Runner/Info.plist
如图所示

enter image description here

<key>LSApplicationQueriesSchemes</key>
<array>
    <string>https</string>
    <string>http</string>
    <string>tel</string>
    <string>mailto</string>

3
投票

确保在 Android 清单中添加以下行

<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>

也不要忘记 Info.plist

<key>LSApplicationQueriesSchemes</key>
<array>
    <string>https</string>
    <string>http</string>
    <string>tel</string>
    <string>mailto</string>
</array> 

2
投票

我花了近 2 个小时才找到问题所在,在我从最近的选项卡关闭应用程序并重新运行应用程序后,它工作得非常好。

你可能错过的事情,

  1. 在 pubspec.yaml 中添加 Url_launcher 依赖项
  2. 在 Android 清单文件中的标签后添加查询

<queries>
    <intent>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="https" />
    </intent>
</queries>

  1. 在ios目录下的info.plist文件中添加key

<key>LSApplicationQueriesSchemes</key>
<array>
    <string>https</string>
    <string>http</string>
</array> 

  1. 只需尝试卸载应用程序并运行代码

0
投票

我在使用 win 和 linux 进行 Android 开发时遇到了同样的错误 并通过以下步骤解决它 这个想法是将 AndroidManifest.xml 添加到 app/src/main/AndroidManifest.xml 中 为此并使其正常工作,您必须更新 gardle 版本,因此步骤如下 1-将我的 gradle-wrapper.properties 更新为 gradle-6.7.1-all.zip

distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip

2-将 android/build.gradle 中的类路径更新为 com.android.tools.build:gradle:4.2.1

buildscript {
    ext.kotlin_version = '1.3.50'
    repositories {
        google()
        jcenter()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:4.2.1'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

rootProject.buildDir = '../build'
subprojects {
    project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
    project.evaluationDependsOn(':app')
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

3-将以上内容添加到 app/src/main/AndroidManifest.xml 中的 AndroidManifest.xml 中

   <queries>
        <!-- If your app opens https URLs -->
        <intent>
            <action android:name="android.intent.action.VIEW" />
            <data android:scheme="https" />
        </intent>
        <!-- If your app makes calls -->
        <intent>
            <action android:name="android.intent.action.DIAL" />
            <data android:scheme="tel" />
        </intent>
        <!-- If your app emails -->
        <intent>
            <action android:name="android.intent.action.SEND" />
            <data android:mimeType="*/*" />
        </intent>
    </queries>

现在一切正常


0
投票

如果以上方法均不起作用,请尝试将其添加到 AndroidManifest.xml 中(如果有,则添加到主文件中)。在

<application>
标签上方添加这段代码。

<queries>
    <intent>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="https" />
    </intent>
</queries>

0
投票
 if (await canLaunchUrl(Uri.parse(url))) {
      await launchUrl(Uri.parse(url),
      mode: LaunchMode.externalApplication, );
    } 

0
投票

对于 Android 只需添加您的 androidmanifest.xml 文件

<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>

对于 iOS 将这些添加到您的 info.plist 文件中

<key>LSApplicationQueriesSchemes</key>
<array>
    <string>https</string>
    <string>http</string>
    <string>tel</string>
    <string>mailto</string>
</array> 

0
投票

QUERY_ALL_PACKAGES
(不推荐)

因为如官方文档

中所述,Google将来可能会停止让人们使用它

如果您使用从应用程序启动任何网址,只需使用

<manifest package="com.example.game">
    <queries>
        <package android:name="com.android.chrome" />
        
    </queries>
    ...
</manifest>

同样,您可以根据您的需要添加

<queries>

例如。

<queries>
        <!-- If your app opens https URLs -->
        <intent>
            <action android:name="android.intent.action.VIEW" />
            <data android:scheme="https" />
        </intent>
        <!-- If your app makes calls -->
        <intent>
            <action android:name="android.intent.action.DIAL" />
            <data android:scheme="tel" />
        </intent>
        <!-- If your app emails -->
        <intent>
            <action android:name="android.intent.action.SEND" />
            <data android:mimeType="*/*" />
        </intent>
    </queries>

0
投票

我遇到了类似的问题,由于包含,我的应用程序在 Google Play 商店中被拒绝

<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>

清单中的许可。

我是这样解决的:

  1. 从 Android 清单中删除 QUERY_ALL_PACKAGES 权限 遵守 Google Play 商店政策。

  2. 将启动器包降级到版本 6.0.0,与 所需的权限。

  3. 此外,您可以像这样更新代码以避免潜在的问题 问题:

    if(等待launchUrl(Uri.parse(url))){ 等待启动(网址); } 别的 { 抛出“无法启动$url”; }

这解决了拒绝问题并确保我的应用程序正常运行。希望这可以帮助其他面临类似挑战的人!


-4
投票

这对我有用

if (!url.contains('http')) url = 'https://$url';

完整方法:

launchURL(String url) async {
  if (!url.contains('http')) url = 'https://$url';
  if (await canLaunch(url)) {
    await launch(url);
  } else {
    throw 'Could not launch $url';
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.