fix: keyboard close on modal opens

This commit is contained in:
cogwheel0
2025-09-08 01:15:31 +05:30
parent c78d1448b8
commit 30fc460d08
3 changed files with 56 additions and 1 deletions

View File

@@ -36,6 +36,9 @@ final prefilledInputTextProvider = StateProvider<String?>((ref) => null);
// Trigger to request focus on the chat input (increment to signal) // Trigger to request focus on the chat input (increment to signal)
final inputFocusTriggerProvider = StateProvider<int>((ref) => 0); final inputFocusTriggerProvider = StateProvider<int>((ref) => 0);
// Whether the chat composer currently has focus
final composerHasFocusProvider = StateProvider<bool>((ref) => false);
class ChatMessagesNotifier extends StateNotifier<List<ChatMessage>> { class ChatMessagesNotifier extends StateNotifier<List<ChatMessage>> {
final Ref _ref; final Ref _ref;
StreamSubscription? _messageStream; StreamSubscription? _messageStream;

View File

@@ -1533,12 +1533,25 @@ class _ChatPageState extends ConsumerState<ChatPage> {
WidgetRef ref, WidgetRef ref,
List<Model> models, List<Model> 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( showModalBottomSheet(
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
builder: (context) => _ModelSelectorSheet(models: models, ref: ref), 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 // TODO: Implement chat options when needed

View File

@@ -250,6 +250,10 @@ class _ModernChatInputState extends ConsumerState<ModernChatInput>
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted || _isDeactivated) return; if (!mounted || _isDeactivated) return;
final hasFocus = _focusNode.hasFocus; final hasFocus = _focusNode.hasFocus;
// Publish composer focus state
try {
ref.read(composerHasFocusProvider.notifier).state = hasFocus;
} catch (_) {}
if (hasFocus) { if (hasFocus) {
if (!_isExpanded) _setExpanded(true); if (!_isExpanded) _setExpanded(true);
} else { } else {
@@ -277,6 +281,9 @@ class _ModernChatInputState extends ConsumerState<ModernChatInput>
@override @override
void dispose() { void dispose() {
try {
ref.read(composerHasFocusProvider.notifier).state = false;
} catch (_) {}
_controller.dispose(); _controller.dispose();
_focusNode.dispose(); _focusNode.dispose();
_expandController.dispose(); _expandController.dispose();
@@ -1286,7 +1293,13 @@ class _ModernChatInputState extends ConsumerState<ModernChatInput>
void _showAttachmentOptions() { void _showAttachmentOptions() {
HapticFeedback.selectionClick(); HapticFeedback.selectionClick();
final prevCanRequest = _focusNode.canRequestFocus; final prevCanRequest = _focusNode.canRequestFocus;
final wasFocused = _focusNode.hasFocus;
_focusNode.canRequestFocus = false; _focusNode.canRequestFocus = false;
// Ensure keyboard is closed before presenting modal
try {
FocusScope.of(context).unfocus();
SystemChannels.textInput.invokeMethod('TextInput.hide');
} catch (_) {}
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
@@ -1361,6 +1374,16 @@ class _ModernChatInputState extends ConsumerState<ModernChatInput>
).whenComplete(() { ).whenComplete(() {
if (mounted) { if (mounted) {
_focusNode.canRequestFocus = prevCanRequest; _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<ModernChatInput>
void _showUnifiedToolsModal() { void _showUnifiedToolsModal() {
HapticFeedback.selectionClick(); HapticFeedback.selectionClick();
final prevCanRequest = _focusNode.canRequestFocus; final prevCanRequest = _focusNode.canRequestFocus;
final wasFocused = _focusNode.hasFocus;
_focusNode.canRequestFocus = false; _focusNode.canRequestFocus = false;
// Ensure keyboard is closed before presenting modal
try {
FocusScope.of(context).unfocus();
SystemChannels.textInput.invokeMethod('TextInput.hide');
} catch (_) {}
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
@@ -1376,6 +1405,16 @@ class _ModernChatInputState extends ConsumerState<ModernChatInput>
).whenComplete(() { ).whenComplete(() {
if (mounted) { if (mounted) {
_focusNode.canRequestFocus = prevCanRequest; _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 (_) {}
});
}
} }
}); });
} }