From 30fc460d08500cc30152922f606eb053ce7df374 Mon Sep 17 00:00:00 2001 From: cogwheel0 <172976095+cogwheel0@users.noreply.github.com> Date: Mon, 8 Sep 2025 01:15:31 +0530 Subject: [PATCH] fix: keyboard close on modal opens --- .../chat/providers/chat_providers.dart | 3 ++ lib/features/chat/views/chat_page.dart | 15 ++++++- .../chat/widgets/modern_chat_input.dart | 39 +++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/lib/features/chat/providers/chat_providers.dart b/lib/features/chat/providers/chat_providers.dart index 664d478..fe4c473 100644 --- a/lib/features/chat/providers/chat_providers.dart +++ b/lib/features/chat/providers/chat_providers.dart @@ -36,6 +36,9 @@ final prefilledInputTextProvider = StateProvider((ref) => null); // Trigger to request focus on the chat input (increment to signal) final inputFocusTriggerProvider = StateProvider((ref) => 0); +// Whether the chat composer currently has focus +final composerHasFocusProvider = StateProvider((ref) => false); + class ChatMessagesNotifier extends StateNotifier> { final Ref _ref; StreamSubscription? _messageStream; diff --git a/lib/features/chat/views/chat_page.dart b/lib/features/chat/views/chat_page.dart index 4004333..283cee9 100644 --- a/lib/features/chat/views/chat_page.dart +++ b/lib/features/chat/views/chat_page.dart @@ -1533,12 +1533,25 @@ class _ChatPageState extends ConsumerState { WidgetRef ref, List models, ) { + // Ensure keyboard is closed before presenting modal + final hadFocus = ref.read(composerHasFocusProvider); + try { + FocusManager.instance.primaryFocus?.unfocus(); + SystemChannels.textInput.invokeMethod('TextInput.hide'); + } catch (_) {} showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (context) => _ModelSelectorSheet(models: models, ref: ref), - ); + ).whenComplete(() { + if (!mounted) return; + if (hadFocus) { + // Bump focus trigger to restore composer focus + IME + final cur = ref.read(inputFocusTriggerProvider); + ref.read(inputFocusTriggerProvider.notifier).state = cur + 1; + } + }); } // TODO: Implement chat options when needed diff --git a/lib/features/chat/widgets/modern_chat_input.dart b/lib/features/chat/widgets/modern_chat_input.dart index 31c0629..7b03d5c 100644 --- a/lib/features/chat/widgets/modern_chat_input.dart +++ b/lib/features/chat/widgets/modern_chat_input.dart @@ -250,6 +250,10 @@ class _ModernChatInputState extends ConsumerState WidgetsBinding.instance.addPostFrameCallback((_) { if (!mounted || _isDeactivated) return; final hasFocus = _focusNode.hasFocus; + // Publish composer focus state + try { + ref.read(composerHasFocusProvider.notifier).state = hasFocus; + } catch (_) {} if (hasFocus) { if (!_isExpanded) _setExpanded(true); } else { @@ -277,6 +281,9 @@ class _ModernChatInputState extends ConsumerState @override void dispose() { + try { + ref.read(composerHasFocusProvider.notifier).state = false; + } catch (_) {} _controller.dispose(); _focusNode.dispose(); _expandController.dispose(); @@ -1286,7 +1293,13 @@ class _ModernChatInputState extends ConsumerState void _showAttachmentOptions() { HapticFeedback.selectionClick(); final prevCanRequest = _focusNode.canRequestFocus; + final wasFocused = _focusNode.hasFocus; _focusNode.canRequestFocus = false; + // Ensure keyboard is closed before presenting modal + try { + FocusScope.of(context).unfocus(); + SystemChannels.textInput.invokeMethod('TextInput.hide'); + } catch (_) {} showModalBottomSheet( context: context, backgroundColor: Colors.transparent, @@ -1361,6 +1374,16 @@ class _ModernChatInputState extends ConsumerState ).whenComplete(() { if (mounted) { _focusNode.canRequestFocus = prevCanRequest; + if (wasFocused && widget.enabled) { + if (!_isExpanded) _setExpanded(true); + WidgetsBinding.instance.addPostFrameCallback((_) { + if (!mounted) return; + _ensureFocusedIfEnabled(); + try { + SystemChannels.textInput.invokeMethod('TextInput.show'); + } catch (_) {} + }); + } } }); } @@ -1368,7 +1391,13 @@ class _ModernChatInputState extends ConsumerState void _showUnifiedToolsModal() { HapticFeedback.selectionClick(); final prevCanRequest = _focusNode.canRequestFocus; + final wasFocused = _focusNode.hasFocus; _focusNode.canRequestFocus = false; + // Ensure keyboard is closed before presenting modal + try { + FocusScope.of(context).unfocus(); + SystemChannels.textInput.invokeMethod('TextInput.hide'); + } catch (_) {} showModalBottomSheet( context: context, backgroundColor: Colors.transparent, @@ -1376,6 +1405,16 @@ class _ModernChatInputState extends ConsumerState ).whenComplete(() { if (mounted) { _focusNode.canRequestFocus = prevCanRequest; + if (wasFocused && widget.enabled) { + if (!_isExpanded) _setExpanded(true); + WidgetsBinding.instance.addPostFrameCallback((_) { + if (!mounted) return; + _ensureFocusedIfEnabled(); + try { + SystemChannels.textInput.invokeMethod('TextInput.show'); + } catch (_) {} + }); + } } }); }