You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

技术求助: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:

  1. 添加权限声明:
<!-- 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" />
  1. 在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权限,可改用MediaStore API保存(无需该权限),不过gallery_saver已经做了底层适配,只要权限通过就能正常工作。

记得在对应版本的真机上测试权限流程和保存结果,基本能覆盖你遇到的所有场景。

内容的提问来源于stack exchange,提问作者Etest V

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 20:17:46