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 createState() => _HomepageState(); } class _HomepageState extends State { late StreamSubscription subscription; final HomeModel viewModel = HomeModel(); final TextEditingController _chatController = TextEditingController(); final ScrollController _scrollController = ScrollController(); String text = ""; String data = ''; List list = []; int created = 0; String msg = ""; Future chat() async { EasyLoading.show(status: 'loading...'); List> 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 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: [ 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'), ), ), ), ], ), ); } }