图文请求

This commit is contained in:
zhangzhan 2024-06-22 15:04:17 +08:00
parent d3113a5e64
commit 87e1b8a18c
18 changed files with 2221 additions and 125 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 979 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 925 KiB

BIN
assets/images/kuang.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
assets/images/upbg.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

View 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);
}

View 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
View 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;
}
}

View File

@ -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,
//

View File

@ -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);
}
//

View File

@ -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"; //
}

View File

@ -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

View File

@ -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,

View File

@ -1,6 +0,0 @@
class Message{
final String text;
final bool isMe;
Message(this.text, this.isMe);
}

View 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'),
)
]),
),
);
}
}

View 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) {
//512512使
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) {
//512512使
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) {
//512512使
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) {
//512512使
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) {
//512512使
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;
}
}

View File

@ -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:

View File

@ -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>