新增一个chat_popup 来显示聊天窗口

This commit is contained in:
2025-09-22 17:15:44 +08:00
parent e04110c6d5
commit 049cb7b5c4
10 changed files with 912 additions and 20 deletions

View File

@@ -1,99 +0,0 @@
import 'package:ai_chat_assistant/models/chat_message.dart';
import 'package:basic_intl/intl.dart';
import 'package:flutter/material.dart';
import '../enums/message_status.dart';
import '../widgets/chat_box.dart';
import '../widgets/gradient_background.dart';
import '../services/message_service.dart';
import 'package:provider/provider.dart';
import '../widgets/chat_header.dart';
import '../widgets/chat_footer.dart';
class FullScreen extends StatefulWidget {
const FullScreen({super.key});
@override
State<FullScreen> createState() => _FullScreenState();
}
class _FullScreenState extends State<FullScreen> {
final ScrollController _scrollController = ScrollController();
@override
void initState() {
super.initState();
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
void _scrollToBottom() {
if (_scrollController.hasClients) {
_scrollController.animateTo(
0.0,
duration: const Duration(milliseconds: 200),
curve: Curves.easeOut,
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: GradientBackground(
colors: const [
Color(0XFF3B0A3F),
Color(0xFF0E0E24),
Color(0xFF0C0B33),
],
child: SafeArea(
child: Column(
children: [
ChatHeader(
onClose: () {
Provider.of<MessageService>(context, listen: false)
.abortReply();
Navigator.pop(context);
},
),
const SizedBox(height: 12),
Expanded(
child: Consumer<MessageService>(
builder: (context, messageService, child) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollToBottom();
});
if (messageService.messages.isEmpty) {
Future.delayed(const Duration(milliseconds: 500), () {
String text = Intl.getCurrentLocale().startsWith('zh')
? "您好,我是众众,请问有什么可以帮您?"
: "Hi, I'm Zhongzhong, may I help you ? ";
messageService.addMessage(text, false, MessageStatus.normal);
});
}
return ChatBox(
scrollController: _scrollController,
messages: messageService.messages,
);
},
),
),
const SizedBox(height: 12),
Consumer<MessageService>(
builder: (context, messageService, child) => ChatFooter(
onClear: () {
messageService.abortReply();
messageService.clearMessages();
},
),
),
],
),
),
),
);
}
}

View File

@@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import '../widgets/floating_icon.dart';
import '../utils/assets_util.dart';
import '../widgets/chat_popup.dart';
import '../widgets/chat_floating_icon.dart';
class MainScreen extends StatefulWidget {
const MainScreen({super.key});
@@ -34,7 +36,8 @@ class _MainScreenState extends State<MainScreen> {
),
),
),
FloatingIcon(),
// FloatingIcon(),
ChatPopup()
],
),
);

View File

@@ -1,11 +1,11 @@
import 'package:flutter/material.dart';
import 'dart:ui';
import '../widgets/chat_box.dart';
import '../screens/full_screen.dart';
import 'package:provider/provider.dart';
import '../services/message_service.dart';
import '../widgets/gradient_background.dart';
import '../utils/assets_util.dart';
import '../pages/full_screen.dart';
class PartScreen extends StatefulWidget {
final VoidCallback? onHide;
@@ -66,7 +66,7 @@ class _PartScreenState extends State<PartScreen> {
MaterialPageRoute(
builder: (context) => ChangeNotifierProvider.value(
value: messageService, // 传递同一个单例实例
child: const FullScreen(),
child: const FullScreenPage(),
),
),
);
@@ -76,13 +76,21 @@ class _PartScreenState extends State<PartScreen> {
EdgeInsets _calculatePosition(BoxConstraints constraints) {
final screenWidth = constraints.maxWidth;
final screenHeight = constraints.maxHeight;
final iconX = screenWidth - widget.floatingIconPosition.dx;
final iconY = screenHeight - widget.floatingIconPosition.dy;
// 聊天框的尺寸
final chatWidth = screenWidth * 0.94;
final maxChatHeight = screenHeight * 0.4;
final iconX = screenWidth - widget.floatingIconPosition.dx;
final iconY = screenHeight - widget.floatingIconPosition.dy;
// 1. 先将icon的right/bottom转为屏幕坐标icon左下角
var iconPosition = widget.floatingIconPosition;
final iconLeft = screenWidth - iconPosition.dx - widget.iconSize;
final iconBottom = iconPosition.dy;
final iconTop = iconBottom + widget.iconSize;
final iconCenterX = iconLeft + widget.iconSize / 2;
// 判断FloatingIcon在屏幕的哪一边
final isOnRightSide = iconX < screenWidth / 2;
@@ -96,18 +104,31 @@ class _PartScreenState extends State<PartScreen> {
leftPadding = screenWidth - chatWidth - (screenWidth - chatWidth) * 0.1;
}
// 计算垂直位置,确保聊天框在图标上方
double bottomPadding = iconY + widget.iconSize + 20;
// 优先尝试放在icon上方
double chatBottom = iconTop + 12; // 聊天框底部距离icon顶部12px
double chatTop = screenHeight - chatBottom - maxChatHeight;
// 确保聊天框不会超出屏幕
final minBottomPadding = screenHeight * 0.15;
final maxBottomPadding = screenHeight - maxChatHeight - 50;
bottomPadding = bottomPadding.clamp(minBottomPadding, maxBottomPadding);
// 如果上方空间不足则放在icon下方
if (chatTop < 0) {
chatBottom = iconBottom - 12 - maxChatHeight / 2; // 聊天框顶部距离icon底部12px
// 如果下方也不足,则贴底
if (chatBottom < 0) {
chatBottom = 20; // 距离底部20px
}
}
// // 计算垂直位置,确保聊天框在图标上方
// double bottomPadding = iconY + widget.iconSize + 20;
//
// // 确保聊天框不会超出屏幕
// final minBottomPadding = screenHeight * 0.15;
// final maxBottomPadding = screenHeight - maxChatHeight - 50;
// bottomPadding = bottomPadding.clamp(minBottomPadding, maxBottomPadding);
return EdgeInsets.only(
left: leftPadding,
right: screenWidth - leftPadding - chatWidth,
bottom: bottomPadding,
bottom: chatBottom,
);
}