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'; class PartScreen extends StatefulWidget { final VoidCallback? onHide; final Offset floatingIconPosition; final double iconSize; const PartScreen({ super.key, this.onHide, required this.floatingIconPosition, required this.iconSize, }); @override State createState() => _PartScreenState(); } class _PartScreenState extends State { final ScrollController _scrollController = ScrollController(); bool _isInitialized = false; int _lastMessageCount = 0; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { final messageService = Provider.of(context, listen: false); messageService.removeNonListeningMessages(); setState(() { _isInitialized = true; }); }); } @override void dispose() { _scrollController.dispose(); super.dispose(); } void _scrollToBottom() { if (_scrollController.hasClients) { _scrollController.animateTo( 0.0, duration: const Duration(milliseconds: 200), curve: Curves.easeOut, ); } } void _openFullScreen() async { widget.onHide?.call(); await Navigator.of(context).push( MaterialPageRoute( builder: (context) => const FullScreen(), ), ); } // 计算PartScreen的位置,使其显示在FloatingIcon附近 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; // 判断FloatingIcon在屏幕的哪一边 final isOnRightSide = iconX < screenWidth / 2; // 计算水平位置 double leftPadding; if (isOnRightSide) { // 图标在右边,聊天框显示在左边 leftPadding = (screenWidth - chatWidth) * 0.1; } else { // 图标在左边,聊天框显示在右边 leftPadding = screenWidth - chatWidth - (screenWidth - chatWidth) * 0.1; } // 计算垂直位置,确保聊天框在图标上方 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, ); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.transparent, body: LayoutBuilder( builder: (context, constraints) { if (!_isInitialized) { return const SizedBox.shrink(); } final position = _calculatePosition(constraints); final double minHeight = constraints.maxHeight * 0.16; final double maxHeight = constraints.maxHeight * 0.4; final double chatWidth = constraints.maxWidth * 0.94; return Stack( children: [ Positioned.fill( child: IgnorePointer( ignoring: false, child: GestureDetector( behavior: HitTestBehavior.translucent, onTap: () { final messageService = context.read(); if (widget.onHide != null) widget.onHide!(); setState(() { _isInitialized = true; }); messageService.abortReply(); messageService.initializeEmpty(); }, child: Container( color: Colors.transparent, ), ), ), ), Positioned( left: position.left, right: position.right, bottom: position.bottom, child: Consumer( builder: (context, messageService, child) { final messageCount = messageService.messages.length; if (messageCount > _lastMessageCount) { WidgetsBinding.instance.addPostFrameCallback((_) { _scrollToBottom(); }); } _lastMessageCount = messageCount; return TweenAnimationBuilder( tween: Tween(begin: 0.8, end: 1.0), duration: const Duration(milliseconds: 200), curve: Curves.easeOutBack, builder: (context, scale, child) { return Transform.scale( scale: scale, child: Container( width: chatWidth, constraints: BoxConstraints( minHeight: minHeight, maxHeight: maxHeight, ), child: ClipRRect( borderRadius: BorderRadius.circular(24), child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 12, sigmaY: 12), child: GradientBackground( colors: const [ Color(0XBF3B0A3F), Color(0xBF0E0E24), Color(0xBF0C0B33), ], borderRadius: BorderRadius.circular(6), child: Stack( children: [ Padding( padding: const EdgeInsets.only( top: 50, left: 6, right: 6, bottom: 30), child: LayoutBuilder( builder: (context, boxConstraints) { return ChatBox( scrollController: _scrollController, messages: messageService.messages, ); }), ), Positioned( top: 6, right: 6, child: IconButton( icon: Image.asset( 'assets/images/open_in_full.png', width: 24, height: 24, package: 'ai_chat_assistant', ), onPressed: _openFullScreen, padding: EdgeInsets.zero, ), ), ], ), ), ), ), ), ); }, ); }, ), ), ], ); }, ), ); } }