362 lines
12 KiB
Dart
362 lines
12 KiB
Dart
import 'dart:async';
|
||
import 'dart:convert';
|
||
import 'dart:core';
|
||
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 'package:web_socket_channel/web_socket_channel.dart';
|
||
|
||
import '../../network/NetworkConfig.dart';
|
||
import 'HomeModel.dart';
|
||
import '../../beans/MessageBean.dart';
|
||
|
||
class Homepage extends StatefulWidget {
|
||
const Homepage({super.key});
|
||
|
||
@override
|
||
State<Homepage> createState() => _HomepageState();
|
||
}
|
||
|
||
class _HomepageState extends State<Homepage> {
|
||
late StreamSubscription subscription;
|
||
final HomeModel viewModel = HomeModel();
|
||
final TextEditingController _chatController = TextEditingController();
|
||
final ScrollController _scrollController = ScrollController();
|
||
String text = "";
|
||
String data = '';
|
||
List<MessageBean> list = [];
|
||
int created = 0;
|
||
String msg = "";
|
||
|
||
Future<void> chat() async {
|
||
EasyLoading.show(status: 'loading...');
|
||
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) {
|
||
text = str;
|
||
}
|
||
|
||
late WebSocketChannel channel;
|
||
bool isConnected = false;
|
||
|
||
String outputImg = "";
|
||
|
||
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') ||
|
||
(responseData["type"] == 'executed' &&
|
||
responseData["data"]['node'] == '13')) {
|
||
print("responseData: " +
|
||
responseData["data"]['output']['images'][0]['filename']);
|
||
|
||
outputImg = "${"http://$serverAddress/view?filename=" +
|
||
responseData["data"]['output']['images'][0]['filename']}&type=output";
|
||
list[list.length - 1].img = outputImg;
|
||
setState(() {
|
||
EasyLoading.dismiss();
|
||
});
|
||
}
|
||
}
|
||
|
||
// 如果我们还没有标记为已连接,现在标记为已连接
|
||
}, onError: (error) {
|
||
// 处理错误
|
||
print('WebSocket error: $error');
|
||
isConnected = false; // 连接已关闭或出错
|
||
}, onDone: () {
|
||
// 连接关闭时的处理
|
||
print('WebSocket closed');
|
||
isConnected = false; // 连接已关闭
|
||
}, cancelOnError: true);
|
||
}
|
||
|
||
@override
|
||
void initState() {
|
||
// TODO: implement initState
|
||
super.initState();
|
||
var nowTime = DateTime.now(); //获取当前时间
|
||
var nTime = nowTime.millisecondsSinceEpoch; //单位是毫秒(千分之一秒),13位时间戳
|
||
NetworkConfig.clientId = nTime.toString();
|
||
connectToWebSocket(NetworkConfig.serverAddress, NetworkConfig.clientId);
|
||
MessageBean a = MessageBean("user", "Start a new Chat", "");
|
||
MessageBean b = MessageBean(
|
||
"assistant",
|
||
"这天,小猫娘身穿粉色的内衣,黑色丝袜,在家里玩耍,看到主人正在沙发上玩手机,小猫娘忽然对主人看的内容很感兴趣,也想要看看,就跑到主人面前一脸希翼的看着主人",
|
||
"assets/images/20240622145935.png");
|
||
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);
|
||
});
|
||
|
||
subscription = viewModel.streamController.stream.listen((newData) {
|
||
String code = newData['code'];
|
||
if (code.isNotEmpty) {
|
||
if (code == "chat") {
|
||
//有数据
|
||
data = newData['data'];
|
||
list.add(MessageBean("assistant", data, ""));
|
||
setState(() {});
|
||
Future.delayed(const Duration(milliseconds: 500), () {
|
||
_scrollController
|
||
.jumpTo(_scrollController.position.maxScrollExtent);
|
||
});
|
||
//EasyLoading.showToast(data);
|
||
//print("data" + data.toString());
|
||
}
|
||
EasyLoading.dismiss();
|
||
} else {
|
||
EasyLoading.dismiss();
|
||
}
|
||
});
|
||
}
|
||
|
||
@override
|
||
void dispose() {
|
||
// TODO: implement dispose
|
||
_chatController.dispose();
|
||
subscription.cancel();
|
||
super.dispose();
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Scaffold(
|
||
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(
|
||
children: [
|
||
Container(
|
||
width: double.infinity,
|
||
height: 1,
|
||
color: Color(0xFFDFDFDF),
|
||
),
|
||
Expanded(
|
||
child: Container(
|
||
alignment: Alignment.topCenter,
|
||
child: ListView.builder(
|
||
// reverse: true,
|
||
controller: _scrollController,
|
||
itemCount: list.length,
|
||
itemBuilder: (Context, index) {
|
||
return _item(index);
|
||
}),
|
||
)),
|
||
Card(
|
||
elevation: 5,
|
||
color: Colors.white,
|
||
margin: EdgeInsets.only(left: 16, right: 16, bottom: 30),
|
||
child: Row(
|
||
children: [
|
||
//输入框
|
||
Expanded(
|
||
child: Container(
|
||
margin: EdgeInsets.only(left: 16),
|
||
child: TextField(
|
||
controller: _chatController,
|
||
onChanged: _textFieldChanged,
|
||
decoration: InputDecoration(
|
||
border: InputBorder.none, // 移除非聚焦状态下的边框
|
||
enabledBorder: InputBorder.none, // 移除获得焦点但未输入内容时的边框
|
||
focusedBorder: InputBorder.none, // 移除输入时的边框
|
||
),
|
||
),
|
||
),
|
||
),
|
||
GestureDetector(
|
||
onTap: () {
|
||
list.add(MessageBean("user", text, ""));
|
||
_chatController.clear();
|
||
Future.delayed(const Duration(milliseconds: 200), () {
|
||
_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))),
|
||
child: Text(
|
||
"发送",
|
||
style: TextStyle(color: Colors.white),
|
||
),
|
||
),
|
||
)
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
_item(index) {
|
||
if (index == 0) {
|
||
return Container();
|
||
}
|
||
return Padding(
|
||
padding: EdgeInsets.symmetric(vertical: 8.0),
|
||
child: list[index].role != 'user'
|
||
? Column(
|
||
children: [
|
||
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 - 80, // 确保不超过屏幕宽度
|
||
),
|
||
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,
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
list[index].img != ""
|
||
? SizedBox(
|
||
width: MediaQuery.of(context).size.width,
|
||
child: Image.network(list[index].img!))
|
||
: Container(
|
||
child: Text("正在生图中"),
|
||
)
|
||
],
|
||
)
|
||
: Row(
|
||
mainAxisAlignment: MainAxisAlignment.end,
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
ConstrainedBox(
|
||
constraints: BoxConstraints(
|
||
maxWidth:
|
||
MediaQuery.of(context).size.width - 100, // 确保不超过屏幕宽度
|
||
),
|
||
child: Container(
|
||
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].content!,
|
||
style: TextStyle(
|
||
fontSize: 16,
|
||
color: Colors.white,
|
||
),
|
||
),
|
||
),
|
||
),
|
||
Container(
|
||
width: 45,
|
||
height: 45,
|
||
margin: EdgeInsets.only(right: 16, top: 4),
|
||
child: ClipRRect(
|
||
borderRadius: BorderRadius.circular(10),
|
||
child: Image(
|
||
fit: BoxFit.cover,
|
||
image: AssetImage('assets/images/avatar1.png'),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|