feat: Implement automatic keyboard hiding on screen exit for improved user experience
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
package com.rosetta.messenger.ui.components
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
|
||||
/**
|
||||
* 🎹 Контроллер клавиатуры для Rosetta Messenger
|
||||
*
|
||||
* Использует современный WindowInsetsController (API 30+) для мгновенного
|
||||
* скрытия клавиатуры. Решает проблему "залипания" клавиатуры при навигации.
|
||||
*
|
||||
* Использование:
|
||||
* ```kotlin
|
||||
* @Composable
|
||||
* fun MyScreen() {
|
||||
* // Автоматически скрывает клавиатуру при выходе с экрана
|
||||
* HideKeyboardOnDispose()
|
||||
*
|
||||
* // Или получите контроллер для ручного управления
|
||||
* val keyboard = rememberKeyboardController()
|
||||
* Button(onClick = { keyboard.hide() }) { Text("Hide") }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class KeyboardManager(
|
||||
private val view: View,
|
||||
private val context: Context
|
||||
) {
|
||||
/**
|
||||
* Скрыть клавиатуру мгновенно (WindowInsetsController - API 30+)
|
||||
*/
|
||||
fun hide() {
|
||||
// WindowInsetsController - самый быстрый способ (API 30+)
|
||||
ViewCompat.getWindowInsetsController(view)?.hide(WindowInsetsCompat.Type.ime())
|
||||
}
|
||||
|
||||
/**
|
||||
* Показать клавиатуру
|
||||
*/
|
||||
fun show() {
|
||||
ViewCompat.getWindowInsetsController(view)?.show(WindowInsetsCompat.Type.ime())
|
||||
}
|
||||
|
||||
/**
|
||||
* Скрыть клавиатуру с fallback для старых устройств
|
||||
*/
|
||||
fun hideWithFallback() {
|
||||
// Сначала пробуем WindowInsetsController
|
||||
ViewCompat.getWindowInsetsController(view)?.hide(WindowInsetsCompat.Type.ime())
|
||||
|
||||
// Fallback через InputMethodManager
|
||||
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
|
||||
imm?.hideSoftInputFromWindow(view.windowToken, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверить, видна ли клавиатура
|
||||
*/
|
||||
fun isVisible(): Boolean {
|
||||
val insets = ViewCompat.getRootWindowInsets(view)
|
||||
return insets?.isVisible(WindowInsetsCompat.Type.ime()) == true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Composable для получения KeyboardManager
|
||||
*/
|
||||
@Composable
|
||||
fun rememberKeyboardController(): KeyboardManager {
|
||||
val view = LocalView.current
|
||||
val context = LocalContext.current
|
||||
return remember(view, context) { KeyboardManager(view, context) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔥 Автоматически скрывает клавиатуру при выходе с экрана (onDispose)
|
||||
*
|
||||
* Добавьте в начало любого Composable экрана с клавиатурой:
|
||||
* ```kotlin
|
||||
* @Composable
|
||||
* fun ChatScreen() {
|
||||
* HideKeyboardOnDispose()
|
||||
* // ... остальной UI
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
@OptIn(ExperimentalComposeUiApi::class)
|
||||
@Composable
|
||||
fun HideKeyboardOnDispose() {
|
||||
val keyboard = rememberKeyboardController()
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
// WindowInsetsController - мгновенное скрытие
|
||||
keyboard.hide()
|
||||
// Fallback для Compose
|
||||
keyboardController?.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔥 Расширенная версия с очисткой фокуса
|
||||
*
|
||||
* Использовать когда нужно также сбросить фокус с TextField
|
||||
*/
|
||||
@OptIn(ExperimentalComposeUiApi::class)
|
||||
@Composable
|
||||
fun HideKeyboardAndClearFocusOnDispose() {
|
||||
val keyboard = rememberKeyboardController()
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
// Сбрасываем фокус
|
||||
focusManager.clearFocus(force = true)
|
||||
// WindowInsetsController - мгновенное скрытие
|
||||
keyboard.hide()
|
||||
// Fallback для Compose
|
||||
keyboardController?.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension function для View - скрыть клавиатуру мгновенно
|
||||
*/
|
||||
fun View.hideKeyboardNow() {
|
||||
ViewCompat.getWindowInsetsController(this)?.hide(WindowInsetsCompat.Type.ime())
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension function для View - показать клавиатуру
|
||||
*/
|
||||
fun View.showKeyboardNow() {
|
||||
ViewCompat.getWindowInsetsController(this)?.show(WindowInsetsCompat.Type.ime())
|
||||
}
|
||||
Reference in New Issue
Block a user