【问题标题】:Flutter url_launcher Unhandled Exception: Could not launch youtube url (Caused by canLaunch)Flutter url_launcher 未处理的异常:无法启动 youtube url(由 canLaunch 引起)
【发布时间】:2020-12-16 20:53:45
【问题描述】:

我正在尝试使用 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);
}

我不太清楚为什么不能使用README Example 中写的canLaunch 方法

错误:

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


    【解决方案1】:

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

    您可以添加:

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

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

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

    【讨论】:

    【解决方案2】:

    就个人而言,我不喜欢使用 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> 
    

    只是想分享一下,以防它帮助别人。

    【讨论】:

    • 谢谢。如果没有这些设置,我的 Android 应用程序将无法运行。 iOS有这样的设置吗?我正在 iPhone 12 Pro max 模拟器上进行测试。我的网址可以打开,但不能打开电话和电子邮件。所以,我想知道iOS也有这样的设置吗?
    • 没问题@Hesam,我已经用 iOS 特定信息更新了我的答案
    • 感谢您更新您的答案。我添加了这些东西,但电话和电子邮件仍然无法在模拟器上运行,尽管它可以在实际设备上运行。我不是 iOS 开发人员,所以我想知道这是模拟器问题还是 iOS 开发人员的常见问题。
    • 嗨@Hesam,是的,我似乎记得在我的测​​试中,Xcode 模拟器不支持电话和电子邮件。以下链接同意这一点:discussions.apple.com/thread/3830910dummies.com/web-design-development/mobile-apps/…。在您将 LSApplicationQueriesSchemes 添加到 info.plist 文件之前,电话和电子邮件是否在您的实际设备上工作?
    • 任务':app:processDebugResources'执行失败...进程无法访问该文件,因为它正被另一个进程使用。在android的manifest标签中添加上述内容后出现此错误。
    【解决方案3】:

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

    PRE STEPS:按照已接受的答案建议进行操作。

    AndroidManifest.xml&lt;application/&gt;之前添加以下标签

    <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: 'pratik13butani@gmail.com',
          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");
    

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

    【讨论】:

    • 我有一个问题,电话应用程序启动时写入了号码但没有被呼叫,我必须点击拨号才能拨打电话。这是包应该做的吗?
    【解决方案4】:

    兄弟只放“!”在“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';
    }
    

    【讨论】:

    • 它.. 对我有用吗? - 但是你可以完全删除“canLaunch”。 canLaunch 坏了吗?
    • 花了将近一个小时才找到这个简单的解决方案。谢谢!
    【解决方案5】:

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

    这是代码

    <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,如图所示

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

    【讨论】:

      【解决方案6】:

      确保在 android manifest 中添加以下行

      <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> 
      

      【讨论】:

        【解决方案7】:

        我在使用 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- 将以上内容添加到我的 AndroidManifest.xml 中的 app/src/main/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>
        

        现在一切正常

        【讨论】:

          【解决方案8】:

          如果上述方法均无效,请尝试将其添加到 AndroidManifest.xml 中(如果有,则为主要的)。在&lt;application&gt; 标记上方添加这段代码。

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

          【讨论】:

            【解决方案9】:

            我花了将近 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. 尝试卸载应用并运行代码

            【讨论】:

              【解决方案10】:

              这对我有用

              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';
                }
              }
              

              【讨论】:

                猜你喜欢
                • 2021-07-10
                • 1970-01-01
                • 2021-04-29
                • 1970-01-01
                • 2019-07-02
                • 2020-05-04
                • 1970-01-01
                • 2013-12-22
                • 1970-01-01
                相关资源
                最近更新 更多