fix: keyboard close on modal opens
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 (_) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user