feat(update):添加图片长按下载

This commit is contained in:
2026-04-22 11:35:25 +08:00
parent d6f27690c6
commit 74a5bba5f1
8 changed files with 427 additions and 68 deletions

View File

@@ -3,7 +3,9 @@ import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_common/upload_image/down_load_image_tool.dart';
import 'package:flutter_common/upload_image/ossUtil.dart';
import 'package:flutter_common/utils/toast_utils.dart';
import 'package:get/get.dart';
import 'package:image_editor_plus/image_editor_plus.dart';
import 'package:path_provider/path_provider.dart';
@@ -14,6 +16,7 @@ class LookImagesTool {
required List<String> listData,
int? currentPage,
void Function(String)? onCallBack,
void Function(String, int)? onEditCallBack,
String? oSSAccessKeyId,
Function? callBack,
bool? isShowEdit,
@@ -31,6 +34,7 @@ class LookImagesTool {
listData: listData,
currentPage: currentPage,
onCallBack: onCallBack,
onEditCallBack: onEditCallBack,
oSSAccessKeyId: oSSAccessKeyId,
policy: policy,
callback: callback,
@@ -52,6 +56,7 @@ class LookImagesWidget extends StatefulWidget {
final String? ossDirectory;
final String? ossHost;
final void Function(String)? onCallBack;
final void Function(String, int)? onEditCallBack;
final bool? isShowEdit;
const LookImagesWidget({
@@ -65,6 +70,7 @@ class LookImagesWidget extends StatefulWidget {
this.ossDirectory,
this.ossHost,
this.onCallBack,
this.onEditCallBack,
this.isShowEdit,
});
@@ -73,7 +79,7 @@ class LookImagesWidget extends StatefulWidget {
}
class _LookImagesWidgetState extends State<LookImagesWidget> {
List listData = [];
List<String> listData = [];
late int currentPage;
late int initialPage = 0;
@@ -87,9 +93,11 @@ class _LookImagesWidgetState extends State<LookImagesWidget> {
options: Options(responseType: ResponseType.bytes),
);
// 响应成功且数据非空时,直接转为 Uint8List
return response.statusCode == 200 && response.data != null ? Uint8List.fromList(response.data!) : null;
return response.statusCode == 200 && response.data != null
? Uint8List.fromList(response.data!)
: null;
} catch (e) {
print('图片转换失败:$e'); // 捕获网络错误、URL 非法等异常
debugPrint('图片转换失败:$e'); // 捕获网络错误、URL 非法等异常
return null;
}
}
@@ -143,11 +151,16 @@ class _LookImagesWidgetState extends State<LookImagesWidget> {
if (uint8List.length < 4) return "bin"; // 无法识别时返回二进制后缀
// PNG 头89 50 4E 47
if (uint8List[0] == 0x89 && uint8List[1] == 0x50 && uint8List[2] == 0x4E && uint8List[3] == 0x47) {
if (uint8List[0] == 0x89 &&
uint8List[1] == 0x50 &&
uint8List[2] == 0x4E &&
uint8List[3] == 0x47) {
return "png";
}
// JPG 头FF D8 FF
else if (uint8List[0] == 0xFF && uint8List[1] == 0xD8 && uint8List[2] == 0xFF) {
else if (uint8List[0] == 0xFF &&
uint8List[1] == 0xD8 &&
uint8List[2] == 0xFF) {
return "jpg";
}
// MP4 头00 00 00 18 66 74 79 70
@@ -169,7 +182,8 @@ class _LookImagesWidgetState extends State<LookImagesWidget> {
}
/// Uint8List 转临时 File 并且上传到oss并返回访问路径
Future<String?> uint8ListToTempFile(Uint8List uint8List, {String fileName = "temp_file"}) async {
Future<String?> uint8ListToTempFile(Uint8List uint8List,
{String fileName = "temp_file"}) async {
if (uint8List.isEmpty) return null;
try {
// 1. 获取临时存储目录(跨平台兼容)
@@ -178,7 +192,8 @@ class _LookImagesWidgetState extends State<LookImagesWidget> {
String tempPath = tempDir.path;
// 2. 拼接文件路径(可自定义后缀,如 .png、.mp4 等)
File tempFile = File("$tempPath/$fileName.${getExtension(uint8List)}"); // 自动识别后缀(可选)
File tempFile =
File("$tempPath/$fileName.${getExtension(uint8List)}"); // 自动识别后缀(可选)
// 3. 将 Uint8List 写入文件
await tempFile.writeAsBytes(uint8List);
@@ -198,14 +213,29 @@ class _LookImagesWidgetState extends State<LookImagesWidget> {
// print("上传后的访问路径:$imageUrl");
return imageUrl;
} catch (e) {
print("转换临时文件失败:$e");
debugPrint("转换临时文件失败:$e");
return null;
}
}
Future<void> _saveCurrentImage() async {
if (listData.isEmpty) {
ToastUtils.showToast(msg: '暂无可保存的图片');
return;
}
final result = await DownLoadImageTool.savePhoto(
imageUrl: listData[currentPage],
);
if (result != null) {
ToastUtils.showToast(msg: '已保存到相册');
}
}
@override
void initState() {
listData = widget.listData;
listData = List<String>.from(widget.listData);
if (widget.currentPage == null) {
initialPage = 0;
currentPage = 0;
@@ -220,40 +250,63 @@ class _LookImagesWidgetState extends State<LookImagesWidget> {
return Scaffold(
body: Stack(
children: [
PhotoViewGallery.builder(
itemCount: listData.length,
pageController: PageController(initialPage: currentPage),
onPageChanged: (index) {
setState(() {
currentPage = index;
});
},
builder: (_, index) {
return PhotoViewGalleryPageOptions(
imageProvider: NetworkImage(
listData[index],
),
);
},
GestureDetector(
behavior: HitTestBehavior.translucent,
onLongPress: _saveCurrentImage,
child: PhotoViewGallery.builder(
itemCount: listData.length,
pageController: PageController(initialPage: currentPage),
onPageChanged: (index) {
setState(() {
currentPage = index;
});
},
builder: (_, index) {
return PhotoViewGalleryPageOptions(
imageProvider: NetworkImage(
listData[index],
),
);
},
),
),
Positioned(
left: 15,
top: 50,
child: GestureDetector(onTap: () => Get.back(), child: Icon(Icons.arrow_back_ios, color: Colors.white))),
left: 15,
top: 50,
child: GestureDetector(
onTap: () => Get.back(),
child: const Icon(Icons.arrow_back_ios, color: Colors.white),
),
),
Positioned(
right: 15,
top: 50,
child: GestureDetector(
onTap: () async {
Uint8List? imageFile = await editImage(url: listData[currentPage]);
String? url = await uint8ListToTempFile(imageFile ?? Uint8List(0));
if(widget.onCallBack != null){
widget.onCallBack!(url??'');
}
},
child: Visibility(
visible: widget.isShowEdit??false,
child: Icon(Icons.edit, color: Colors.white)))),
right: 15,
top: 50,
child: GestureDetector(
onTap: () async {
Uint8List? imageFile =
await editImage(url: listData[currentPage]);
if (imageFile == null || imageFile.isEmpty) {
return;
}
String? url = await uint8ListToTempFile(imageFile);
if (url == null || url.isEmpty) {
ToastUtils.showToast(msg: '图片上传失败');
return;
}
setState(() {
listData[currentPage] = url;
});
widget.onEditCallBack?.call(url, currentPage);
widget.onCallBack?.call(url);
},
child: Visibility(
visible: widget.isShowEdit ?? false,
child: const Icon(Icons.edit, color: Colors.white),
),
),
),
//图片张数指示器
Positioned(
left: 0,