From b9ab384fde4c87120d0d07d01d3132a86f3aafea Mon Sep 17 00:00:00 2001 From: "guangfei.zhao" Date: Mon, 22 Sep 2025 17:51:21 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BD=BF=E7=94=A8Overlay=E6=9D=A5=E5=BC=B9?= =?UTF-8?q?=E5=87=BA=E8=81=8A=E5=A4=A9=E7=AA=97=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/screens/main_screen.dart | 3 +- lib/widgets/chat/chat_window_content.dart | 8 -- lib/widgets/chat_popup.dart | 109 ++++++++++------------ 3 files changed, 49 insertions(+), 71 deletions(-) diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index f8b0927..67ced92 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -37,7 +37,8 @@ class _MainScreenState extends State { ), ), // FloatingIcon(), - ChatPopup() + ChatPopup( + ) ], ), ); diff --git a/lib/widgets/chat/chat_window_content.dart b/lib/widgets/chat/chat_window_content.dart index addc59c..07c4e15 100644 --- a/lib/widgets/chat/chat_window_content.dart +++ b/lib/widgets/chat/chat_window_content.dart @@ -27,7 +27,6 @@ class ChatWindowContent extends StatefulWidget { class _ChatWindowContentState extends State { final ScrollController _scrollController = ScrollController(); - bool _isInitialized = false; int _lastMessageCount = 0; @override @@ -36,9 +35,6 @@ class _ChatWindowContentState extends State { WidgetsBinding.instance.addPostFrameCallback((_) { final messageService = Provider.of(context, listen: false); messageService.removeNonListeningMessages(); - setState(() { - _isInitialized = true; - }); }); } @@ -73,10 +69,6 @@ class _ChatWindowContentState extends State { @override Widget build(BuildContext context) { - if (!_isInitialized) { - return const SizedBox.shrink(); - } - final minHeight = widget.minHeight ?? 0; final maxHeight = widget.maxHeight ?? double.infinity; final chatWidth = widget.chatWidth ?? double.infinity; diff --git a/lib/widgets/chat_popup.dart b/lib/widgets/chat_popup.dart index 0a2e979..8b41c42 100644 --- a/lib/widgets/chat_popup.dart +++ b/lib/widgets/chat_popup.dart @@ -1,6 +1,7 @@ import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import '../pages/full_screen.dart'; import '../services/message_service.dart'; import 'chat/chat_window_content.dart'; import 'chat_floating_icon.dart'; @@ -10,8 +11,11 @@ class ChatPopup extends StatefulWidget { final Widget? icon; final Size chatSize; + final ChatFloatingIcon? child; + const ChatPopup({ super.key, + this.child, this.iconSize, this.icon, this.chatSize = const Size(320, 450), @@ -25,7 +29,6 @@ class _ChatPopupState extends State with SingleTickerProviderStateMix OverlayEntry? _overlayEntry; Offset _iconPosition = const Offset(10, 120); final double _iconSizeDefault = 80.0; - bool _isShowingPopup = false; late AnimationController _partScreenAnimationController; @override @@ -35,9 +38,6 @@ class _ChatPopupState extends State with SingleTickerProviderStateMix duration: const Duration(milliseconds: 250), vsync: this, ); - WidgetsBinding.instance.addPostFrameCallback((_) { - _insertOverlay(); - }); } @override @@ -47,35 +47,34 @@ class _ChatPopupState extends State with SingleTickerProviderStateMix super.dispose(); } + void _openFullScreen() async { + final messageService = context.read(); + _removeOverlay(); + + await Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => ChangeNotifierProvider.value( + value: messageService, // 传递同一个单例实例 + child: const FullScreenPage(), + ), + ), + ); + } + void _insertOverlay() { if (_overlayEntry != null) return; _overlayEntry = OverlayEntry( builder: (context) => _buildOverlayContent(), ); Overlay.of(context).insert(_overlayEntry!); - } - - void _removeOverlay() { - _overlayEntry?.remove(); - _overlayEntry = null; - } - - void _showPopup() { - if (_isShowingPopup) return; - setState(() { - _isShowingPopup = true; - }); _partScreenAnimationController.forward(); } - void _hidePopup() { - if (!_isShowingPopup) return; + void _removeOverlay() { + if (_overlayEntry == null) return; _partScreenAnimationController.reverse().then((_) { - if (mounted) { - setState(() { - _isShowingPopup = false; - }); - } + _overlayEntry?.remove(); + _overlayEntry = null; }); } @@ -84,39 +83,29 @@ class _ChatPopupState extends State with SingleTickerProviderStateMix value: MessageService.instance, child: Material( color: Colors.transparent, - child: Stack( - children: [ - // 1. 底层:弹窗内容(只有在显示时才渲染) - if (_isShowingPopup) ...[ - _buildPopupBackground(), - LayoutBuilder( - builder: (context, constraints) { - 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; - // 只在 isInitialized 时返回弹窗,否则返回空 - final chatWindow = ChatWindowContent( + child: LayoutBuilder( + builder: (context, constraints) { + 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: [ + _buildPopupBackground(), + Positioned( + left: position.left, + right: position.right, + bottom: position.bottom, + child: ChatWindowContent( animationController: _partScreenAnimationController, minHeight: minHeight, maxHeight: maxHeight, chatWidth: chatWidth, - ); - if ((chatWindow as dynamic)._isInitialized == false) { - return const SizedBox.shrink(); - } - return Positioned( - left: position.left, - right: position.right, - bottom: position.bottom, - child: chatWindow, - ); - }, - ), - ], - // 2. 顶层:FloatingIcon(始终在最上层,事件不被遮挡) - _buildFloatingIcon(), - ], + ), + ), + ], + ); + }, ), ), ); @@ -127,7 +116,7 @@ class _ChatPopupState extends State with SingleTickerProviderStateMix child: GestureDetector( onTap: () { final messageService = MessageService.instance; - _hidePopup(); + _removeOverlay(); messageService.abortReply(); messageService.initializeEmpty(); }, @@ -142,12 +131,9 @@ class _ChatPopupState extends State with SingleTickerProviderStateMix return ChatFloatingIcon( iconSize: widget.iconSize ?? _iconSizeDefault, icon: widget.icon, - onTap: () { - // 默认点击弹出全屏 - }, onLongPress: () async { final messageService = MessageService.instance; - _showPopup(); + _insertOverlay(); await messageService.startVoiceInput(); }, onLongPressUp: () async { @@ -155,7 +141,7 @@ class _ChatPopupState extends State with SingleTickerProviderStateMix await messageService.stopAndProcessVoiceInput(); final hasMessage = messageService.messages.isNotEmpty; if (!hasMessage) { - _hidePopup(); + _removeOverlay(); } }, onPositionChanged: (position) { @@ -168,7 +154,6 @@ class _ChatPopupState extends State with SingleTickerProviderStateMix ); } - // 计算PartScreen的位置,使其显示在FloatingIcon附近 EdgeInsets _calculatePosition(BoxConstraints constraints) { final screenWidth = constraints.maxWidth; @@ -217,7 +202,7 @@ class _ChatPopupState extends State with SingleTickerProviderStateMix @override Widget build(BuildContext context) { - // 只返回空容器,实际内容都在 Overlay 中 - return const SizedBox.shrink(); + // 只渲染 FloatingIcon,Overlay 弹窗内容在长按时插入 + return _buildFloatingIcon(); } -} \ No newline at end of file +}