我正在构建一个Flutter应用程序,它基本上是从云端获取数据.

现在我想激发一个隐式意图,这样用户就可以 Select 他们最喜欢的应用程序来处理负载.

我一直在寻找答案,我try 了以下几种途径:

  1. url_Launcher插件(如How to open an application from a Flutter app?中建议的那样)
  2. Android意图
  3. 共享插件

路#3真的不是我想要的,因为它使用的是平台的"共享"机制(即.在Twitter上发布/发送给联系人),而不是打开有效负载.

路由1和2有点奏效.以一种摇摇欲坠的、怪异的方式.我稍后会解释的.

下面是我的代码流程:

import 'package:url_launcher/url_launcher.dart';

// ...
// retrieve payload from internet and save it to an External Storage location
File payload = await getPayload();
String uriToShare = samplePayload.uri.toString();

// at this point uriToShare looks like: 'file:///storage/emulated/0/jpg_example.jpg'
uriToShare = uriToShare.replaceFirst("file://", "content://");

// launch url    
if (await canLaunch(uriToShare)) {
  await launch(uriToShare);
} else {
  throw "Failed to launch $uriToShare";

上面的代码使用的是url_launcher插件.如果我使用的是android_intent插件,那么最后一行代码变成:

// fire intent 
AndroidIntent intent = AndroidIntent(
  action: "action_view",
  data: uriToShare,
);
await intent.launch();

在将文件保存到外部目录之前,所有操作都可以正常工作(运行代码后,我可以确认文件是否存在)

当我试图共享URI时,事情变得很奇怪. 我已经在3部不同的手机上测试了这段代码.其中之一(三星Galaxy S9)会抛出以下异常:

I/io.flutter.plugins.androidintent.AndroidIntentPlugin(10312): Sending intent Intent { act=android.intent.action.VIEW dat=content:///storage/emulated/0/jpg_example.jpg }
E/MethodChannel#plugins.flutter.io/android_intent(10312): Failed to handle method call
E/MethodChannel#plugins.flutter.io/android_intent(10312): java.lang.SecurityException: Permission Denial: starting Intent { act=android.intent.action.VIEW dat=content:///storage/emulated/0/jpg_example.jpg cmp=com.google.android.gm/.browse.TrampolineActivity } from ProcessRecord{6da6f74 10312:com.safe.fmeexpress/u0a218} (pid=10312, uid=10218) requires com.google.android.gm.permission.READ_GMAIL
E/MethodChannel#plugins.flutter.io/android_intent(10312):   at android.os.Parcel.readException(Parcel.java:1959)
E/MethodChannel#plugins.flutter.io/android_intent(10312):   at android.os.Parcel.readException(Parcel.java:1905)
E/MethodChannel#plugins.flutter.io/android_intent(10312):   at android.app.IActivityManager$Stub$Proxy.startActivity(IActivityManager.java:4886)
E/MethodChannel#plugins.flutter.io/android_intent(10312):   at android.app.Instrumentation.execStartActivity(Instrumentation.java:1617)
E/MethodChannel#plugins.flutter.io/android_intent(10312):   at android.app.Activity.startActivityForResult(Activity.java:4564)
E/MethodChannel#plugins.flutter.io/android_intent(10312):   at android.app.Activity.startActivityForResult(Activity.java:4522)
E/MethodChannel#plugins.flutter.io/android_intent(10312):   at android.app.Activity.startActivity(Activity.java:4883)
E/MethodChannel#plugins.flutter.io/android_intent(10312):   at android.app.Activity.startActivity(Activity.java:4851)
E/MethodChannel#plugins.flutter.io/android_intent(10312):   at io.flutter.plugins.androidintent.AndroidIntentPlugin.onMethodCall(AndroidIntentPlugin.java:141)
E/MethodChannel#plugins.flutter.io/android_intent(10312):   at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:191)
E/MethodChannel#plugins.flutter.io/android_intent(10312):   at io.flutter.view.FlutterNativeView.handlePlatformMessage(FlutterNativeView.java:152)
E/MethodChannel#plugins.flutter.io/android_intent(10312):   at android.os.MessageQueue.nativePollOnce(Native Method)
E/MethodChannel#plugins.flutter.io/android_intent(10312):   at android.os.MessageQueue.next(MessageQueue.java:325)
E/MethodChannel#plugins.flutter.io/android_intent(10312):   at android.os.Looper.loop(Looper.java:142)
E/MethodChannel#plugins.flutter.io/android_intent(10312):   at android.app.ActivityThread.main(ActivityThread.java:6938)
E/MethodChannel#plugins.flutter.io/android_intent(10312):   at java.lang.reflect.Method.invoke(Native Method)
E/MethodChannel#plugins.flutter.io/android_intent(10312):   at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
E/MethodChannel#plugins.flutter.io/android_intent(10312):   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)

我不知道这个意图是怎么被cmp=com.google.android.gm/.browse.TrampolineActivity度污染的

这一例外仅在Galaxy S9中出现. 另外两部手机没有给我带来这个问题. 他们确实启动了文件uri,有人问我如何打开这个文件,但没有提供任何图像处理应用程序(如Gallery、QuickPic或Google Photos).

为了澄清一下,url_launcherandroid_intent路由都会产生完全相同的结果.

感觉我好像错过了一步.有人能指出我做错了什么吗?我是否必须开始使用平台渠道来完成此任务?

关于我为什么这么做的一些澄清:

  • 将uri类型从file://转换为content://,因为我得到的是android.os.FileUriExposedException
  • 将临时文件存储在外部存储中,因为我现在还不想处理授予URI读取权限的问题.(我试过了,但android_intent目前还没有办法设置意向标志)

推荐答案

很难让它正常工作.这里有一些提示,帮助我从Ffltter启动了ACTION_VIEW个意向,并用Ffltter下载了文件.

1)在Android list 中注册FileProvider:

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.example.myapp.provider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/provider_paths"/>
</provider>

provider_paths.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <paths>
        <external-path name="external_files" path="." />
    </paths>

2) 创建一个提供2种方法的定制平台频道(下面的代码是Kotlin):

100:应返回下载文件放置的数据目录.试试这个:

val downloadsDir = applicationContext.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).path
result.success(downloadsDir);

100,它接受2个字符串参数:path(DART中的File.path)和type(例如"application/pdf"):

val file = File(path);
val uri = FileProvider.getUriForFile(this, "com.example.myapp.provider", file);

val viewFileIntent = Intent(Intent.ACTION_VIEW);
viewFileIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_GRANT_READ_URI_PERMISSION);
viewFileIntent.setDataAndType(uri, type);
try {
  startActivity(viewFileIntent);
  result.success(null);
} catch (e: ActivityNotFoundException) {
  result.error(...);
}

最重要的部分是创造FileProvider人.url_launcher和android_意向将不起作用,你必须创建自己的平台频道.您可以更改下载路径,但还必须找到正确的Provider /权限设置.

在iOS上实现这一点也是可能的,但是超出了这个问题的范围.


如果您使用的是image_picer插件:

FileProvider与当前版本的image_picer(0.4.6)插件冲突,修复程序将很快发布.

Flutter相关问答推荐

Riverpod生成器和生成util类

CLI数据库连接后将SHA-1密钥添加到Firebase项目

在Flutter 中实现中小型应用程序栏标题

修复后期变量的抖动皮棉

无法在仿真器上运行Flutter 项目-文件名太长错误

在flutter web上渲染多个Youtube视频

BaseInputfield和Textfield有什么不同?

Riverpod 2.0-如何使用FutureOr和AutoDisposeSyncNotificationer处理api状态代码?

如何使listview.builder在筛选列表后实时更新

SliverAppbar 呈蓝色

自定义类提供程序 Riverpod Flutter 中的可变变量

如何使Flutter 边框宽度在外面而不是在里面?

如何删除图像边框半径与其父容器边框半径之间的空白

在 flutter 的另一个类中使用变量值

Flutter 构建方法使用的是旧版本的变量?

有没有办法为文本中的下划线创建自定义样式?

Flutter HLS .m3u8 视频无法实时播放

如何在 flutter 的alert 对话框中实现进度指示器?

我正在try 使用 Firebase 在 Flutter 中使用 Google 注销,但它不起作用

Flutter 上Denim blue colored颜色 的 colored颜色 矩阵代码是什么?