Files
flutter_common/lib/upload_image/look_images_widget.dart
2025-11-14 11:35:45 +08:00

263 lines
8.6 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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: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,
String? oSSAccessKeyId,
String? policy,
String? callback,
String? signature,
String? ossDirectory,
String? ossHost,
}) async {
showDialog(
context: Get.context!,
useSafeArea: false,
builder: (_) {
return LookImagesWidget(
listData: listData,
currentPage: currentPage,
oSSAccessKeyId: oSSAccessKeyId,
policy: policy,
callback: callback,
signature: signature,
ossDirectory: ossDirectory,
ossHost: ossHost);
});
}
}
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;
const LookImagesWidget({
super.key,
required this.listData,
this.currentPage,
this.oSSAccessKeyId,
this.policy,
this.callback,
this.signature,
this.ossDirectory,
this.ossHost,
});
@override
State<LookImagesWidget> createState() => _LookImagesWidgetState();
}
class _LookImagesWidgetState extends State<LookImagesWidget> {
List listData = [];
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;
}
@override
void initState() {
listData = widget.listData;
if (widget.currentPage == null) {
initialPage = 0;
currentPage = 0;
} else {
// initialPage = 0;
currentPage = widget.currentPage ?? 0;
}
super.initState();
}
@override
Widget build(BuildContext context) {
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],
),
);
},
),
Positioned(
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 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
Future<File?> uint8ListToTempFile(Uint8List uint8List, {String fileName = "temp_file"}) async {
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 string = 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 ?? '',
);
return tempFile;
} catch (e) {
print("转换临时文件失败:$e");
return null;
}
}
},
child: Icon(Icons.edit, color: Colors.white))),
//图片张数指示器
Positioned(
left: 0,
right: 0,
bottom: 20,
child: Container(
alignment: Alignment.center,
child: Text(
"${currentPage + 1}/${listData.length}",
style: const TextStyle(
color: Colors.white,
fontSize: 16,
decoration: TextDecoration.none,
),
),
),
)
],
),
);
}
}