import 'package:flutter/material.dart'; import 'package:liquid_glass_widgets/liquid_glass_widgets.dart'; import 'package:sweet_chat_app/models/ImMessage.dart'; import 'package:sweet_chat_app/services/webSocket_service.dart'; import 'package:sweet_chat_app/utils/SpUtil.dart'; class ChatMessage { final String text; final bool isMe; final String? avatar; ChatMessage({required this.text, required this.isMe, this.avatar}); } class ChatPage extends StatefulWidget { final int contractId; final String contactName; final String avatarUrl; const ChatPage({ super.key, required this.contractId, required this.contactName, required this.avatarUrl, }); @override State createState() => _ChatPageState(); } class _ChatPageState extends State { final TextEditingController _controller = TextEditingController(); final ScrollController _scrollController = ScrollController(); late int userId; late List _messages; @override void initState() { super.initState(); _messages = []; userId = SpUtil.getInt('userId') ?? 0; final online = ImMessage( type: "ONLINE", from: userId.toString(), to: 'SYSTEM', content: '', ); WebSocketService().connect('ws://172.29.100.146:5050?userId=$userId'); WebSocketService().send(online.toJsonString()); WebSocketService().messages.listen((wsMsg) { final ImMessage imMsg = ImMessage.fromJsonString(wsMsg); setState(() { if (imMsg.type.contains('CHAT')) { _messages.add( ChatMessage( text: imMsg.content, isMe: userId.toString() == imMsg.from, avatar: widget.avatarUrl, ), ); } }); _controller.clear(); scrollToBottom(); }); } @override void dispose() { super.dispose(); WebSocketService().disconnect(); } void _sendMessage() { final text = _controller.text.trim(); if (text.isEmpty) return; final chat = ImMessage( type: "CHAT_PRIVATE", from: userId.toString(), to: widget.contractId.toString(), content: text, ); WebSocketService().send(chat.toJsonString()); } Widget _buildAvatar(String? url) { return Padding( padding: const EdgeInsets.only(right: 8, top: 4), child: CircleAvatar( radius: 18, backgroundColor: Colors.grey[200], backgroundImage: url != null ? NetworkImage(url) : null, child: url == null ? const Icon(Icons.person, size: 18, color: Colors.grey) : null, ), ); } @override Widget build(BuildContext context) { return Scaffold( appBar: _buildAppBar(), body: SafeArea( child: Padding( padding: const EdgeInsets.all(10), child: Column( children: [ Expanded(child: _buildMessage()), SizedBox(height: 10), _buildInput(), ], ), ), ), ); } PreferredSizeWidget _buildAppBar() { return AppBar( backgroundColor: Color(0xFF120D25), foregroundColor: Colors.white, title: Text( widget.contactName, style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, color: Colors.white, ), ), centerTitle: true, ); } Widget _buildMessage() { return GlassCard( child: ListView.separated( controller: _scrollController, separatorBuilder: (BuildContext context, int index) { return SizedBox(height: 10); }, itemCount: _messages.length, itemBuilder: (context, index) { final msg = _messages[index]; return Row( mainAxisAlignment: msg.isMe ? MainAxisAlignment.end : MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ if (!msg.isMe) _buildAvatar(msg.avatar), Flexible( child: Container( decoration: BoxDecoration( color: msg.isMe ? Color(0xFF2FA951): Color(0xFF030816), borderRadius: BorderRadius.circular(12), ), padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 10, ), child: Text( msg.text, style: const TextStyle(fontSize: 16, color: Colors.white), ), ), ), const SizedBox(width: 8), if (msg.isMe) _buildAvatar("https://picsum.photos/id/1027/200"), ], ); }, ), ); } Widget _buildInput() { return SafeArea( child: GlassContainer( width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Row( children: [ Expanded( child: TextField( controller: _controller, onSubmitted: (_) => _sendMessage(), style: const TextStyle(color: Colors.white), cursorColor: Colors.white, decoration: const InputDecoration( hintText: '输入消息...', hintStyle: TextStyle(color: Colors.white), border: InputBorder.none, ), ), ), IconButton( onPressed: _sendMessage, icon: const Icon(Icons.send, color: Colors.green), ), ], ), ), ); } void scrollToBottom() { Future.delayed(const Duration(milliseconds: 100), () { _scrollController.animateTo( _scrollController.position.maxScrollExtent, duration: const Duration(milliseconds: 300), curve: Curves.easeOut, ); }); } }