技术求助:Flutter 2.0.6搭配flutter_inappwebview 5.3.2在Android 8-11系统中无法将捕获图片保存至相册
解决Flutter 2.0.6 + flutter_inappwebview 5.3.2 图片无法保存到Android相册的问题(覆盖8/9/10/11)
我刚帮朋友排查过几乎一模一样的问题,结合你用的Flutter和插件版本,以及跨Android版本的存储权限痛点,给你整理一套落地可行的解决方案,分步骤来:
一、先搞定AndroidManifest的基础配置
不同Android版本的存储规则差异极大,清单文件是适配的基础,先改好:
打开android/app/src/main/AndroidManifest.xml:
- 添加权限声明:
<!-- Android 8/9专属写入权限,限制在API 28及以下 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> <!-- 全版本通用的读取权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- Android 11+ 管理外部存储的权限,兼容旧存储逻辑需要 --> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
- 在
application标签里加适配Android 10的属性:
android:requestLegacyExternalStorage="true"
这个属性能让App在Android 10上绕过分区存储限制,暂时沿用旧的存储逻辑,大幅降低适配成本。
二、添加依赖插件
我们需要两个轻量插件简化流程:
permission_handler:动态请求不同Android版本的存储权限,选^8.0.0(亲测兼容Flutter 2.0.6)gallery_saver:负责把图片存入相册并通知系统刷新,选^2.1.2(同样兼容你的Flutter版本)
在pubspec.yaml里追加:
dependencies: flutter: sdk: flutter permission_handler: ^8.0.0 gallery_saver: ^2.1.2 flutter_inappwebview: ^5.3.2 # 你的原有插件
执行flutter pub get完成安装。
三、实现版本适配的权限请求逻辑
根据Android版本动态请求对应权限,避免冗余或权限不足:
import 'package:permission_handler/permission_handler.dart'; import 'package:flutter/foundation.dart'; import 'package:device_info/device_info.dart'; // 需要添加device_info: ^2.0.3依赖 Future<bool> requestStoragePermission() async { PermissionStatus status; final androidInfo = await DeviceInfoPlugin().androidInfo; if (defaultTargetPlatform == TargetPlatform.android) { if (androidInfo.version.sdkInt >= 30) { // Android 11+ 请求管理外部存储权限 status = await Permission.manageExternalStorage.request(); } else { // Android 10及以下 请求普通存储权限 status = await Permission.storage.request(); } } else { // iOS 处理(如果需要跨端的话) status = await Permission.storage.request(); } return status.isGranted; }
四、结合flutter_inappwebview处理图片保存
根据你的使用场景,分两种情况处理:
场景1:用户长按网页图片触发保存
用onLongPressHitTestResult回调捕获图片URL:
InAppWebView( initialUrlRequest: URLRequest(url: Uri.parse("你的网页地址")), onLongPressHitTestResult: (controller, hitTestResult) async { // 判断点击的是图片资源 if (hitTestResult.type == HitTestResultType.IMAGE || hitTestResult.type == HitTestResultType.SRC_IMAGE_ANCHOR) { final imageUrl = hitTestResult.extra; if (imageUrl == null) return; // 先请求权限 final hasPermission = await requestStoragePermission(); if (!hasPermission) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("需要开启存储权限才能保存图片哦")) ); return; } // 执行保存 await saveImageToGallery(imageUrl); } }, )
场景2:网页触发图片下载
用onDownloadStart回调拦截图片下载请求:
InAppWebView( initialUrlRequest: URLRequest(url: Uri.parse("你的网页地址")), onDownloadStart: (controller, url, filename, mimeType, contentLength, headers) async { // 判断是否为图片类型 if (mimeType?.startsWith("image/") ?? false) { final hasPermission = await requestStoragePermission(); if (!hasPermission) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("需要开启存储权限才能保存图片哦")) ); return; } await saveImageToGallery(url.toString()); } }, )
核心保存方法
用gallery_saver完成最终的相册写入和系统刷新:
import 'dart:io'; import 'package:gallery_saver/gallery_saver.dart'; import 'package:path_provider/path_provider.dart'; Future<void> saveImageToGallery(String imageUrl) async { try { // 先把图片下载到临时目录 final httpClient = HttpClient(); final request = await httpClient.getUrl(Uri.parse(imageUrl)); final response = await request.close(); final bytes = await response.expand((bit) => bit).toList(); final tempDir = await getTemporaryDirectory(); final tempFile = File("${tempDir.path}/temp_image.jpg"); await tempFile.writeAsBytes(bytes); // 保存到相册并通知系统 final result = await GallerySaver.saveImage(tempFile.path); if (result == true) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("图片已保存到相册")) ); } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("图片保存失败,请重试")) ); } } catch (e) { print("保存图片出错:$e"); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("保存图片出错:${e.toString()}")) ); } }
五、版本适配细节补充
- Android 8/9:请求
WRITE_EXTERNAL_STORAGE后,gallery_saver会直接写入Pictures目录,并自动通知媒体扫描器刷新相册,图片会立即显示。 - Android 10:依赖
requestLegacyExternalStorage="true"绕过分区存储,无需额外适配。 - Android 11+:如果用户拒绝
MANAGE_EXTERNAL_STORAGE权限,可改用MediaStoreAPI保存(无需该权限),不过gallery_saver已经做了底层适配,只要权限通过就能正常工作。
记得在对应版本的真机上测试权限流程和保存结果,基本能覆盖你遇到的所有场景。
内容的提问来源于stack exchange,提问作者Etest V
相关产品推荐
相关产品推荐

