HarmonyOS鸿蒙Next中做鸿蒙化flutter,功能需要保存图片到相册中

HarmonyOS鸿蒙Next中做鸿蒙化flutter,功能需要保存图片到相册中

import 'dart:io';

import 'package:dio/dio.dart';
import 'package:experimental_stu/components/toast/finish_toast.dart';
import 'package:experimental_stu/event/app_events.dart';
import 'package:experimental_stu/event/event_manager.dart';
import 'package:experimental_stu/services/app_permission_status.dart';
import 'package:experimental_stu/utils/assets_util.dart';
import 'package:experimental_stu/utils/toast_util.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart'
  if (dart.library.html) 'package:experimental_stu/web/image_gallery_saver_web.dart';
import 'package:file_system/file_system.dart' as fs;

class Ceshi extends StatefulWidget {
  double scale = 1;
  String qrUrl = '';

  /// 默认及加载失败图
  String defImgPath;
  final String title;
  double? titleSize;
  double? btnFontSize;
  Size? btnSize;

  Ceshi({
    Key? key,
    this.scale = 1,
    required this.title,
    this.titleSize,
    this.btnFontSize,
    this.btnSize,
    required this.qrUrl,
    required this.defImgPath,
  }) : super(key: key);
  @override
  _CeshiState createState() => _CeshiState();
}

class _CeshiState extends State<Ceshi> {
  Uint8List? qrData;
  @override
  initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) async {
      await AppPermissionStatus.instance.saveAlbum.getStatus();
      getImageAsUint8List(widget.qrUrl, AssetsUtil.successQr);
    });
  }

  createDefQr(double scale) {
    return Image.asset(AssetsUtil.successQr,
        width: 200 * scale, height: 200 * scale, fit: BoxFit.fitWidth);
  }

  showSuccessToast(double scale) {
    SmartDialog.showToast('',
        alignment: Alignment.center,
        displayTime: const Duration(seconds: 3), builder: (context) {
      return FinishToast(message: '已保存到相册', scale: scale);
    });
  }

  void showErrorToast(String message, double scale) {
    SmartDialog.showToast('',
        alignment: Alignment.center,
        displayTime: const Duration(seconds: 3), builder: (context) {
      return FinishToast(message: message, scale: scale);
    });
  }

  getImageAsUint8List(String imageUrl, String localAssetName) async {
    Dio dio = Dio();
    Uint8List? imageBytes;
    try {
      // 尝试从网络获取图片
      Response response = await dio.get(imageUrl,
          options: Options(responseType: ResponseType.bytes));
      imageBytes = response.data;
    } catch (e) {
      // 网络请求失败,从本地资产加载图片
      final ByteData data = await rootBundle.load(localAssetName);
      imageBytes = data.buffer.asUint8List();
    }
    if (mounted) {
      setState(() {
        qrData = imageBytes;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    double scale = widget.scale;
    return Container(
      width: 302.w * scale,
      height: 384.w * scale,
      padding: EdgeInsets.symmetric(
          horizontal: 35.w * scale, vertical: 20.w * scale),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(26.9.w),
      ),
      child: Column(
        children: [
          Text(
            widget.title,
            style: TextStyle(
              color: Colors.black,
              fontSize: widget.titleSize ?? 23.55.w,
              fontWeight: FontWeight.w600,
            ),
          ),
          Expanded(
            child: Container(
              width: 232.w * scale,
              height: 232.w * scale,
              alignment: Alignment.center,
              decoration: const BoxDecoration(
                image: DecorationImage(
                  image: AssetImage(
                      AssetsUtil.successQrBorder), // AssetImage用于加载项目中的图片
                  fit: BoxFit.fitWidth, // 图片填充方式
                ),
              ),
              child: qrData == null
                  ? createDefQr(scale)
                  : Image.memory(qrData!,
                      width: 200.w * scale,
                      height: 200.w * scale,
                      fit: BoxFit.fitWidth),
            ),
          ),
          GestureDetector(
            onTap: () async {
              if (qrData == null) {
                showErrorToast('图片不存在,无法保存', scale);
                return;
              }

              // if (Platform.isOhos) {
              //   Map result = await ImageGallerySaver.saveImage(qrData!);
              //   if (result['isSuccess']) {
              //     showSuccessToast(scale);
              //   } else {
              //     print('保存失败了Error: ${result['error']}'); // Handle erro
              //     ToastUtil.shortTime('保存失败');
              //   }
              //   return;
              // }
              bool isGranted = AppPermissionStatus.instance.saveAlbum.isGranted;
              if (!isGranted) {
                ShowPermissionRequestEvent event = ShowPermissionRequestEvent(
                    name: '媒体权限使用说明:',
                    deniedDescribe: '请开启存储权限',
                    tag: 'savePhone',
                    permissionAlbum: AppPermissionStatus.instance.saveAlbum,
                    describe: '用于保存图片到相册');
                EventManager.instance.eventBus.fire(event);
                isGranted = await event.completer.future;
              }
              if (isGranted) {
                await ImageGallerySaver.saveImage(qrData!);
                showSuccessToast(scale);
              }
            },
            child: Container(
              width: widget.btnSize?.width ?? 161.5.w * scale,
              height: widget.btnSize?.height ?? 53.8.w * scale,
              alignment: Alignment.center,
              decoration: BoxDecoration(
                color: const Color(0xFFFF7613),
                borderRadius: BorderRadius.circular(30.w * scale),
              ),
              child: Text(
                '保存图片',
                style: TextStyle(
                  color: Colors.white,
                  fontSize: widget.btnFontSize ?? 20.w,
                  fontWeight: FontWeight.w400,
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

import 'package:permission_handler/permission_handler.dart';

class AppPermissionStatus {
  // 单例实例
  static final AppPermissionStatus _instance = AppPermissionStatus._internal();

  // 私有构造函数,防止外部实例化
  AppPermissionStatus._internal() {
    // 在这里添加初始化逻辑
    // 例如:初始化权限状态、注册监听器等
    _initPermissionStatus();
  }

  // 公共静态方法,用于获取单例实例
  static AppPermissionStatus get instance => _instance;

  // 保存到相册能力
  PermissionAbility saveAlbum = PermissionAbility(list: [
    Permission.storage,
    Permission.photos,
  ]);

  /// 相机权限
  PermissionAbility camera = PermissionAbility(list: [Permission.camera]);

  /// 相册权限
  PermissionAbility mediaPhoto = PermissionAbility(list: [Permission.storage]);

  ///图片权限
  PermissionAbility photos = PermissionAbility(list: [Permission.photos]);

  /// 麦克风
  PermissionAbility microphone =
      PermissionAbility(list: [Permission.microphone]);

  // 添加私有初始化方法
  void _initPermissionStatus() async {
    // 初始化逻辑的具体实现
  }
}

/// 权限能力单元
class PermissionAbility {
  List<Permission> list;
  bool isGranted;
  PermissionAbility({required this.list, this.isGranted = false});

  Future<bool> getStatus() async {
    bool granted = true;
    int i = 0;
    for (i = 0; i < list.length; i++) {
      PermissionStatus status = await list[i].status;
      if (status != PermissionStatus.granted) {
        granted = false;
      }
    }
    isGranted = granted;
    return granted;
  }

  Future<bool> request() async {
    bool granted = isGranted;
    Map<Permission, PermissionStatus> status = await list.request();
    for (int i = 0; i < list.length; i++) {
      if (status[list[i]] == PermissionStatus.granted) {
        granted = true;
        break;
      }
    }
    isGranted = granted;
    return granted;
  }
}

是我主要的代码,其实代码已经写好了,现在唯一的问题是当我点击保存图片的时候他会提示"请开启存储权限",我在使用麦克风的时候开启麦克风权限可以正常开启,但是保存到相册的时候却没有存储权限


更多关于HarmonyOS鸿蒙Next中做鸿蒙化flutter,功能需要保存图片到相册中的实战教程也可以访问 https://www.itying.com/category-92-b0.html

12 回复

插眼学习,

更多关于HarmonyOS鸿蒙Next中做鸿蒙化flutter,功能需要保存图片到相册中的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


没有数据

cke_188.png

  1. 是否弹允许保存的弹窗了?需要用户点击允许授权才能保存到相册。
  2. 确认的的qrData是有图片数据的 如仍然有问题,可以给个你的详细demo,提供给git下载地址。

我没有遇到过允许保存的弹窗,我无法提供代码,其实这些就是差不多的主要代码了,你可以简单写一个demo吗就是简单保存相册就行,我也是非常诧异为什么我的代码有问题

楼主,你这边保存图片是用的flutter_image_gallery_saver三方库吧,这个库不需要申请存储权限,直接使用

ImageGallerySaver.saveImage(qrData!);保存图片即可,具体可以参考example中使用方式.

这是因为该三方库底层是用的API:phAccessHelper.showAssetsCreationDialog,不需要申请权限。

有要学HarmonyOS AI的同学吗,联系我:https://www.itying.com/goods-1206.html

我有想加过但是他是要用户授权的,也就是说他要申请白名单ACL才行,但是AGC平台不受理必须是要克隆,传输数据的才行。然后就提醒 Install Failed: error: failed to install bundle. code:9568289 error: install failed due to grant request permissions failed. View detailed instructions.

使用三方库image_gallery_saver这个不需要申请acl权限,直接调用即可。然后会弹个窗用户允许即可保存到相册。

哥有案例吗,因为你也能看到我的代码里用到了image_gallery_saver,但是压根走不到那里去,直接在

bool isGranted = AppPermissionStatus.instance.saveAlbum.isGranted;
if (!isGranted) {
  ShowPermissionRequestEvent event = ShowPermissionRequestEvent(
      name: '媒体权限使用说明:',
      deniedDescribe: '请开启存储权限',
      tag: 'savePhone',
      permissionAlbum: AppPermissionStatus.instance.saveAlbum,
      describe: '用于保存二维码到相册');
  EventManager.instance.eventBus.fire(event);
  isGranted = await event.completer.future;
}

就卡住了,

在HarmonyOS鸿蒙Next中,使用鸿蒙化Flutter保存图片到相册需通过@ohos.file.photoAccessHelper模块实现。首先获取photoAccessHelper实例,创建相册资源写入选项,使用createAsset方法写入图片数据到指定媒体库位置。需申请ohos.permission.WRITE_IMAGEVIDEO权限。Flutter侧通过MethodChannel调用鸿蒙原生接口传递图片字节数据。

在HarmonyOS Next中,Flutter应用保存图片到相册需要适配鸿蒙的权限机制。你当前使用的是permission_handler插件,该插件在鸿蒙环境下可能无法正确映射存储权限。

解决方案:

  1. 使用鸿蒙原生权限API替代permission_handler
  2. AndroidManifest.xml中确认已声明ohos.permission.WRITE_USER_STORAGE权限
  3. 实现鸿蒙特定的权限请求逻辑:
// 鸿蒙权限检查示例
import 'package:harmonyos_flutter/harmonyos_flutter.dart';

Future<bool> checkStoragePermission() async {
  final result = await PermissionUtil.checkSelfPermission(
    'ohos.permission.WRITE_USER_STORAGE'
  );
  return result == PermissionGrantedResult.GRANTED;
}

注意:image_gallery_saver插件在鸿蒙平台可能需要鸿蒙特定的实现,确保使用支持HarmonyOS的版本或自行实现原生桥接。

回到顶部