From 8589b3b4d72a8e09dbbe7c7deca8111c66b2857c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?lollipopkit=F0=9F=8F=B3=EF=B8=8F=E2=80=8D=E2=9A=A7?= =?UTF-8?q?=EF=B8=8F?= <10864310+lollipopkit@users.noreply.github.com> Date: Wed, 14 Jan 2026 15:15:33 +0800 Subject: [PATCH] opt.: add a btn to minimize ai dialog (#1004) * opt.: add a btn to minimize ai dialog Fixes #1003 * opt. * opt. --- lib/view/page/ssh/page/ask_ai.dart | 155 +++++++++++++++++------------ 1 file changed, 90 insertions(+), 65 deletions(-) diff --git a/lib/view/page/ssh/page/ask_ai.dart b/lib/view/page/ssh/page/ask_ai.dart index 12dbe13f..62a91de3 100644 --- a/lib/view/page/ssh/page/ask_ai.dart +++ b/lib/view/page/ssh/page/ask_ai.dart @@ -84,6 +84,7 @@ class _AskAiSheetState extends ConsumerState<_AskAiSheet> { String? _streamingContent; String? _error; bool _isStreaming = false; + bool _isMinimized = false; @override void initState() { @@ -387,12 +388,23 @@ class _AskAiSheetState extends ConsumerState<_AskAiSheet> { Widget build(BuildContext context) { final theme = Theme.of(context); final bottomPadding = MediaQuery.viewInsetsOf(context).bottom; + final heightFactor = _isMinimized ? 0.18 : 0.85; - return FractionallySizedBox( - heightFactor: 0.85, + return TweenAnimationBuilder( + tween: Tween(end: heightFactor), + duration: const Duration(milliseconds: 200), + curve: Curves.easeOutCubic, + builder: (context, animatedHeightFactor, child) { + return ClipRect( + child: FractionallySizedBox( + heightFactor: animatedHeightFactor, + child: child, + ), + ); + }, child: SafeArea( - child: Column( - children: [ + child: Column( + children: [ Padding( padding: const EdgeInsets.fromLTRB(16, 16, 16, 0), child: Row( @@ -402,83 +414,96 @@ class _AskAiSheetState extends ConsumerState<_AskAiSheet> { if (_isStreaming) const SizedBox(height: 16, width: 16, child: CircularProgressIndicator(strokeWidth: 2)), const Spacer(), + IconButton( + icon: Icon(_isMinimized ? Icons.unfold_more : Icons.unfold_less), + tooltip: libL10n.fold, + onPressed: () { + FocusManager.instance.primaryFocus?.unfocus(); + setState(() { + _isMinimized = !_isMinimized; + }); + }, + ), IconButton(icon: const Icon(Icons.close), onPressed: () => Navigator.of(context).pop()), ], ), ), - Expanded( - child: Scrollbar( - controller: _scrollController, - child: ListView( + if (!_isMinimized) ...[ + Expanded( + child: Scrollbar( controller: _scrollController, - padding: const EdgeInsets.fromLTRB(16, 12, 16, 12), - children: [ - Text(context.l10n.askAiSelectedContent, style: theme.textTheme.titleMedium), - const SizedBox(height: 6), - CardX( - child: Padding( - padding: const EdgeInsets.all(12), - child: SelectableText( - widget.selection, - style: const TextStyle(fontFamily: 'monospace'), - ), - ), - ), - const SizedBox(height: 16), - Text(context.l10n.askAiConversation, style: theme.textTheme.titleMedium), - const SizedBox(height: 6), - ..._buildConversationWidgets(context, theme), - if (_error != null) ...[ - const SizedBox(height: 16), + child: ListView( + controller: _scrollController, + padding: const EdgeInsets.fromLTRB(16, 12, 16, 12), + children: [ + Text(context.l10n.askAiSelectedContent, style: theme.textTheme.titleMedium), + const SizedBox(height: 6), CardX( child: Padding( padding: const EdgeInsets.all(12), - child: Text(_error!, style: TextStyle(color: theme.colorScheme.error)), + child: SelectableText( + widget.selection, + style: const TextStyle(fontFamily: 'monospace'), + ), ), ), + const SizedBox(height: 16), + Text(context.l10n.askAiConversation, style: theme.textTheme.titleMedium), + const SizedBox(height: 6), + ..._buildConversationWidgets(context, theme), + if (_error != null) ...[ + const SizedBox(height: 16), + CardX( + child: Padding( + padding: const EdgeInsets.all(12), + child: Text(_error!, style: TextStyle(color: theme.colorScheme.error)), + ), + ), + ], + if (_isStreaming) ...[const SizedBox(height: 16), const LinearProgressIndicator()], + const SizedBox(height: 16), ], - if (_isStreaming) ...[const SizedBox(height: 16), const LinearProgressIndicator()], - const SizedBox(height: 16), - ], + ), ), ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(16, 8, 16, 0), - child: Text( - context.l10n.askAiDisclaimer, - style: theme.textTheme.bodySmall?.copyWith( - color: theme.colorScheme.error, - fontWeight: FontWeight.bold, - ), - textAlign: TextAlign.center, - ), - ), - Padding( - padding: EdgeInsets.fromLTRB(16, 8, 16, 16 + bottomPadding), - child: Row( - children: [ - Expanded( - child: Input( - controller: _inputController, - minLines: 1, - maxLines: 4, - hint: context.l10n.askAiFollowUpHint, - action: TextInputAction.send, - onSubmitted: (_) => _sendMessage(), - ), - ), - const SizedBox(width: 12), - Btn.icon( - onTap: _isStreaming || _inputController.text.trim().isEmpty ? null : _sendMessage, - icon: const Icon(Icons.send, size: 18), + Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 16, 0), + child: Text( + context.l10n.askAiDisclaimer, + style: theme.textTheme.bodySmall?.copyWith( + color: theme.colorScheme.error, + fontWeight: FontWeight.bold, + ), + textAlign: TextAlign.center, ), + ), + Padding( + padding: EdgeInsets.fromLTRB(16, 8, 16, 16 + bottomPadding), + child: Row( + children: [ + Expanded( + child: Input( + controller: _inputController, + minLines: 1, + maxLines: 4, + hint: context.l10n.askAiFollowUpHint, + action: TextInputAction.send, + onSubmitted: (_) => _sendMessage(), + ), + ), + const SizedBox(width: 12), + Btn.icon( + onTap: _isStreaming || _inputController.text.trim().isEmpty ? null : _sendMessage, + icon: const Icon(Icons.send, size: 18), + ), + ], + ).cardx, + ), + ] else + const SizedBox(height: 8), ], - ).cardx, + ), ), - ], - ), - ), ); } }