Files
sweet_chat_app/lib/pages/chat_page.dart
2026-06-18 14:20:37 +08:00

226 lines
5.9 KiB
Dart

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<ChatPage> createState() => _ChatPageState();
}
class _ChatPageState extends State<ChatPage> {
final TextEditingController _controller = TextEditingController();
final ScrollController _scrollController = ScrollController();
late int userId;
late List<ChatMessage> _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,
);
});
}
}