Как получить Context в Jetpack Compose (Kotlin)
Правильный способ получения Context в Jetpack Compose
В Jetpack Compose для доступа к Context используется композиционный локальный объект LocalContext. Это часть системы CompositionLocal, которая предоставляет значения, автоматически распространяемые вниз по дереву композиции. Не нужно передавать Context вручную через параметры — это нарушило бы принципы декларативного подхода и усложнило бы код.
Вот как правильно получить Context в вашем примере с Toast:
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import android.widget.Toast
import androidx.compose.material3.Button
import androidx.compose.material3.Text
@Composable
fun MyScreen() {
// Получаем Context через LocalContext
val context = LocalContext.current
Button(onClick = {
Toast.makeText(context, "Нажато!", Toast.LENGTH_SHORT).show()
}) {
Text("Показать Toast")
}
}
Пояснение работы LocalContext
LocalContext.current возвращает объект Context, который соответствует текущей Activity или Fragment, в котором размещён ваш Composable. В большинстве случаев это Activity-контекст, которого достаточно для запуска новых Activity, доступа к ресурсам и системным сервисам.
Доступ к ресурсам
С помощью Context вы можете получать строки, цвета, размеры и другие ресурсы:
@Composable
fun Greeting() {
val context = LocalContext.current
val appName = context.getString(R.string.app_name)
val primaryColor = context.getColor(R.color.primary)
Text(
text = "Добро пожаловать в $appName",
color = Color(primaryColor)
)
}
Работа с системными сервисами
Для доступа к сервисам, таким как ConnectivityManager, используйте тот же Context:
@Composable
fun NetworkStatus() {
val context = LocalContext.current
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
// Далее можно проверять статус сети
}
Разница с LocalLifecycleOwner
LocalLifecycleOwner.current возвращает объект LifecycleOwner (обычно Activity или Fragment), который позволяет подписываться на жизненный цикл. Это другой инструмент — он не заменяет Context. Используйте его, когда вам нужно реагировать на события жизненного цикла (например, запускать корутину при старте Activity).
Важные замечания
- Не храните Context в переменных вне композиции. Это может привести к утечкам памяти. Используйте
LocalContext.currentтолько внутри @Composable функций или лямбд. - Для ViewModel Context не нужен — используйте
AndroidViewModel, если требуется Application контекст. - Для startActivity используйте
Intent(context, TargetActivity::class.java)— это работает корректно с Activity-контекстом. - В Preview (аннотация @Preview) LocalContext может быть недоступен или возвращать mock-контекст. Для Preview лучше не использовать Context-зависимые операции.
Альтернативный подход: передача Context через параметры
Хотя это не рекомендуется, вы можете передать Context в качестве параметра, если ваш Composable используется только в одном месте. Но это нарушает переиспользуемость и усложняет тестирование:
@Composable
fun MyScreen(context: Context) {
Button(onClick = {
Toast.makeText(context, "Нажато!", Toast.LENGTH_SHORT).show()
}) { ... }
}
Используйте LocalContext.current — это стандартный идиоматичный способ в Jetpack Compose. Он оптимизирован под реактивное обновление и не создаёт проблем с памятью при правильном использовании.
Использование remember для оптимизации
Если вы получаете Context внутри лямбды (например, в onClick), можно использовать remember, чтобы избежать повторного чтения при каждой рекомпозиции. Это особенно полезно, если Context нужен в нескольких местах одного Composable:
@Composable
fun MyScreen() {
val context = remember { LocalContext.current }
Button(onClick = {
Toast.makeText(context, "Кнопка 1", Toast.LENGTH_SHORT).show()
}) {
Text("Кнопка 1")
}
Button(onClick = {
Toast.makeText(context, "Кнопка 2", Toast.LENGTH_SHORT).show()
}) {
Text("Кнопка 2")
}
}
remember гарантирует, что Context будет получен только один раз за время жизни Composable в композиции. Это не обязательно для производительности (LocalContext.current — лёгкая операция), но делает код чище и явно показывает, что Context не меняется.
Для Activity-специфичных задач
Если вам нужно получить саму Activity (например, для вызова finish() или startActivityForResult), используйте приведение типа:
val activity = LocalContext.current as? AppCompatActivity
activity?.finish() // Закрыть Activity
Будьте осторожны с таким приведением — оно может выбросить исключение, если Context не является Activity. Проверяйте тип через оператор is или используйте as? для безопасного приведения.