Files
ai_chat_assistant/lib/screens/chat_screen.dart
2025-06-18 11:28:37 +08:00

180 lines
6.2 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import '../widgets/ai_avatar.dart';
import '../widgets/chat_bubble.dart';
import '../widgets/chat_input.dart';
import '../widgets/gradient_background.dart';
import '../models/chat_message.dart';
import 'dart:convert';
class ChatScreen extends StatefulWidget {
const ChatScreen({super.key});
@override
State<ChatScreen> createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
final List<ChatMessage> _messages = [];
int? _currentStreamingMessageIndex;
int _lastUserMessageCount = 0; // 添加这个变量跟踪用户消息数量
void _handleSendMessage(String text) {
if (text.trim().isEmpty) return;
setState(() {
_messages.add(ChatMessage(
text: text,
isUser: true,
timestamp: DateTime.now(),
));
_lastUserMessageCount = _getUserMessageCount(); // 更新用户消息计数
_currentStreamingMessageIndex = null; // 重置流索引,强制创建新气泡
});
}
// 处理语音识别后的文本
void _handleAIResponse(String text) {
setState(() {
_messages.add(ChatMessage(
text: text,
isUser: true, // 用户的消息
timestamp: DateTime.now(),
));
_lastUserMessageCount = _getUserMessageCount(); // 更新用户消息计数
_currentStreamingMessageIndex = null; // 重置流索引,强制创建新气泡
});
}
// 计算用户消息总数
int _getUserMessageCount() {
return _messages.where((msg) => msg.isUser).length;
}
// 处理 AI 流式响应
void _handleAIStreamResponse(String text, bool isComplete) {
// 检查是否有新的用户消息如果有应该创建新的AI响应
final currentUserMessageCount = _getUserMessageCount();
final hasNewUserMessage = currentUserMessageCount > _lastUserMessageCount;
setState(() {
if (hasNewUserMessage || _currentStreamingMessageIndex == null) {
// 有新用户消息或首次响应,创建新气泡
_messages.add(ChatMessage(
text: text,
isUser: false,
timestamp: DateTime.now(),
));
_currentStreamingMessageIndex = _messages.length - 1;
_lastUserMessageCount = currentUserMessageCount; // 更新追踪
} else {
// 更新现有气泡
if (_currentStreamingMessageIndex! < _messages.length) {
_messages[_currentStreamingMessageIndex!] = ChatMessage(
text: text,
isUser: false,
timestamp: _messages[_currentStreamingMessageIndex!].timestamp,
);
}
}
// 如果完成了,重置索引
if (isComplete) {
_currentStreamingMessageIndex = null;
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: GradientBackground(
child: SafeArea(
child: Column(
children: [
// Header with AI avatar
SizedBox(
height: 140,
child: Stack(
children: [
// 背景渐变已由外层GradientBackground提供
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// AI贴图
const Padding(
padding: EdgeInsets.only(left: 16, top: 0),
child: AIAvatar(width: 120, height: 140),
),
const SizedBox(width: 8),
// 文字整体下移
Padding(
padding: const EdgeInsets.only(top: 48),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Hi, 我是众众!',
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 8),
Text(
'您的专属看、选、买、用车助手',
style: TextStyle(
fontSize: 14,
color: Colors.white.withOpacity(0.8),
),
),
],
),
),
// 可选:右上角关闭按钮
const Spacer(),
Padding(
padding: const EdgeInsets.only(top: 16, right: 16),
child: Icon(Icons.close, color: Colors.white),
),
],
),
// 底部分割线
Positioned(
left: 0,
right: 0,
bottom: 0,
child: Container(
height: 1,
color: Colors.white.withOpacity(0.15),
),
),
],
),
),
// Chat messages
Expanded(
child: ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 16),
itemCount: _messages.length,
reverse: true,
itemBuilder: (context, index) {
final message = _messages[_messages.length - 1 - index];
return ChatBubble(message: message);
},
),
),
// Input area
ChatInput(
onSendMessage: _handleSendMessage,
onAIResponse: _handleAIResponse,
onAIStreamResponse: _handleAIStreamResponse, // 添加新回调
),
],
),
),
),
);
}
}