152 lines
4.3 KiB
Dart
152 lines
4.3 KiB
Dart
import 'dart:async';
|
|
import 'dart:io';
|
|
import 'dart:ui' as ui;
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter/scheduler.dart';
|
|
import 'package:image_gallery_saver/image_gallery_saver.dart';
|
|
import 'package:permission_handler/permission_handler.dart';
|
|
|
|
class ImageSaveService {
|
|
/// 保存图片到相册 - 改进版本,避免 debugNeedsPaint 错误
|
|
static Future<bool> saveImageToGallery(
|
|
GlobalKey repaintBoundaryKey, {
|
|
String? fileName,
|
|
}) async {
|
|
try {
|
|
// 1. 检查权限
|
|
final hasPermission = await _requestPermission();
|
|
if (!hasPermission) {
|
|
debugPrint('没有相册访问权限');
|
|
return false;
|
|
}
|
|
|
|
// 2. 使用调度器确保在正确的时机捕获
|
|
final completer = Completer<bool>();
|
|
|
|
SchedulerBinding.instance.addPostFrameCallback((_) async {
|
|
try {
|
|
// 额外等待确保所有绘制完成
|
|
await Future.delayed(const Duration(milliseconds: 100));
|
|
|
|
final result = await _captureAndSave(repaintBoundaryKey, fileName);
|
|
completer.complete(result);
|
|
} catch (e) {
|
|
debugPrint('捕获图片失败: $e');
|
|
completer.complete(false);
|
|
}
|
|
});
|
|
|
|
return await completer.future;
|
|
} catch (e) {
|
|
debugPrint('保存图片失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 内部方法:捕获并保存图片
|
|
static Future<bool> _captureAndSave(
|
|
GlobalKey repaintBoundaryKey,
|
|
String? fileName,
|
|
) async {
|
|
try {
|
|
// 获取 RepaintBoundary
|
|
final boundary =
|
|
repaintBoundaryKey.currentContext?.findRenderObject()
|
|
as RenderRepaintBoundary?;
|
|
|
|
if (boundary == null) {
|
|
debugPrint('无法获取RepaintBoundary');
|
|
return false;
|
|
}
|
|
|
|
// 多次尝试捕获,避免绘制冲突
|
|
ui.Image? image;
|
|
int attempts = 0;
|
|
const maxAttempts = 5;
|
|
|
|
while (attempts < maxAttempts) {
|
|
try {
|
|
// 检查是否正在绘制
|
|
if (boundary.debugNeedsPaint) {
|
|
debugPrint(
|
|
'RepaintBoundary 正在绘制中,等待... (尝试 ${attempts + 1}/$maxAttempts)',
|
|
);
|
|
await Future.delayed(Duration(milliseconds: 200 * (attempts + 1)));
|
|
attempts++;
|
|
continue;
|
|
}
|
|
|
|
// 尝试捕获图片
|
|
image = await boundary.toImage(pixelRatio: 3.0);
|
|
break;
|
|
} catch (e) {
|
|
debugPrint('捕获尝试 ${attempts + 1} 失败: $e');
|
|
attempts++;
|
|
if (attempts < maxAttempts) {
|
|
await Future.delayed(Duration(milliseconds: 300 * attempts));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (image == null) {
|
|
debugPrint('所有捕获尝试都失败了');
|
|
return false;
|
|
}
|
|
|
|
// 转换为字节数据
|
|
final byteData = await image.toByteData(format: ui.ImageByteFormat.png);
|
|
if (byteData == null) {
|
|
debugPrint('无法转换图片数据');
|
|
return false;
|
|
}
|
|
|
|
final pngBytes = byteData.buffer.asUint8List();
|
|
|
|
// 保存到相册
|
|
final result = await ImageGallerySaver.saveImage(
|
|
pngBytes,
|
|
name: fileName ?? 'MoodCanvas_${DateTime.now().millisecondsSinceEpoch}',
|
|
quality: 100,
|
|
);
|
|
|
|
debugPrint('保存结果: $result');
|
|
return result['isSuccess'] == true;
|
|
} catch (e) {
|
|
debugPrint('_captureAndSave 失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 请求相册访问权限
|
|
static Future<bool> _requestPermission() async {
|
|
if (Platform.isIOS) {
|
|
// iOS 14+ 需要 photos 权限
|
|
final status = await Permission.photos.request();
|
|
return status.isGranted;
|
|
} else if (Platform.isAndroid) {
|
|
// Android 需要存储权限
|
|
final status = await Permission.storage.request();
|
|
return status.isGranted;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// 检查权限状态
|
|
static Future<bool> checkPermission() async {
|
|
if (Platform.isIOS) {
|
|
final status = await Permission.photos.status;
|
|
return status.isGranted;
|
|
} else if (Platform.isAndroid) {
|
|
final status = await Permission.storage.status;
|
|
return status.isGranted;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// 打开应用设置页面
|
|
static Future<void> openSettings() async {
|
|
await openAppSettings();
|
|
}
|
|
}
|