Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -1,21 +1,43 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_common/upload_image/ossUtil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:photo_view/photo_view.dart';
|
||||
import 'package:image_editor_plus/image_editor_plus.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:photo_view/photo_view_gallery.dart';
|
||||
|
||||
class LookImagesTool {
|
||||
static lookImages({
|
||||
required List<String> listData,
|
||||
int? currentPage,
|
||||
void Function(String)? onCallBack,
|
||||
String? oSSAccessKeyId,
|
||||
Function? callBack,
|
||||
bool? isShowEdit,
|
||||
String? policy,
|
||||
String? callback,
|
||||
String? signature,
|
||||
String? ossDirectory,
|
||||
String? ossHost,
|
||||
}) async {
|
||||
showDialog(
|
||||
context: Get.context!,
|
||||
useSafeArea: false,
|
||||
builder: (_) {
|
||||
return LookImagesWidget(
|
||||
listData: listData,
|
||||
currentPage: currentPage,
|
||||
);
|
||||
listData: listData,
|
||||
currentPage: currentPage,
|
||||
onCallBack: onCallBack,
|
||||
oSSAccessKeyId: oSSAccessKeyId,
|
||||
policy: policy,
|
||||
callback: callback,
|
||||
isShowEdit: isShowEdit,
|
||||
signature: signature,
|
||||
ossDirectory: ossDirectory,
|
||||
ossHost: ossHost);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -23,11 +45,27 @@ class LookImagesTool {
|
||||
class LookImagesWidget extends StatefulWidget {
|
||||
final List<String> listData;
|
||||
final int? currentPage;
|
||||
final String? oSSAccessKeyId;
|
||||
final String? policy;
|
||||
final String? callback;
|
||||
final String? signature;
|
||||
final String? ossDirectory;
|
||||
final String? ossHost;
|
||||
final void Function(String)? onCallBack;
|
||||
final bool? isShowEdit;
|
||||
|
||||
const LookImagesWidget({
|
||||
super.key,
|
||||
required this.listData,
|
||||
this.currentPage,
|
||||
this.oSSAccessKeyId,
|
||||
this.policy,
|
||||
this.callback,
|
||||
this.signature,
|
||||
this.ossDirectory,
|
||||
this.ossHost,
|
||||
this.onCallBack,
|
||||
this.isShowEdit,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -39,6 +77,132 @@ class _LookImagesWidgetState extends State<LookImagesWidget> {
|
||||
late int currentPage;
|
||||
late int initialPage = 0;
|
||||
|
||||
/// Dio 最简版:网络图片转 Uint8List
|
||||
Future<Uint8List?> networkImageToUint8ListWithDio(String imageUrl) async {
|
||||
final dio = Dio(); // 初始化 Dio 实例
|
||||
try {
|
||||
// 发起 GET 请求,响应类型设为字节数组(关键)
|
||||
final response = await dio.get<List<int>>(
|
||||
imageUrl,
|
||||
options: Options(responseType: ResponseType.bytes),
|
||||
);
|
||||
// 响应成功且数据非空时,直接转为 Uint8List
|
||||
return response.statusCode == 200 && response.data != null ? Uint8List.fromList(response.data!) : null;
|
||||
} catch (e) {
|
||||
print('图片转换失败:$e'); // 捕获网络错误、URL 非法等异常
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<Uint8List?> editImage({required String url}) async {
|
||||
Uint8List? imageBytes = await networkImageToUint8ListWithDio(url);
|
||||
ImageEditor.setI18n({
|
||||
'crop': '裁剪',
|
||||
'rotate left': '左旋转',
|
||||
'rotate right': '右旋转',
|
||||
'flip': '水平翻转',
|
||||
'brush': '涂抹',
|
||||
'link': '链接',
|
||||
'save': '保存',
|
||||
'text': '文本',
|
||||
'blur': '模糊',
|
||||
'filter': '滤镜',
|
||||
'size': '大小',
|
||||
'color': '颜色',
|
||||
'background color': '背景颜色',
|
||||
'background opacity': '背景透明度',
|
||||
'reset': '重置',
|
||||
'freeform': '自由裁剪',
|
||||
'remove': '移除',
|
||||
'emoji': '表情',
|
||||
'slider color': '滑块颜色',
|
||||
'color opacity': '透明度',
|
||||
'blur radius': '模糊半径',
|
||||
});
|
||||
if (mounted) {
|
||||
Uint8List? editedImage = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ImageEditor(
|
||||
image: imageBytes,
|
||||
blurOption: null,
|
||||
filtersOption: null,
|
||||
brushOption: null,
|
||||
textOption: null,
|
||||
emojiOption: null,
|
||||
),
|
||||
),
|
||||
);
|
||||
return editedImage;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// 可选:根据字节头自动识别文件后缀(如图片、视频)
|
||||
String getExtension(Uint8List uint8List) {
|
||||
if (uint8List.length < 4) return "bin"; // 无法识别时返回二进制后缀
|
||||
|
||||
// PNG 头:89 50 4E 47
|
||||
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) {
|
||||
return "jpg";
|
||||
}
|
||||
// MP4 头:00 00 00 18 66 74 79 70
|
||||
else if (uint8List.length >= 8 &&
|
||||
uint8List[0] == 0x00 &&
|
||||
uint8List[1] == 0x00 &&
|
||||
uint8List[2] == 0x00 &&
|
||||
uint8List[3] == 0x18 &&
|
||||
uint8List[4] == 0x66 &&
|
||||
uint8List[5] == 0x74 &&
|
||||
uint8List[6] == 0x79 &&
|
||||
uint8List[7] == 0x70) {
|
||||
return "mp4";
|
||||
}
|
||||
// 其他格式可自行扩展(如 GIF、PDF 等)
|
||||
else {
|
||||
return "bin";
|
||||
}
|
||||
}
|
||||
|
||||
/// Uint8List 转临时 File 并且上传到oss并返回访问路径
|
||||
Future<String?> uint8ListToTempFile(Uint8List uint8List, {String fileName = "temp_file"}) async {
|
||||
if (uint8List.isEmpty) return null;
|
||||
try {
|
||||
// 1. 获取临时存储目录(跨平台兼容)
|
||||
Directory tempDir = await getTemporaryDirectory();
|
||||
|
||||
String tempPath = tempDir.path;
|
||||
|
||||
// 2. 拼接文件路径(可自定义后缀,如 .png、.mp4 等)
|
||||
File tempFile = File("$tempPath/$fileName.${getExtension(uint8List)}"); // 自动识别后缀(可选)
|
||||
|
||||
// 3. 将 Uint8List 写入文件
|
||||
await tempFile.writeAsBytes(uint8List);
|
||||
|
||||
// print("临时文件路径:${tempFile.path}");
|
||||
|
||||
String imageUrl = await UploadOss.upload(
|
||||
tempFile.path,
|
||||
fileType: getExtension(uint8List),
|
||||
oSSAccessKeyId: widget.oSSAccessKeyId ?? '',
|
||||
ossHost: widget.ossHost ?? '',
|
||||
ossDirectory: widget.ossDirectory ?? '',
|
||||
policy: widget.policy ?? '',
|
||||
callback: widget.callback ?? '',
|
||||
signature: widget.signature ?? '',
|
||||
);
|
||||
// print("上传后的访问路径:$imageUrl");
|
||||
return imageUrl;
|
||||
} catch (e) {
|
||||
print("转换临时文件失败:$e");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
listData = widget.listData;
|
||||
@@ -46,7 +210,6 @@ class _LookImagesWidgetState extends State<LookImagesWidget> {
|
||||
initialPage = 0;
|
||||
currentPage = 0;
|
||||
} else {
|
||||
// initialPage = 0;
|
||||
currentPage = widget.currentPage ?? 0;
|
||||
}
|
||||
super.initState();
|
||||
@@ -77,6 +240,20 @@ class _LookImagesWidgetState extends State<LookImagesWidget> {
|
||||
left: 15,
|
||||
top: 50,
|
||||
child: GestureDetector(onTap: () => Get.back(), child: 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)))),
|
||||
//图片张数指示器
|
||||
Positioned(
|
||||
left: 0,
|
||||
|
||||
Reference in New Issue
Block a user