图文请求
This commit is contained in:
parent
d3113a5e64
commit
87e1b8a18c
BIN
assets/images/20240622145440.png
Normal file
BIN
assets/images/20240622145440.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 979 KiB |
BIN
assets/images/20240622145935.png
Normal file
BIN
assets/images/20240622145935.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 925 KiB |
BIN
assets/images/kuang.png
Normal file
BIN
assets/images/kuang.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
BIN
assets/images/upbg.webp
Normal file
BIN
assets/images/upbg.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 59 KiB |
19
lib/beans/MessageBean.dart
Normal file
19
lib/beans/MessageBean.dart
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'MessageBean.g.dart';
|
||||
|
||||
@JsonSerializable(explicitToJson: true)
|
||||
class MessageBean{
|
||||
|
||||
String? role;
|
||||
String? content;
|
||||
|
||||
|
||||
MessageBean(this.role,this.content,);
|
||||
|
||||
factory MessageBean.fromJson(Map<String, dynamic> json) => _$MessageBeanFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$MessageBeanToJson(this);
|
||||
|
||||
}
|
||||
18
lib/beans/MessageBean.g.dart
Normal file
18
lib/beans/MessageBean.g.dart
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'MessageBean.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
MessageBean _$MessageBeanFromJson(Map<String, dynamic> json) => MessageBean(
|
||||
json['role'] as String?,
|
||||
json['content'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$MessageBeanToJson(MessageBean instance) =>
|
||||
<String, dynamic>{
|
||||
'role': instance.role,
|
||||
'content': instance.content,
|
||||
};
|
||||
237
lib/common/app_util.dart
Normal file
237
lib/common/app_util.dart
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:image_cropper/image_cropper.dart';
|
||||
import 'package:image_gallery_saver/image_gallery_saver.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:mime/mime.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
import '../network/NetworkConfig.dart';
|
||||
|
||||
class AppUtil {
|
||||
/// 默认为下载网络图片,如需下载资源图片,需要指定 [isAsset] 为 `true`。
|
||||
static Future<void> saveImage(String imageUrl, {bool isAsset = false}) async {
|
||||
try {
|
||||
if (imageUrl == null) throw '保存失败,图片不存在!';
|
||||
|
||||
print(NetworkConfig.systemVersion);
|
||||
|
||||
/// 权限检测
|
||||
PermissionStatus storageStatus;
|
||||
|
||||
///android13及以上版本 "storage" 已废除 图片 视频 音频权限需要分别申请
|
||||
if (int.parse(NetworkConfig.systemVersion) >= 13) {
|
||||
storageStatus = await Permission.photos.status;
|
||||
} else {
|
||||
storageStatus = await Permission.storage.status;
|
||||
}
|
||||
|
||||
if (storageStatus != PermissionStatus.granted) {
|
||||
if (int.parse(NetworkConfig.systemVersion) >= 13) {
|
||||
storageStatus = await Permission.photos.request();
|
||||
} else {
|
||||
storageStatus = await Permission.storage.request();
|
||||
}
|
||||
if (storageStatus != PermissionStatus.granted) {
|
||||
throw '无法存储图片,请先授权!';
|
||||
}
|
||||
}
|
||||
|
||||
/// 保存的图片数据
|
||||
Uint8List imageBytes;
|
||||
|
||||
if (isAsset == true) {
|
||||
/// 保存资源图片
|
||||
ByteData bytes = await rootBundle.load(imageUrl);
|
||||
imageBytes = bytes.buffer.asUint8List();
|
||||
} else {
|
||||
Map<String, String> map = {};
|
||||
|
||||
/// 保存网络图片
|
||||
CachedNetworkImage image = CachedNetworkImage(imageUrl: imageUrl);
|
||||
BaseCacheManager manager = image.cacheManager ?? DefaultCacheManager();
|
||||
Map<String, String> headers =
|
||||
image.httpHeaders != null ? image.httpHeaders! : map;
|
||||
File file = await manager.getSingleFile(
|
||||
image.imageUrl,
|
||||
headers: headers,
|
||||
);
|
||||
imageBytes = await file.readAsBytes();
|
||||
}
|
||||
|
||||
/// 保存图片
|
||||
final result =
|
||||
await ImageGallerySaver.saveImage(imageBytes, quality: 100);
|
||||
|
||||
if (result == null || result == '') throw '图片保存失败';
|
||||
|
||||
EasyLoading.showToast("Successfully saved");
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
///保存Widget图片
|
||||
static Future<void> saveWidgetImage(ByteData bytes) async {
|
||||
try {
|
||||
if (bytes == null) throw '保存失败,图片不存在!';
|
||||
|
||||
/// 权限检测
|
||||
PermissionStatus storageStatus;
|
||||
|
||||
///android13及以上版本 "storage" 已废除 图片 视频 音频权限需要分别申请
|
||||
if (int.parse(NetworkConfig.systemVersion) >= 13) {
|
||||
storageStatus = await Permission.photos.status;
|
||||
} else {
|
||||
storageStatus = await Permission.storage.status;
|
||||
}
|
||||
|
||||
if (storageStatus != PermissionStatus.granted) {
|
||||
if (int.parse(NetworkConfig.systemVersion) >= 13) {
|
||||
storageStatus = await Permission.photos.request();
|
||||
} else {
|
||||
storageStatus = await Permission.storage.request();
|
||||
}
|
||||
|
||||
if (storageStatus != PermissionStatus.granted) {
|
||||
throw '无法存储图片,请先授权!';
|
||||
}
|
||||
}
|
||||
|
||||
Uint8List imageBytes = bytes.buffer.asUint8List();
|
||||
|
||||
/// 保存图片
|
||||
final result =
|
||||
await ImageGallerySaver.saveImage(imageBytes, quality: 100);
|
||||
|
||||
if (result == null || result == '') throw '图片保存失败';
|
||||
|
||||
EasyLoading.showToast("Successfully saved");
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
//获取图片
|
||||
static Future<XFile> getImages() async {
|
||||
final pkImage = await ImagePicker().pickImage(
|
||||
source: ImageSource.gallery,
|
||||
);
|
||||
return pkImage!;
|
||||
}
|
||||
|
||||
//裁剪
|
||||
static cropImage(BuildContext context, String path, int index) async {
|
||||
double x = 1.0;
|
||||
double y = 1.0;
|
||||
switch (index) {
|
||||
case 0:
|
||||
x = 1.0;
|
||||
y = 1.0;
|
||||
break;
|
||||
case 1:
|
||||
x = 3.0;
|
||||
y = 4.0;
|
||||
break;
|
||||
case 2:
|
||||
x = 4.0;
|
||||
y = 3.0;
|
||||
break;
|
||||
case 3:
|
||||
x = 9.0;
|
||||
y = 16.0;
|
||||
break;
|
||||
case 4:
|
||||
x = 16.0;
|
||||
y = 9.0;
|
||||
break;
|
||||
}
|
||||
CroppedFile? croppedFile = await ImageCropper().cropImage(
|
||||
maxHeight: 100,
|
||||
sourcePath: path,
|
||||
// aspectRatioPresets: [
|
||||
// CropAspectRatioPreset.square, //1:1
|
||||
// ],
|
||||
//aspectRatio: CropAspectRatio(ratioX: x, ratioY: y),
|
||||
uiSettings: [
|
||||
IOSUiSettings(
|
||||
title: 'Cropper',
|
||||
aspectRatioLockEnabled: true,
|
||||
resetAspectRatioEnabled: false,
|
||||
),
|
||||
AndroidUiSettings(
|
||||
toolbarTitle: 'Cropper',
|
||||
toolbarColor: Color(0xFFBE6FDF),
|
||||
toolbarWidgetColor: Colors.white,
|
||||
activeControlsWidgetColor: Color(0xFFBE6FDF),
|
||||
initAspectRatio: CropAspectRatioPreset.square,
|
||||
lockAspectRatio: true,
|
||||
),
|
||||
WebUiSettings(
|
||||
context: context,
|
||||
viewwMode: WebViewMode.mode_1,
|
||||
cropBoxMovable: false
|
||||
/*customDialogBuilder: (cropper, initCropper, crop, rotate, scale) {
|
||||
return CropperDialog(
|
||||
cropper: cropper,
|
||||
initCropper: initCropper,
|
||||
crop: crop,
|
||||
rotate: rotate,
|
||||
scale: scale,
|
||||
);
|
||||
},*/
|
||||
),
|
||||
],
|
||||
);
|
||||
return croppedFile;
|
||||
}
|
||||
|
||||
//解析图片转base64
|
||||
static uploadImage(XFile pickedFile) async {
|
||||
var file = File(pickedFile.path);
|
||||
String? type = lookupMimeType(pickedFile.path); //读取格式
|
||||
Uint8List imageBytes = await file.readAsBytes();
|
||||
String base64 = base64Encode(imageBytes);
|
||||
return "data:$type;base64,$base64";
|
||||
}
|
||||
|
||||
//解析图片转base64
|
||||
static uploadImage2(File pickedFile) async {
|
||||
String? type = lookupMimeType(pickedFile.path); //读取格式
|
||||
Uint8List imageBytes = await pickedFile.readAsBytes();
|
||||
String base64 = base64Encode(imageBytes);
|
||||
return "data:$type;base64,$base64";
|
||||
}
|
||||
|
||||
// 获取文件大小
|
||||
static String getFileSizeString({int? bytes, int decimals = 0}) {
|
||||
if (bytes! <= 0) return "0 Bytes";
|
||||
const suffixes = [" Bytes", "KB", "MB", "GB", "TB"];
|
||||
var i = (log(bytes) / log(1024)).floor();
|
||||
return ((bytes / pow(1024, i)).toStringAsFixed(decimals)) + suffixes[i];
|
||||
}
|
||||
|
||||
// 获取文件大小(mb)
|
||||
static double getFileSizeDouble({int? bytes, int decimals = 0}) {
|
||||
final kb = bytes! / 1024;
|
||||
final mb = kb / 1024;
|
||||
print("object===$mb");
|
||||
return mb;
|
||||
}
|
||||
|
||||
///网络图片转base64
|
||||
static Future<String?> networkImageToBase64(String imageUrl) async {
|
||||
http.Response response = await http.get(Uri.parse(imageUrl));
|
||||
final bytes = response.bodyBytes;
|
||||
print("bytes===$bytes");
|
||||
return bytes != null ? base64Encode(bytes) : null;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ import 'dart:io';
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:Chat/tools/HomePage.dart';
|
||||
import 'package:Chat/tools/upload/upload_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
|
|
@ -50,6 +51,7 @@ class _ChatAppState extends State<ChatApp> {
|
|||
//注册路由
|
||||
routes: <String, WidgetBuilder>{
|
||||
'/HomePage': (BuildContext context) => const Homepage(),
|
||||
'/UploadPage': (BuildContext context) => const UploadPage(),
|
||||
},
|
||||
debugShowMaterialGrid: false,
|
||||
//显示网格
|
||||
|
|
|
|||
|
|
@ -11,11 +11,17 @@ class BaseEntity {
|
|||
|
||||
// 数据解析
|
||||
factory BaseEntity.fromJson(json) {
|
||||
dynamic data = json["content"][0]["text"];
|
||||
return BaseEntity(code: 0,data: data);
|
||||
}
|
||||
|
||||
|
||||
// 数据解析
|
||||
factory BaseEntity.PlayfromComfyUi(json) {
|
||||
Map<String, dynamic> responseData = json;
|
||||
|
||||
|
||||
dynamic data = json["choices"][0]["message"]["content"];
|
||||
return BaseEntity(data: data);
|
||||
String message = responseData["type"]; //错误描述
|
||||
dynamic data = responseData["name"];
|
||||
return BaseEntity(code: 0, result: 0, message: message, data: data);
|
||||
}
|
||||
|
||||
// 数据解析
|
||||
|
|
|
|||
|
|
@ -9,13 +9,13 @@ class NetworkConfig {
|
|||
static int SELECT_INDEX = 0;
|
||||
|
||||
static List BASE_URLS = [
|
||||
"http://117.50.182.144:5000",
|
||||
"http://117.50.182.144:5000",
|
||||
"http://117.50.182.144:5000",
|
||||
"http://127.0.0.1:5000",
|
||||
"http://127.0.0.1:5000",
|
||||
"http://127.0.0.1:5000",
|
||||
];
|
||||
|
||||
static List BASE_URLS_AI = [
|
||||
"http://117.50.182.144:5000",
|
||||
"http://127.0.0.1:5000",
|
||||
];
|
||||
|
||||
|
||||
|
|
@ -37,4 +37,8 @@ class NetworkConfig {
|
|||
|
||||
static const String chat = "/v1/chat/completions"; //聊天
|
||||
|
||||
static const String uploadImg = "/upload/image"; //审核模式上传图片
|
||||
|
||||
static const String prompt = "/prompt"; //审核模式上传图片
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
|
|
@ -27,14 +29,36 @@ class RequestCenter {
|
|||
|
||||
Dio? _dio, _dioLog;
|
||||
|
||||
final dio = Dio();
|
||||
|
||||
void setupDio() {
|
||||
dio.options.baseUrl = 'https://api.gptsapi.net/v1';
|
||||
dio.interceptors.add(InterceptorsWrapper(
|
||||
onRequest: (options, handler) {
|
||||
// 可以在这里添加其他请求配置
|
||||
return handler.next(options);
|
||||
},
|
||||
onResponse: (response, handler) {
|
||||
// 处理响应
|
||||
return handler.next(response);
|
||||
},
|
||||
onError: (DioError e, handler) {
|
||||
// 处理错误
|
||||
return handler.next(e);
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
RequestCenter._internal() {
|
||||
setup();
|
||||
setupDio();
|
||||
}
|
||||
|
||||
static RequestCenter _getInstance() {
|
||||
_instance ??= RequestCenter._internal();
|
||||
//域名不一致,重新初始化
|
||||
if (_instance!._dio != null && NetworkConfig.ServerDomain_Online != _instance!._dio!.options.baseUrl) {
|
||||
if (_instance!._dio != null &&
|
||||
NetworkConfig.ServerDomain_Online != _instance!._dio!.options.baseUrl) {
|
||||
_instance!._dio!.options.baseUrl = NetworkConfig.ServerDomain_Online;
|
||||
}
|
||||
return _instance!;
|
||||
|
|
@ -50,7 +74,7 @@ class RequestCenter {
|
|||
connectTimeout: const Duration(seconds: 20),
|
||||
contentType: Headers.jsonContentType,
|
||||
responseType: ResponseType.json));
|
||||
_dio!.interceptors.add(DioLogInterceptor());
|
||||
//_dio!.interceptors.add(DioLogInterceptor());
|
||||
/* _dio.interceptors.add(new ResponseInterceptors());*/
|
||||
_dioLog = Dio(BaseOptions(
|
||||
sendTimeout: const Duration(seconds: 10),
|
||||
|
|
@ -58,14 +82,64 @@ class RequestCenter {
|
|||
connectTimeout: const Duration(seconds: 20),
|
||||
contentType: Headers.jsonContentType,
|
||||
responseType: ResponseType.json));
|
||||
_dioLog!.interceptors.add(DioLogInterceptor());
|
||||
|
||||
|
||||
//_dioLog!.interceptors.add(DioLogInterceptor());
|
||||
}
|
||||
}
|
||||
|
||||
// 网络请求默认为post
|
||||
Future<BaseEntity?> request(
|
||||
path,
|
||||
Map<Object, dynamic> parmeters,
|
||||
Function(BaseEntity dataEntity) success,
|
||||
Function(ErrorEntity errorEntity) error,
|
||||
{RequestMethod? method}) async {
|
||||
try {
|
||||
//FormData formData = FormData.fromMap(parmeters);
|
||||
Response response = await _dio!.post(path, data: parmeters);
|
||||
BaseEntity entity = BaseEntity.fromJson(response.data);
|
||||
success(entity);
|
||||
return entity;
|
||||
} catch (e) {
|
||||
error(ErrorEntity(code: -1, message: "Network Anomaly"));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<BaseEntity?> sendRequest(
|
||||
Map<Object, dynamic> parmeters,
|
||||
Function(BaseEntity dataEntity) success,
|
||||
Function(ErrorEntity errorEntity) error,
|
||||
) async {
|
||||
try {
|
||||
print('Request: $parmeters');
|
||||
final response = await dio.post('/messages',
|
||||
data: parmeters,
|
||||
options: Options(
|
||||
contentType: Headers.jsonContentType,
|
||||
responseType: ResponseType.json,
|
||||
headers: {
|
||||
'x-api-key':
|
||||
'sk-V6d51cc6aa28906caecb7f22803d92ae3f18cfeb799nh4mc',
|
||||
'anthropic-version': "2023-06-01"
|
||||
},
|
||||
));
|
||||
print('Response: ${response.data}');
|
||||
BaseEntity entity = BaseEntity.fromJson(response.data);
|
||||
success(entity);
|
||||
return entity;
|
||||
} on DioError catch (e) {
|
||||
print('Error: ${e.response?.data}');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//特殊处理网络请求默认为post
|
||||
Future<BaseEntity?> request1(path, Map<String, dynamic> parmeters, Function(BaseEntity dataEntity) success, Function(ErrorEntity errorEntity) error,
|
||||
Future<BaseEntity?> request1(
|
||||
path,
|
||||
Map<String, dynamic> parmeters,
|
||||
Function(BaseEntity dataEntity) success,
|
||||
Function(ErrorEntity errorEntity) error,
|
||||
{RequestMethod? method}) async {
|
||||
Map<String, dynamic> headers = {
|
||||
"AppId": NetworkConfig.AppId,
|
||||
|
|
@ -94,7 +168,10 @@ class RequestCenter {
|
|||
}
|
||||
|
||||
Future<BaseEntity?> requestLog(
|
||||
path, Map<String, dynamic> parmeters, Function(BaseEntity dataEntity) success, Function(ErrorEntity errorEntity) error,
|
||||
path,
|
||||
Map<String, dynamic> parmeters,
|
||||
Function(BaseEntity dataEntity) success,
|
||||
Function(ErrorEntity errorEntity) error,
|
||||
{RequestMethod? method}) async {
|
||||
Map<String, dynamic> headers = {
|
||||
"AppId": NetworkConfig.AppId,
|
||||
|
|
@ -123,31 +200,12 @@ class RequestCenter {
|
|||
}
|
||||
}
|
||||
|
||||
// 网络请求默认为post
|
||||
Future<BaseEntity?> request(path, Object parmeters, Function(BaseEntity dataEntity) success, Function(ErrorEntity errorEntity) error,
|
||||
{RequestMethod? method}) async {
|
||||
try {
|
||||
//FormData formData = FormData.fromMap(parmeters);
|
||||
Response response = await _dio!.post(path, data: parmeters);
|
||||
if (response.statusCode == 200) {
|
||||
BaseEntity entity = BaseEntity.fromJson(response.data);
|
||||
success(entity);
|
||||
return entity;
|
||||
} else {
|
||||
error(ErrorEntity(code: -1, message: "Network Anomaly"));
|
||||
return null;
|
||||
}
|
||||
} catch (e) {
|
||||
error(ErrorEntity(code: -1, message: "Network Anomaly"));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 网络请求默认为post
|
||||
Future<BaseEntity?> requestPay(
|
||||
path, Map<String, dynamic> parmeters, Function(BaseEntity dataEntity) success, Function(ErrorEntity errorEntity) error,
|
||||
path,
|
||||
Map<String, dynamic> parmeters,
|
||||
Function(BaseEntity dataEntity) success,
|
||||
Function(ErrorEntity errorEntity) error,
|
||||
{RequestMethod? method}) async {
|
||||
Map<String, dynamic> headers = {
|
||||
"AppId": NetworkConfig.AppId,
|
||||
|
|
@ -176,41 +234,67 @@ class RequestCenter {
|
|||
}
|
||||
|
||||
// 网络请求默认为post
|
||||
Future<BaseEntity?> requestPlay(
|
||||
path, Map<String, dynamic> parmeters, Function(BaseEntity dataEntity) success, Function(ErrorEntity errorEntity) error,
|
||||
Future<BaseEntity?> requestComfyUI1(
|
||||
formData,
|
||||
Function(BaseEntity dataEntity) success,
|
||||
Function(ErrorEntity errorEntity) error,
|
||||
{RequestMethod? method}) async {
|
||||
Map<String, dynamic> headers = {
|
||||
"AppId": NetworkConfig.AppId,
|
||||
/*"BossId": NetworkConfig.BossId,*/
|
||||
"UserId": NetworkConfig.userId,
|
||||
"Version": NetworkConfig.Version,
|
||||
"Language": NetworkConfig.Language
|
||||
};
|
||||
parmeters.addAll(headers);
|
||||
Map<String, dynamic> parmetersSign = sign(parmeters);
|
||||
try {
|
||||
FormData formData = FormData.fromMap(parmetersSign);
|
||||
print("Authorization==${NetworkConfig.token}");
|
||||
//用户验签
|
||||
_dio!.options.headers = {"Authorization": NetworkConfig.token};
|
||||
Response response = await _dio!.post(path, data: formData);
|
||||
if (response != null && response.statusCode == 200) {
|
||||
BaseEntity entity = BaseEntity.PlayfromJson(response.data);
|
||||
var response = await _dio!.post(
|
||||
"http://192.168.1.103:8188${NetworkConfig.uploadImg}",
|
||||
data: formData,
|
||||
options: Options(
|
||||
contentType: Headers.jsonContentType,
|
||||
responseType: ResponseType.json,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
),
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
BaseEntity entity = BaseEntity.PlayfromComfyUi(response.data);
|
||||
success(entity);
|
||||
return entity;
|
||||
} else {
|
||||
error(ErrorEntity(code: -1, message: "Network Anomaly"));
|
||||
return null;
|
||||
}
|
||||
} catch (e) {
|
||||
error(ErrorEntity(code: -1, message: "Network Anomaly"));
|
||||
print("e" + e.toString());
|
||||
//error(ErrorEntity(code: -1, message: "Network Anomaly"));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<BaseEntity?> requestComfyUI2(
|
||||
formData,
|
||||
Function(BaseEntity dataEntity) success,
|
||||
Function(ErrorEntity errorEntity) error,
|
||||
{RequestMethod? method}) async {
|
||||
try {
|
||||
var response = await _dio!.post(
|
||||
"http://192.168.1.103:8188${NetworkConfig.prompt}",
|
||||
data: formData,
|
||||
options: Options(
|
||||
contentType: Headers.jsonContentType,
|
||||
responseType: ResponseType.json,
|
||||
),
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
BaseEntity entity = BaseEntity.PlayfromComfyUi(response.data);
|
||||
success(entity);
|
||||
return entity;
|
||||
}
|
||||
} catch (e) {
|
||||
print("e" + e.toString());
|
||||
//error(ErrorEntity(code: -1, message: "Network Anomaly"));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 网络请求默认为post (网页)
|
||||
Future<BaseEntity?> requestWeb(
|
||||
path, Map<String, dynamic> parmeters, Function(BaseEntity dataEntity) success, Function(ErrorEntity errorEntity) error,
|
||||
path,
|
||||
Map<String, dynamic> parmeters,
|
||||
Function(BaseEntity dataEntity) success,
|
||||
Function(ErrorEntity errorEntity) error,
|
||||
{RequestMethod? method}) async {
|
||||
Map<String, dynamic> headers = {
|
||||
"AppId": NetworkConfig.AppId,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,10 +1,14 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_client_sse/constants/sse_request_type_enum.dart';
|
||||
import 'package:flutter_client_sse/flutter_client_sse.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
|
||||
import 'HomeModel.dart';
|
||||
import 'Message.dart';
|
||||
import '../beans/MessageBean.dart';
|
||||
|
||||
class Homepage extends StatefulWidget {
|
||||
const Homepage({super.key});
|
||||
|
|
@ -20,11 +24,16 @@ class _HomepageState extends State<Homepage> {
|
|||
final ScrollController _scrollController = ScrollController();
|
||||
String text = "";
|
||||
String data = '';
|
||||
List<Message> list = [];
|
||||
List<MessageBean> list = [];
|
||||
int created = 0;
|
||||
String msg = "";
|
||||
|
||||
Future<void> chat() async {
|
||||
EasyLoading.show(status: 'loading...');
|
||||
viewModel.chat(text, 'Assistant');
|
||||
List<Map<String, dynamic>> jsonList =
|
||||
list.map((user) => user.toJson()).toList();
|
||||
// String jsonList = jsonEncode(list.map((user) => user.toJson()).toList());
|
||||
viewModel.chat(jsonList, 'assistant');
|
||||
}
|
||||
|
||||
void _textFieldChanged(String str) {
|
||||
|
|
@ -35,6 +44,27 @@ class _HomepageState extends State<Homepage> {
|
|||
void initState() {
|
||||
// TODO: implement initState
|
||||
super.initState();
|
||||
MessageBean a = MessageBean("user", "Start a new Chat");
|
||||
MessageBean b = MessageBean("assistant", "这天小猫娘在家里玩耍,主人在沙发上玩手机,小猫娘忽然对主人看的内容很感兴趣,也想要看看,就跑到主人面前一脸希翼的看着主人");
|
||||
list.add(a);
|
||||
list.add(b);
|
||||
// SSEClient.subscribeToSSE(
|
||||
// method: SSERequestType.GET,
|
||||
// url:
|
||||
// 'http://127.0.0.1:5000/v1/chat/completions',
|
||||
// header: {
|
||||
// "Cookie":
|
||||
// 'jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3QiLCJpYXQiOjE2NDMyMTAyMzEsImV4cCI6MTY0MzgxNTAzMX0.U0aCAM2fKE1OVnGFbgAU_UVBvNwOMMquvPY8QaLD138; Path=/; Expires=Wed, 02 Feb 2022 15:17:11 GMT; HttpOnly; SameSite=Strict',
|
||||
// "Accept": "text/event-stream",
|
||||
// "Cache-Control": "no-cache",
|
||||
// }).listen(
|
||||
// (event) {
|
||||
// print('Id: ' + event.id!);
|
||||
// print('Event: ' + event.event!);
|
||||
// print('Data: ' + event.data!);
|
||||
// },
|
||||
// );
|
||||
|
||||
// 监听输入变化
|
||||
_chatController.addListener(() {
|
||||
print(_chatController.text);
|
||||
|
|
@ -46,10 +76,11 @@ class _HomepageState extends State<Homepage> {
|
|||
if (code == "chat") {
|
||||
//有数据
|
||||
data = newData['data'];
|
||||
list.add(Message(data, false));
|
||||
list.add(MessageBean("assistant", data));
|
||||
setState(() {});
|
||||
Future.delayed(Duration(milliseconds: 500), () {
|
||||
_scrollController.jumpTo(_scrollController.position.maxScrollExtent);
|
||||
Future.delayed(const Duration(milliseconds: 500), () {
|
||||
_scrollController
|
||||
.jumpTo(_scrollController.position.maxScrollExtent);
|
||||
});
|
||||
//EasyLoading.showToast(data);
|
||||
print("data" + data.toString());
|
||||
|
|
@ -75,11 +106,31 @@ class _HomepageState extends State<Homepage> {
|
|||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
"王梦阳",
|
||||
"家辉",
|
||||
style: TextStyle(fontSize: 18),
|
||||
),
|
||||
backgroundColor: Colors.white,
|
||||
centerTitle: true,
|
||||
actions: <Widget>[
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.pushNamed(context, "/UploadPage");
|
||||
},
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(right: 20),
|
||||
width: 70,
|
||||
height: 50,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFF006CFF),
|
||||
borderRadius: BorderRadius.all(Radius.circular(8.0))),
|
||||
child: Text(
|
||||
"生图",
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
body: Container(
|
||||
child: Column(
|
||||
|
|
@ -123,20 +174,22 @@ class _HomepageState extends State<Homepage> {
|
|||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
chat();
|
||||
list.add(Message(text, true));
|
||||
list.add(MessageBean("user", text));
|
||||
_chatController.clear();
|
||||
Future.delayed(Duration(milliseconds: 200), () {
|
||||
_scrollController.jumpTo(_scrollController.position.maxScrollExtent);
|
||||
_scrollController
|
||||
.jumpTo(_scrollController.position.maxScrollExtent);
|
||||
});
|
||||
setState(() {});
|
||||
chat();
|
||||
},
|
||||
child: Container(
|
||||
width: 70,
|
||||
height: 50,
|
||||
alignment: Alignment.center,
|
||||
decoration:
|
||||
BoxDecoration(color: Color(0xFF006CFF), borderRadius: BorderRadius.all(Radius.circular(8.0))),
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFF006CFF),
|
||||
borderRadius: BorderRadius.all(Radius.circular(8.0))),
|
||||
child: Text(
|
||||
"发送",
|
||||
style: TextStyle(color: Colors.white),
|
||||
|
|
@ -153,45 +206,62 @@ class _HomepageState extends State<Homepage> {
|
|||
}
|
||||
|
||||
_item(index) {
|
||||
if(index == 0){
|
||||
return Container();
|
||||
}
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: !list[index].isMe
|
||||
? Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
child: list[index].role != 'user'
|
||||
? Column(
|
||||
children: [
|
||||
Container(
|
||||
width: 45,
|
||||
height: 45,
|
||||
margin: EdgeInsets.only(left: 16, top: 4),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: Image(
|
||||
fit: BoxFit.cover,
|
||||
image: AssetImage('assets/images/avatar2.png'),
|
||||
),
|
||||
),
|
||||
),
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery.of(context).size.width - 150, // 确保不超过屏幕宽度
|
||||
),
|
||||
child: Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 16.0, vertical: 4.0),
|
||||
padding: EdgeInsets.all(12.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFECECEC),
|
||||
borderRadius: BorderRadius.circular(16.0),
|
||||
),
|
||||
child: Text(
|
||||
list[index].text,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.black,
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: 45,
|
||||
height: 45,
|
||||
margin: EdgeInsets.only(left: 16, top: 4),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: Image(
|
||||
fit: BoxFit.cover,
|
||||
image: AssetImage('assets/images/20240622145440.png'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery.of(context).size.width -
|
||||
150, // 确保不超过屏幕宽度
|
||||
),
|
||||
child: Container(
|
||||
margin: EdgeInsets.symmetric(
|
||||
horizontal: 16.0, vertical: 4.0),
|
||||
padding: EdgeInsets.all(12.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFECECEC),
|
||||
borderRadius: BorderRadius.circular(16.0),
|
||||
),
|
||||
child: Text(
|
||||
list[index].content!,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
index == 1 ?
|
||||
Container(
|
||||
child: const Image(
|
||||
height: 200,
|
||||
fit: BoxFit.cover,
|
||||
image: AssetImage('assets/images/20240622145935.png'),
|
||||
),
|
||||
):Container(),
|
||||
],
|
||||
)
|
||||
: Row(
|
||||
|
|
@ -200,17 +270,19 @@ class _HomepageState extends State<Homepage> {
|
|||
children: [
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery.of(context).size.width - 150, // 确保不超过屏幕宽度
|
||||
maxWidth:
|
||||
MediaQuery.of(context).size.width - 150, // 确保不超过屏幕宽度
|
||||
),
|
||||
child: Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 16.0, vertical: 4.0),
|
||||
margin:
|
||||
EdgeInsets.symmetric(horizontal: 16.0, vertical: 4.0),
|
||||
padding: EdgeInsets.all(12.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFF006CFF),
|
||||
borderRadius: BorderRadius.circular(16.0),
|
||||
),
|
||||
child: Text(
|
||||
list[index].text,
|
||||
list[index].content!,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.white,
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
class Message{
|
||||
final String text;
|
||||
final bool isMe;
|
||||
|
||||
Message(this.text, this.isMe);
|
||||
}
|
||||
48
lib/tools/upload/CropperDialog.dart
Normal file
48
lib/tools/upload/CropperDialog.dart
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CropperDialog extends StatefulWidget {
|
||||
var cropper;
|
||||
var initCropper;
|
||||
var crop;
|
||||
var rotate;
|
||||
var scale;
|
||||
|
||||
CropperDialog(
|
||||
{this.cropper, this.initCropper, this.crop, this.rotate, this.scale});
|
||||
|
||||
@override
|
||||
_CropperDialogState createState() => _CropperDialogState();
|
||||
}
|
||||
|
||||
class _CropperDialogState extends State<CropperDialog> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
/// IMPORTANT: must to call this function
|
||||
widget.initCropper();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Dialog(
|
||||
child: SizedBox(
|
||||
width: 500,
|
||||
height: 500,
|
||||
child: Column(children: [
|
||||
SizedBox(width: 400, height: 400, child: widget.cropper),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
/// IMPORTANT: to call crop() function and return
|
||||
/// result data to plugin, for example:
|
||||
final result = await widget.crop();
|
||||
Navigator.of(context).pop(result);
|
||||
},
|
||||
child: Text('Crop'),
|
||||
)
|
||||
]),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
494
lib/tools/upload/upload_page.dart
Normal file
494
lib/tools/upload/upload_page.dart
Normal file
|
|
@ -0,0 +1,494 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:flutter_native_image/flutter_native_image.dart';
|
||||
import 'package:image_cropper/image_cropper.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:web_socket_channel/web_socket_channel.dart';
|
||||
|
||||
import '../../common/app_util.dart';
|
||||
import '../../network/NetworkConfig.dart';
|
||||
import '../HomeModel.dart';
|
||||
import 'CropperDialog.dart';
|
||||
|
||||
///上传
|
||||
class UploadPage extends StatefulWidget {
|
||||
const UploadPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<UploadPage> createState() => _MePageState();
|
||||
}
|
||||
|
||||
class _MePageState extends State<UploadPage>
|
||||
with SingleTickerProviderStateMixin {
|
||||
StreamSubscription? subscription;
|
||||
final HomeModel viewModel = HomeModel();
|
||||
CroppedFile? selectImage; //选择的相册图
|
||||
int currentSizeIndex = 0; //当前尺寸下标
|
||||
String initImg = ""; //参考图
|
||||
var imageValue = false;
|
||||
|
||||
String outputImg = "";
|
||||
|
||||
Future<void> _createPic() async {
|
||||
EasyLoading.show(status: 'loading...');
|
||||
|
||||
String fileName = 'image.png';
|
||||
FormData formData = FormData.fromMap({
|
||||
'image': MultipartFile.fromBytes(_imageBytes!, filename: fileName),
|
||||
});
|
||||
|
||||
viewModel.uploadImg(formData);
|
||||
}
|
||||
|
||||
late WebSocketChannel channel;
|
||||
bool isConnected = false;
|
||||
|
||||
void connectToWebSocket(String serverAddress, String clientId) async {
|
||||
final url = 'ws://$serverAddress/ws?clientId=$clientId';
|
||||
|
||||
Uri uri = Uri.parse(url); // 注意这里我们没有添加查询参数
|
||||
WebSocketChannel channel = WebSocketChannel.connect(uri);
|
||||
channel.stream.listen((message) {
|
||||
Map<String, dynamic> responseData = jsonDecode(message);
|
||||
// 处理接收到的消息
|
||||
if (responseData["type"] != 'crystools.monitor') {
|
||||
print('Received: $message');
|
||||
if (responseData["type"] == 'executed' &&
|
||||
responseData["data"]['node'] == '384') {
|
||||
print("responseData: " +
|
||||
responseData["data"]['output']['images'][0]['filename']);
|
||||
|
||||
outputImg = "http://192.168.1.103:8188/view?filename=" +
|
||||
responseData["data"]['output']['images'][0]['filename'] +
|
||||
"&type=output";
|
||||
setState(() {
|
||||
EasyLoading.dismiss();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 如果我们还没有标记为已连接,现在标记为已连接
|
||||
}, onError: (error) {
|
||||
// 处理错误
|
||||
print('WebSocket error: $error');
|
||||
isConnected = false; // 连接已关闭或出错
|
||||
}, onDone: () {
|
||||
// 连接关闭时的处理
|
||||
print('WebSocket closed');
|
||||
isConnected = false; // 连接已关闭
|
||||
}, cancelOnError: true);
|
||||
}
|
||||
|
||||
// 发送 clientId 的函数
|
||||
/*void sendClientId(WebSocketChannel channel, String clientId) {
|
||||
if (isConnected) {
|
||||
// 如果 sink 已经被取消(例如,由于连接关闭),则不发送消息
|
||||
return;
|
||||
}
|
||||
String jsonMessage = jsonEncode({'client_id': clientId});
|
||||
channel.sink.add(utf8.encode(jsonMessage)); // 使用 utf8 编码发送字符串
|
||||
}*/
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
// TODO: implement initState
|
||||
super.initState();
|
||||
String serverAddress = '192.168.1.103:8188'; // 假设这是你的服务器地址
|
||||
String clientId = 'zhangzhan8228'; // 假设这是你的客户端 ID
|
||||
connectToWebSocket(serverAddress, clientId);
|
||||
//_channel = WebSocketChannel.connect(Uri.parse('ws://your-comfyui-url'));
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// TODO: implement dispose
|
||||
subscription?.cancel();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
//裁剪
|
||||
cropImage(String path, int index) async {
|
||||
|
||||
CroppedFile? croppedFile = await ImageCropper().cropImage(
|
||||
sourcePath: path,
|
||||
// aspectRatioPresets: [
|
||||
// CropAspectRatioPreset.square, //1:1
|
||||
// ],
|
||||
//aspectRatio: CropAspectRatio(ratioX: x, ratioY: y),
|
||||
|
||||
uiSettings: [
|
||||
IOSUiSettings(
|
||||
title: 'Cropper',
|
||||
aspectRatioLockEnabled: true,
|
||||
resetAspectRatioEnabled: false,
|
||||
),
|
||||
AndroidUiSettings(
|
||||
toolbarTitle: 'Cropper',
|
||||
toolbarColor: Color(0xFFBE6FDF),
|
||||
toolbarWidgetColor: Colors.white,
|
||||
activeControlsWidgetColor: Color(0xFFBE6FDF),
|
||||
initAspectRatio: CropAspectRatioPreset.square,
|
||||
lockAspectRatio: true,
|
||||
),
|
||||
WebUiSettings(
|
||||
context: context,
|
||||
viewwMode: WebViewMode.mode_1,
|
||||
cropBoxMovable: false,
|
||||
customDialogBuilder: (cropper, initCropper, crop, rotate, scale) {
|
||||
return CropperDialog(
|
||||
cropper: cropper,
|
||||
initCropper: initCropper,
|
||||
crop: crop,
|
||||
rotate: rotate,
|
||||
scale: scale,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
return croppedFile;
|
||||
}
|
||||
|
||||
Uint8List? _imageBytes;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
body: Stack(
|
||||
children: [
|
||||
const Image(
|
||||
width: double.infinity,
|
||||
fit: BoxFit.fitWidth,
|
||||
image: AssetImage('assets/images/upbg.webp'),
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
XFile img = await AppUtil.getImages();
|
||||
selectImage = await cropImage(img.path, currentSizeIndex);
|
||||
|
||||
final Response<List<int>> response =
|
||||
await Dio().get<List<int>>(
|
||||
selectImage!.path,
|
||||
options: Options(
|
||||
responseType: ResponseType.bytes), // 指定响应类型为字节
|
||||
);
|
||||
_imageBytes = Uint8List.fromList(response.data!);
|
||||
|
||||
setState(() {
|
||||
//_imageBytes = selectImage!.readAsBytes() as Uint8List?;
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
width: 300,
|
||||
height: 300,
|
||||
margin:
|
||||
const EdgeInsets.only(top: 100, left: 60, right: 60),
|
||||
child: selectImage != null
|
||||
? SizedBox(
|
||||
width: 300.0, // 设置宽度
|
||||
height: 300,
|
||||
child: Image.network(outputImg != ""
|
||||
? outputImg
|
||||
: selectImage!
|
||||
.path) /*Image.file(
|
||||
File(selectImage!.path),
|
||||
fit: BoxFit.cover, // 使用 BoxFit.cover 以适应容器的大小
|
||||
)*/
|
||||
)
|
||||
: const Image(
|
||||
fit: BoxFit.fitWidth,
|
||||
image: AssetImage('assets/images/kuang.png'),
|
||||
),
|
||||
)),
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 20, left: 20, right: 20),
|
||||
child: Text(
|
||||
'开始图生图',
|
||||
style:
|
||||
const TextStyle(color: Color(0xFF000000), fontSize: 14),
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
_generateImages();
|
||||
},
|
||||
child: Container(
|
||||
height: 50,
|
||||
margin: const EdgeInsets.only(top: 20, left: 50, right: 50),
|
||||
width: double.infinity,
|
||||
alignment: Alignment.center,
|
||||
decoration: const BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.centerLeft, //渐变开始于上面的中间开始
|
||||
end: Alignment.centerRight, //渐变结束于下面的中间
|
||||
colors: [Color(0xFF808EEF), Color(0xFFBE6FDF)]),
|
||||
borderRadius: BorderRadius.all(Radius.circular(25))),
|
||||
child: Text(
|
||||
'确认提交',
|
||||
style: const TextStyle(
|
||||
color: Color(0xFFFFFFFF), fontSize: 18),
|
||||
),
|
||||
))
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
///生成图片
|
||||
_generateImages() async {
|
||||
//if (NetworkConfig.userId != "") {
|
||||
if (selectImage != null) {
|
||||
//处理图片
|
||||
//initImg = await _compressPictures();
|
||||
|
||||
// File file = File(selectImage!.path);
|
||||
// ByteData data = await rootBundle.load(file.path);
|
||||
// Uint8List imageBytes = data.buffer.asUint8List();
|
||||
// initImg = base64Encode(imageBytes);
|
||||
//initImg = await AppUtil.uploadImage2(file);
|
||||
|
||||
// final Response<List<int>> response = await Dio().get<List<int>>(
|
||||
// selectImage!.path,
|
||||
// options: Options(responseType: ResponseType.bytes), // 指定响应类型为字节
|
||||
// );
|
||||
// Uint8List imageBytes = Uint8List.fromList(response.data!);
|
||||
//initImg = base64Encode(imageBytes);
|
||||
|
||||
///图生图
|
||||
_createPic();
|
||||
}
|
||||
/*} else {
|
||||
showLoginDialog();
|
||||
}*/
|
||||
}
|
||||
|
||||
//处理图片
|
||||
_compressPictures() async {
|
||||
String img = "";
|
||||
//1,取方向,判断原图方向横竖
|
||||
// 2,同方向取宽高中最大值,匹配已选择的画面比例宽高值。反方向小值
|
||||
|
||||
//1:1 512*512 3:4 512*704 4:3 704*512 9:16 512*910 16:9 910*512
|
||||
File file = File(selectImage!.path);
|
||||
File? compressedFile;
|
||||
// ImageProperties properties =
|
||||
// await FlutterNativeImage.getImageProperties(file.path);
|
||||
//判断当前方向
|
||||
/*int isDirection = 0; //0 正方形 1 横向长方形 2 纵向长方形
|
||||
if (properties.width == properties.height) {
|
||||
isDirection = 0;
|
||||
} else if (properties.width! > properties.height!) {
|
||||
isDirection = 1;
|
||||
} else if (properties.width! < properties.height!) {
|
||||
isDirection = 2;
|
||||
}
|
||||
//下标选择方向
|
||||
if (currentSizeIndex == 0) {
|
||||
if (isDirection == 0) {
|
||||
//如果大于512的正方形直接换比例成512,否则使用原图
|
||||
if (properties.width! > 512) {
|
||||
compressedFile = await FlutterNativeImage.compressImage(file.path,
|
||||
quality: 100, targetWidth: 512, targetHeight: 512);
|
||||
} else {
|
||||
compressedFile = file;
|
||||
}
|
||||
} else if (isDirection == 1) {
|
||||
if (properties.width! > 512) {
|
||||
compressedFile = await FlutterNativeImage.compressImage(file.path,
|
||||
quality: 100,
|
||||
targetWidth: 512,
|
||||
targetHeight:
|
||||
(properties.height! * 512 / properties.width!).round());
|
||||
} else {
|
||||
compressedFile = file;
|
||||
}
|
||||
} else if (isDirection == 2) {
|
||||
if (properties.height! > 512) {
|
||||
compressedFile = await FlutterNativeImage.compressImage(file.path,
|
||||
quality: 100,
|
||||
targetWidth:
|
||||
(properties.width! * 512 / properties.height!).round(),
|
||||
targetHeight: 512);
|
||||
} else {
|
||||
compressedFile = file;
|
||||
}
|
||||
}
|
||||
} else if (currentSizeIndex == 1) {
|
||||
//下标方向为纵向
|
||||
if (isDirection == 0) {
|
||||
//如果大于512的正方形直接换比例成512,否则使用原图
|
||||
if (properties.width! > 512) {
|
||||
compressedFile = await FlutterNativeImage.compressImage(file.path,
|
||||
quality: 100, targetWidth: 512, targetHeight: 512);
|
||||
} else {
|
||||
compressedFile = file;
|
||||
}
|
||||
} else if (isDirection == 1) {
|
||||
//横向
|
||||
if (properties.width! > 512) {
|
||||
compressedFile = await FlutterNativeImage.compressImage(file.path,
|
||||
quality: 100,
|
||||
targetWidth: 512,
|
||||
targetHeight:
|
||||
(properties.height! * 512 / properties.width!).round());
|
||||
} else {
|
||||
compressedFile = file;
|
||||
}
|
||||
} else if (isDirection == 2) {
|
||||
//纵向
|
||||
if (properties.height! > 704) {
|
||||
compressedFile = await FlutterNativeImage.compressImage(file.path,
|
||||
quality: 100,
|
||||
targetWidth:
|
||||
(properties.width! * 704 / properties.height!).round(),
|
||||
targetHeight: 704);
|
||||
} else {
|
||||
compressedFile = file;
|
||||
}
|
||||
}
|
||||
} else if (currentSizeIndex == 2) {
|
||||
//下标方向为横向
|
||||
if (isDirection == 0) {
|
||||
//如果大于512的正方形直接换比例成512,否则使用原图
|
||||
if (properties.height! > 512) {
|
||||
compressedFile = await FlutterNativeImage.compressImage(file.path,
|
||||
quality: 100, targetWidth: 512, targetHeight: 512);
|
||||
} else {
|
||||
compressedFile = file;
|
||||
}
|
||||
} else if (isDirection == 1) {
|
||||
//横向
|
||||
if (properties.width! > 704) {
|
||||
compressedFile = await FlutterNativeImage.compressImage(file.path,
|
||||
quality: 100,
|
||||
targetWidth: 704,
|
||||
targetHeight:
|
||||
(properties.height! * 704 / properties.width!).round());
|
||||
} else {
|
||||
compressedFile = file;
|
||||
}
|
||||
} else if (isDirection == 2) {
|
||||
//纵向
|
||||
if (properties.height! > 512) {
|
||||
compressedFile = await FlutterNativeImage.compressImage(file.path,
|
||||
quality: 100,
|
||||
targetWidth:
|
||||
(properties.width! * 512 / properties.height!).round(),
|
||||
targetHeight: 512);
|
||||
} else {
|
||||
compressedFile = file;
|
||||
}
|
||||
}
|
||||
} else if (currentSizeIndex == 3) {
|
||||
//下标方向为纵向
|
||||
if (isDirection == 0) {
|
||||
//如果大于512的正方形直接换比例成512,否则使用原图
|
||||
if (properties.width! > 512) {
|
||||
compressedFile = await FlutterNativeImage.compressImage(file.path,
|
||||
quality: 100, targetWidth: 512, targetHeight: 512);
|
||||
} else {
|
||||
compressedFile = file;
|
||||
}
|
||||
} else if (isDirection == 1) {
|
||||
//横向
|
||||
if (properties.width! > 512) {
|
||||
compressedFile = await FlutterNativeImage.compressImage(file.path,
|
||||
quality: 100,
|
||||
targetWidth: 512,
|
||||
targetHeight:
|
||||
(properties.height! * 512 / properties.width!).round());
|
||||
} else {
|
||||
compressedFile = file;
|
||||
}
|
||||
} else if (isDirection == 2) {
|
||||
//纵向
|
||||
if (properties.height! > 910) {
|
||||
compressedFile = await FlutterNativeImage.compressImage(file.path,
|
||||
quality: 100,
|
||||
targetWidth:
|
||||
(properties.width! * 910 / properties.height!).round(),
|
||||
targetHeight: 910);
|
||||
} else {
|
||||
compressedFile = file;
|
||||
}
|
||||
}
|
||||
} else if (currentSizeIndex == 4) {
|
||||
//下标方向为横向
|
||||
if (isDirection == 0) {
|
||||
//如果大于512的正方形直接换比例成512,否则使用原图
|
||||
if (properties.height! > 512) {
|
||||
compressedFile = await FlutterNativeImage.compressImage(file.path,
|
||||
quality: 100, targetWidth: 512, targetHeight: 512);
|
||||
} else {
|
||||
compressedFile = file;
|
||||
}
|
||||
} else if (isDirection == 1) {
|
||||
//横向
|
||||
if (properties.width! > 910) {
|
||||
compressedFile = await FlutterNativeImage.compressImage(file.path,
|
||||
quality: 100,
|
||||
targetWidth: 910,
|
||||
targetHeight:
|
||||
(properties.height! * 910 / properties.width!).round());
|
||||
} else {
|
||||
compressedFile = file;
|
||||
}
|
||||
} else if (isDirection == 2) {
|
||||
//纵向
|
||||
if (properties.height! > 512) {
|
||||
compressedFile = await FlutterNativeImage.compressImage(file.path,
|
||||
quality: 100,
|
||||
targetWidth:
|
||||
(properties.width! * 512 / properties.height!).round(),
|
||||
targetHeight: 512);
|
||||
} else {
|
||||
compressedFile = file;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
ImageProperties properties2 =
|
||||
await FlutterNativeImage.getImageProperties(compressedFile!.path);
|
||||
// print("ImageProperties===" +
|
||||
// properties2.width.toString() +
|
||||
// "*" +
|
||||
// properties2.height.toString());
|
||||
|
||||
///判断图片大小
|
||||
// double size = AppUtil.getFileSizeDouble(bytes: compressedFile.lengthSync());
|
||||
//取整 大于10m直接抛弃
|
||||
//var s = size.ceil();
|
||||
// if (s > 10) {
|
||||
// EasyLoading.showToast('图片过大');
|
||||
// } else {
|
||||
// //取整 大于1m直接压缩,小于1m使用原图
|
||||
// if (s > 1) {
|
||||
// //图片压缩,压缩算法为 (10-s)*10,m越大调整后百分比越小
|
||||
// int percentage = (10 - s) * 10;
|
||||
// File compressedFile2 = await FlutterNativeImage.compressImage(
|
||||
// compressedFile.path,
|
||||
// quality: 80,
|
||||
// percentage: percentage);
|
||||
// //AppUtil.getFileSizeDouble(bytes: compressedFile2.lengthSync());
|
||||
// img = await AppUtil.uploadImage2(compressedFile2);
|
||||
// } else {
|
||||
// //不压
|
||||
//img = await AppUtil.uploadImage2(compressedFile2);
|
||||
// }
|
||||
// }
|
||||
|
||||
return img;
|
||||
}
|
||||
}
|
||||
10
pubspec.yaml
10
pubspec.yaml
|
|
@ -37,6 +37,16 @@ dependencies:
|
|||
crypto: ^3.0.3
|
||||
shared_preferences: ^2.0.7
|
||||
json_annotation: ^4.9.0
|
||||
cached_network_image: ^3.2.3
|
||||
image_cropper: ^7.0.5
|
||||
# image_cropper_for_web: ^5.0.4
|
||||
flutter_native_image: ^0.0.6
|
||||
photo_view: ^0.14.0
|
||||
image_picker: ^0.8.4
|
||||
permission_handler: ^10.4.5
|
||||
image_gallery_saver: ^2.0.3
|
||||
web_socket_channel: ^3.0.0
|
||||
flutter_client_sse: ^2.0.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
|
|||
|
|
@ -31,6 +31,10 @@
|
|||
|
||||
<title>Chat</title>
|
||||
<link rel="manifest" href="manifest.json">
|
||||
<!-- cropperjs -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.6.2/cropper.css" />
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.6.2/cropper.min.js"></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<script src="flutter_bootstrap.js" async></script>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user