Commit a05f35b4 authored by netyouli's avatar netyouli

添加AI图生图功能

parent f93dea00
......@@ -2,6 +2,7 @@
# platform :ios, '11.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
platform :ios, '13.0'
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
......
......@@ -338,6 +338,6 @@ SPEC CHECKSUMS:
webview_flutter_wkwebview: 2e2d318f21a5e036e2c3f26171342e95908bd60a
WechatOpenSDK-XCFramework: acdeeda129efbef9532bca8a10c24e1b4b8c7d69
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
PODFILE CHECKSUM: 4d886a47a53fe168bd0681f69e154bd6e984d99f
COCOAPODS: 1.11.3
......@@ -515,7 +515,7 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 5;
PRODUCT_BUNDLE_IDENTIFIER = com.wudi.app1;
PRODUCT_BUNDLE_IDENTIFIER = com.wudi.app;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "dev-profile";
......@@ -547,7 +547,7 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 5;
PRODUCT_BUNDLE_IDENTIFIER = com.wudi.app1;
PRODUCT_BUNDLE_IDENTIFIER = com.wudi.app;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "dis-profile";
......
......@@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSCameraUsageDescription</key>
<string>$(EXECUTABLE_NAME)需要拍照权限拍照发送或者上传图片</string>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
......
import 'dart:collection';
import 'dart:io';
import 'package:chart/common/entities/classFyDetail.dart';
import 'package:chart/common/entities/convers.dart';
import 'package:chart/common/entities/entities.dart';
import 'package:chart/common/entities/good.dart';
import 'package:chart/common/utils/utils.dart';
import 'package:chart/common/values/values.dart';
import 'package:dio/dio.dart';
import '../../entity/plan_entity.dart';
import '../../entity/square_entity.dart';
......@@ -72,6 +76,17 @@ class NewsAPI {
return MidJourneyModel.fromJson(response);
}
// 融合图片
static Future<MidJourneyModel> blendImageByMidJourney(List<String> urls, String question, int conversionId) async {
var response = await HttpUtil().post('/midJourney/createImageByBlend', data: {
// "conversionId": conversionId,
// "next": 0,
"urls": urls,
// "question": question
});
return MidJourneyModel.fromJson(response);
}
// 生成图片 v1-v4 u1-u4
static Future<MidJourneyModel> createImageByButtonMidJourney(String button, String buttonMessageId) async {
var response = await HttpUtil().post('/midJourney/createImageByButton', data: {
......@@ -82,6 +97,23 @@ class NewsAPI {
return MidJourneyModel.fromJson(response);
}
static Future<MidJourneyModel> uploadFile(List<File> files) async {
var fileParam = HashMap<String, MultipartFile>();
if (files.length == 1) {
final file = files[0];
final fileName = file.path.split('/').last;
fileParam["file"] = await MultipartFile.fromFile(file.path, filename: fileName);
} else {
for (var i = 0; i < files.length; i++) {
final file = files[i];
final fileName = file.path.split('/').last;
fileParam["file$i"] = await MultipartFile.fromFile(file.path, filename: fileName);
}
}
var response = await HttpUtil().postForm('/midJourney/uploadFile', data: fileParam);
return MidJourneyModel.fromJson(response);
}
static Future<List<GoodEntity>> getGoodsList() async {
var response = await HttpUtil().get(
'/goods/goodsList',
......
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'package:cookie_jar/cookie_jar.dart';
......@@ -265,13 +266,14 @@ class HttpUtil {
if (authorization != null) {
requestOptions.headers!.addAll(authorization);
}
Logger.debugPrint("request = $SERVER_API_URL$path");
var response = await dio.get(
path,
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
);
Logger.debugPrint("response = ${jsonEncode(response.data)}");
return response.data;
}
......@@ -288,6 +290,8 @@ class HttpUtil {
if (authorization != null) {
requestOptions.headers!.addAll(authorization);
}
Logger.debugPrint("request = $SERVER_API_URL$path");
Logger.debugPrint("params = ${jsonEncode(data)}");
var response = await dio.post(
path,
data: data,
......@@ -295,6 +299,7 @@ class HttpUtil {
options: requestOptions,
cancelToken: cancelToken,
);
Logger.debugPrint("response = ${jsonEncode(response.data)}");
return response.data;
}
......
......@@ -38,24 +38,27 @@ class AIDrawImageResultController extends GetxController {
var _buttonMessageId = "";
Rx<List<String>> bottomButtonTitles = Rx<List<String>>([]);
var opearter = "";
var imageUrls = List.generate(0, (index) => "");
@override
void onInit() {
// TODO: implement onInit
super.onInit();
final arguments = Get.arguments;
opearter = arguments["opearter"];
conversionId = arguments["conversionId"];
text.value = arguments["text"];
text.value = arguments["text"] ?? "";
ratio = arguments["ratio"];
s = arguments["s"];
styleName.value = arguments["styleName"];
imageUrls = arguments["imageUrls"] ?? [];
}
@override
void onReady() {
super.onReady();
makeDrawImage();
makeImage();
}
@override
......@@ -107,6 +110,43 @@ class AIDrawImageResultController extends GetxController {
}
}
makeImage() {
if (opearter == "textToImage") {
makeDrawImage();
} else {
makeBlendImage();
}
}
makeBlendImage() async {
progress.value = 0;
final question = '$text --ar $ratio$s';
Logger.debugPrint("question = $question");
EasyLoading.show(status: "AI正在生成中...");
try {
// state.isLoading = true;
final res = await NewsAPI.blendImageByMidJourney(imageUrls, "--ar $ratio$s" ,conversionId);
if (res.status == 401) {
EasyLoading.showInfo('您还未登录,请登录后体验功能。');
Get.toNamed(AppRoutes.SIGN_IN);
// state.isLoading = false;
} else {
EasyLoading.dismiss();
if (res.data == null) {
EasyLoading.showToast(res.message ?? "未知错误");
} else {
showProgressView.value = true;
await initEventSource(res.data ?? "");
}
}
// state.isLoading = false;
} catch (e) {
// state.isLoading = false;
EasyLoading.dismiss();
}
}
makeDrawImage() async {
progress.value = 0;
final question = '$text --ar $ratio$s';
......@@ -156,13 +196,20 @@ class AIDrawImageResultController extends GetxController {
if (model.progress == 100) {
showProgressView.value = false;
}
final progressBase64 = model.progressBase64 ?? "";
if (model.response?.imageUrl != null) {
imageUrl.value = model.response?.imageUrl ?? "" ;
} else if (model.progressImageUrl != null) {
imageUrl.value = model.progressImageUrl ?? "";
}
if (imageUrl.value.isNotEmpty) {
loadImage(imageUrl.value);
if (progressBase64.isNotEmpty) {
final decode = base64Decode(progressBase64.replaceAll('\n', '').split(',')[1]);
placehoderImageData = imageData.value ?? decode;
imageData.value = decode;
} else {
if (imageUrl.value.isNotEmpty) {
loadImage(imageUrl.value);
}
}
final first = model.response?.buttons?.first ?? "";
if (first == "V1" || first == "U1") {
......
......@@ -26,9 +26,10 @@ class MidJourneyModel {
class MidJourneyImageModel {
int? progress;
String? progressImageUrl;
String? progressBase64;
MidJourneyImageResponse? response;
MidJourneyImageModel({this.progress, this.progressImageUrl, this.response});
MidJourneyImageModel({this.progress, this.progressImageUrl, this.response, this.progressBase64,});
MidJourneyImageModel.fromJson(Map<String, dynamic> json) {
progress = json['progress'];
......@@ -36,6 +37,7 @@ class MidJourneyImageModel {
response = json['response'] != null
? new MidJourneyImageResponse.fromJson(json['response'])
: null;
progressBase64 = json["progressBase64"];
}
Map<String, dynamic> toJson() {
......@@ -45,6 +47,7 @@ class MidJourneyImageModel {
if (this.response != null) {
data['response'] = this.response?.toJson();
}
data['progressBase64'] = this.progressBase64;
return data;
}
}
......
......@@ -193,7 +193,7 @@ class AIDrawImageResultPage extends GetView<AIDrawImageResultController> {
InkWell(
onTap: () {
controller.makeDrawImage();
controller.makeImage();
},
child: Container(
width: 150,
......
import 'dart:io';
import 'dart:math';
import 'package:chart/common/apis/apis.dart';
import 'package:chart/common/utils/utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_vibrate/flutter_vibrate.dart';
import 'package:get/get.dart';
import 'package:loading_animation_widget/loading_animation_widget.dart';
import 'package:pinput/pinput.dart';
import 'package:image_picker/image_picker.dart';
import '../../common/apis/user.dart';
import '../../common/routers/names.dart';
......@@ -24,6 +28,7 @@ class AIDrawImageController extends GetxController {
TextEditingController textController = TextEditingController(text: "");
Offset startPoint = const Offset(0, 0);
RxList<AIDrawDescriptiveItem> randomDescriptionItems = RxList<AIDrawDescriptiveItem>();
RxList<XFile> assets = RxList<XFile>();
var sizePictures = [
AIDrawSizePictrue(name: "手机壁纸", ratio: "9:16", imageUrl: "assets/images/v_iphone.png"),
AIDrawSizePictrue(name: "电脑壁纸", ratio: "16:9", imageUrl: "assets/images/h_iphone.png"),
......@@ -54,6 +59,7 @@ class AIDrawImageController extends GetxController {
];
var textCount = 0.obs;
var uploadImageUrls = List.generate(0, (index) => "");
@override
void onInit() {
......@@ -89,6 +95,81 @@ class AIDrawImageController extends GetxController {
textController.setText(model.descriptives[randomIndex].description);
}
clearSelectedImage() {
assets.value = [];
uploadImageUrls.clear();
}
selectImageUpload() async {
/*
final image = await ImagePicker().pickImage(source: ImageSource.gallery);
if (image == null) return null;
*/
uploadImageUrls.clear();
List<XFile>? images = await ImagePicker().pickMultiImage();
final count = images.length;
if (count <= 1 || count > 5) {
EasyLoading.showError("请选择2-5张图片");
} else {
assets.value = images;
}
EasyLoading.show(
status: "正在上传,请稍后",
dismissOnTap: false,
maskType: EasyLoadingMaskType.none,
indicator: LoadingAnimationWidget.staggeredDotsWave(
color: Colors.white,
size: 30,
));
for (var i = 0; i < count; i++) {
final image = images[i];
final res = await NewsAPI.uploadFile([File(image.path)]);
if (res.status == 401) {
EasyLoading.showInfo('您还未登录,请登录后体验功能。');
Get.toNamed(AppRoutes.SIGN_IN);
// state.isLoading = false;
} else {
if (res.data == null) {
EasyLoading.showToast(res.message ?? "未知错误");
} else {
if (i == count - 1) {
EasyLoading.dismiss();
}
uploadImageUrls.add(res.data ?? "");
}
}
}
}
toImageMarkDrawImage() async {
EasyLoading.show(
status: "加载中",
dismissOnTap: false,
maskType: EasyLoadingMaskType.none,
indicator: LoadingAnimationWidget.staggeredDotsWave(
color: Colors.white,
size: 30,
));
Vibrate.feedback(FeedbackType.impact);
final conversionId = await UserAPI.createConversion();
EasyLoading.dismiss();
if (conversionId == 401) {
EasyLoading.showInfo('您还为登录,请登录后体验功能。');
Get.toNamed(AppRoutes.SIGN_IN);
} else {
Get.toNamed(AppRoutes.AI_DRAW_IMAGE_RESULT_PAGE,
arguments: {
"opearter": "imageToImage",
"conversionId": conversionId,
"imageUrls": List<String>.from(uploadImageUrls),
"ratio": ratio.value,
"s": s,
"styleName": styleName.value
});
clearSelectedImage();
}
}
toTextMakeDrawImage() async {
EasyLoading.show(
status: "加载中",
......@@ -107,6 +188,7 @@ class AIDrawImageController extends GetxController {
} else {
Get.toNamed(AppRoutes.AI_DRAW_IMAGE_RESULT_PAGE,
arguments: {
"opearter": "textToImage",
"conversionId": conversionId,
"text": textController.text,
"ratio": ratio.value,
......
import 'dart:io';
import 'package:chart/pages/ai-draw-image/image_square_view.dart';
import 'package:chart/pages/ai-draw-image/text_to_image_view.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
......@@ -5,8 +9,25 @@ import '../../common/style/color.dart';
import 'controller.dart';
Widget makeShowAssetImageView(double width) {
final controller = Get.find<AIDrawImageController>();
return Container(
child: Stack(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: controller.assets.map((element) {
return Image.file(File(element.path), width: (width - 20) / 5.0, height: 160, fit: BoxFit.contain,);
}).toList(),
),
],
),
);
}
Widget makeInsertPictureView() {
final controller = Get.find<AIDrawImageController>();
// 插入图片
return Container(
height: 220,
......@@ -16,34 +37,70 @@ import 'controller.dart';
borderRadius: BorderRadius.circular(20)
),
child: LayoutBuilder(builder: (context, constraints) {
return Column(
return Stack(
children: [
makeSectionTitleView("01.", "插入图片"),
SizedBox(height: 10,),
InkWell(
onTap: () {},
child: Container(
height: 160,
width: constraints.maxWidth - 20,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
decoration: BoxDecoration(
color: AppColor.black1,
borderRadius: BorderRadius.circular(10)
Obx(() => Visibility(
visible: controller.assets.isNotEmpty,
child: Positioned(
right: 0,
top: 0,
child: InkWell(
onTap: () {
controller.clearSelectedImage();
},
child: Image.asset("assets/images/清空图片.png", width: 30, height: 30,),
)
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image(image: AssetImage("assets/images/jifen1.png", ), width: 20, height: 20,),
SizedBox(height: 20,),
Text("支持图片格式:JPG/PNG,低于10M",
style: TextStyle(
fontSize: 12
)
),
Column(
children: [
makeSectionTitleView("01.", "插入图片"),
SizedBox(height: 10,),
Container(
height: 160,
width: constraints.maxWidth - 20,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
decoration: BoxDecoration(
color: AppColor.black1,
borderRadius: BorderRadius.circular(10)
),
child: Obx(() => Stack(
children: [
Visibility(
visible: controller.assets.isEmpty,
child: InkWell(
onTap: () {
controller.selectImageUpload();
},
child: Container(
height: 160,
width: constraints.maxWidth - 20,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image(image: AssetImage("assets/images/添加图片.png", ), width: 40, height: 40,),
SizedBox(height: 20,),
Text("支持图片格式:JPG/PNG,低于10M",
style: TextStyle(
fontSize: 12
),
),
]
),
),
)
),
),
]
Visibility(
visible: controller.assets.isNotEmpty,
child: makeShowAssetImageView(constraints.maxWidth - 20)
)
],
),
)
),
),
),
],
)
],
);
},)
......@@ -52,6 +109,7 @@ import 'controller.dart';
Widget makeStyleSelectionItem(int index) {
final controller = Get.find<AIDrawImageController>();
final item = controller.styles[index];
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
......@@ -63,38 +121,49 @@ import 'controller.dart';
children: [
Stack(
children: [
Container(
Obx(() => Container(
// padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
border: Border.all(
color: item.name == controller.styleName.value ? AppColor.primary : Colors.transparent,
width: 2
)
),
child: ClipRRect(
borderRadius: BorderRadius.circular(5),
child: Image(image: AssetImage(controller.styles[index].imageUrl),
child: Image(image: AssetImage(item.imageUrl),
fit: BoxFit.fill,
// 83:89
width: constraints.maxWidth,
height: constraints.maxHeight - 30,
),
),
),
Positioned(
right: 15,
top: 8,
child: Container(
padding: EdgeInsets.fromLTRB(5, 2, 5, 2),
decoration: BoxDecoration(
color: AppColor.yellow1,
borderRadius: BorderRadius.circular(5),
),
child: Text("VIP",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold
)),
Visibility(
visible: item.vip.isNotEmpty,
child: Positioned(
right: 15,
top: 8,
child: Container(
padding: EdgeInsets.fromLTRB(5, 2, 5, 2),
decoration: BoxDecoration(
color: AppColor.yellow1,
borderRadius: BorderRadius.circular(5),
),
child: Text(item.vip,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold
),
),
),
),
)
)
],
),
SizedBox(height: 5,),
Text("通用风格")
Text(item.name)
],
);
},
......@@ -131,7 +200,10 @@ import 'controller.dart';
scrollDirection: Axis.horizontal,
children: List.generate(controller.styles.length, (index) {
return InkWell(
onTap: () {},
onTap: () {
controller.s = controller.styles[index].s;
controller.styleName.value = controller.styles[index].name;
},
child: makeStyleSelectionItem(index)
);
}),
......@@ -143,6 +215,7 @@ import 'controller.dart';
}
Widget makeImageToImageView() {
final controller = Get.find<AIDrawImageController>();
return Stack(
children: [
SingleChildScrollView(
......@@ -181,7 +254,9 @@ import 'controller.dart';
Positioned(
child: Padding(padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
child: InkWell(
onTap: () {},
onTap: () {
controller.toImageMarkDrawImage();
},
child: Container(
height: 50,
alignment: Alignment.center,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment