使用flutter_email_sender发送文件真机失效问题排查求助
问题解决:Flutter真机无法通过Gmail发送PDF/Word文件
核心问题分析
模拟器环境对应用间交互的权限限制较宽松,但真机(尤其是Android 10+)受Scoped Storage和应用查询权限的约束,导致直接传递文件路径无法被Gmail访问,或无法触发Gmail的启动Intent。
分步解决方案
1. 补充AndroidManifest的应用查询权限
当前queries仅配置了SENDTO(用于无附件的邮件链接),但发送带附件的邮件需要SEND和SEND_MULTIPLE动作声明。修改AndroidManifest.xml的<queries>块:
<queries> <!-- 保留原有配置 --> <intent> <action android:name="android.intent.action.SENDTO" /> <data android:scheme="mailto" /> </intent> <!-- 添加以下两个intent --> <intent> <action android:name="android.intent.action.SEND" /> <data android:mimeType="*/*" /> </intent> <intent> <action android:name="android.intent.action.SEND_MULTIPLE" /> <data android:mimeType="*/*" /> </intent> <!-- 保留其他原有intent --> </queries>
2. 处理Android 10+的Scoped Storage限制
真机上直接传递外部存储的文件路径会导致Gmail无权限访问,需要通过FileProvider生成可共享的Content Uri:
步骤1:注册FileProvider
在AndroidManifest.xml的<application>标签内添加:
<provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
步骤2:创建文件路径配置
在res/xml目录下新建file_paths.xml(无xml目录则手动创建):
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-files-path name="app_files" path="." /> <external-path name="external_files" path="." /> </paths>
步骤3:修改发送逻辑
更新VacancyController的sendFile方法,复制文件到应用共享目录并生成Content Uri:
import 'package:path_provider/path_provider.dart'; import 'package:flutter/services.dart'; Future<String> sendFile() async { if (file != null) { try { // 将选中文件复制到应用可共享的外部存储目录 final appExternalDir = await getExternalFilesDirectory(null); final fileName = file!.path.split('/').last; final copiedFile = await file!.copy('${appExternalDir!.path}/$fileName'); // 生成Content Uri final contentUri = FileProvider.getUriForFile( Get.context!, '${Get.context!.packageName}.fileprovider', copiedFile, ); final Email email = Email( body: 'CV- faylı bizimlə paylaşın!', subject: 'Vakansiya', recipients: ['ahmadovf@box.az'], attachmentPaths: [contentUri.toString()], // 传入Uri字符串 isHTML: false, ); await FlutterEmailSender.send(email); _file.value = null; // 清理临时文件 await copiedFile.delete(); return 'success'; } catch (e) { print('发送错误详情: $e'); return 'fail: ${e.toString()}'; } } return 'fail'; }
3. 添加错误捕获排查问题
如果暂时不想修改FileProvider逻辑,先在sendFile中加入try-catch,打印真机上的具体错误,快速定位问题:
Future<String> sendFile() async { if (file != null) { try { final Email email = Email( body: 'CV- faylı bizimlə paylaşın!', subject: 'Vakansiya', recipients: ['ahmadovf@box.az'], attachmentPaths: [file!.path.toString()], isHTML: false, ); await FlutterEmailSender.send(email); _file.value = null; return 'success'; } catch (e) { print('发送失败原因: $e'); return 'fail: ${e.toString()}'; } } return 'fail'; }
4. Android 13+权限适配
如果真机是Android 13及以上,需要添加读取文档的权限到AndroidManifest.xml:
<uses-permission android:name="android.permission.READ_MEDIA_DOCUMENTS" />
并在运行时通过permission_handler包请求该权限。
内容的提问来源于stack exchange,提问作者Farid Ahmadov
相关产品推荐
相关产品推荐

