React Native и Android 16: обновление SDK, которое кажется простым, пока не сломается интерфейс
Переход на Android 16 (API 36) в React Native выглядит как простое изменение в конфигурации, но на практике приводит к серьезным проблемам с пользовательским интерфейсом из-за обязательного режима edge-to-edge.
Сборка приложения после обновления target SDK — это еще не миграция. Настоящая работа начинается, когда ваш интерфейс ломается из-за новых правил отрисовки Android 16.
Для разработчиков на React Native, чьи приложения все еще нацелены на API 35 (Android 15), в обозримом будущем появится обязательный пункт в дорожной карте. С 31 августа 2026 года Google Play будет требовать, чтобы обновления приложений нацеливались на Android 16 (API 36). И хотя конфигурационные изменения выглядят технически простыми, реальный процесс миграции оказывается куда сложнее и полон подводных камней, которые ломают пользовательский интерфейс.
Что меняется в конфигурации?
Техническая сторона обновления действительно несложна. В вашем build.gradle нужно обновить несколько строк:
compileSdkVersion: 35 → 36targetSdkVersion: 35 → 36buildToolsVersion: 35.x → 36.x- Android Gradle Plugin: с версии 8.7.x до 8.9.1+
- Gradle: с 8.9 до 8.11.1+
При этом minSdkVersion, как правило, остается на уровне 24, а обновление самой версии React Native для этого шага может не потребоваться. Первый успешный запуск после таких изменений создает иллюзию, что миграция завершена. Это главная и опасная ошибка.
Настоящая проблема: обязательный edge-to-edge
Главная причина, по которой интерфейс ломается, — это изменение в поведении edge-to-edge в Android 16. Режим теперь принудительно включается, и старый атрибут windowOptOutEdgeToEdgeEnforcement больше не является надежным решением.
Это разрушает фундаментальное допущение, на котором строились многие приложения: операционная система сама позаботится о том, чтобы ваш контент не заезжал под статус-бар или «челку». В новой реальности ваше приложение отрисовывается за системными элементами.
Симптомы, которые вы увидите:
- Заголовки экранов прячутся под статус-бар.
- Контент оказывается под вырезом для камеры (notch).
- Кнопки навигации (назад, домой) становятся труднонажимаемыми.
- Формы авторизации выглядят «съехавшими».
- Попытки исправить это грубыми паддингами приводят к пустым пространствам.
Как правильно решать проблемы с отступами
Типичное желание — добавить отступы от системных панелей (safe-area insets) на каждый экран. Но это ведет к хаосу: если ваш корневой навигатор уже как-то обрабатывает верхний отступ, а каждый экран добавляет свой, вы получите двойные отступы и непредсказуемые промежутки.
Лучшая практика — централизация. Обработка верхнего отступа (top inset) должна быть единой и находиться на уровне корневого роутера/навигатора. Каждый экран не должен сам решать, нужен ли ему отступ сверху. Это создает единую точку ответственности и делает код предсказуемым.
Проблема с цветом статус-бара
Даже если вы решите вопрос с отступами, визуальная часть может вас подвести. Белый «филлер» за статус-баром будет отлично смотреться на белых экранах, но как только откроется, например, цветной экран авторации, сверху появится белая полоса, портящая весь вид.
Обработка системных панелей — это не только про отступы, но и про визуальный контекст. Нужно думать: что должно быть видно за статус-баром? Для экрана входа это может быть брендинговый фон, а для основного приложения — белый. Универсальных решений тут нет, поэтому опять же централизованная обработка на уровне корня проще в поддержке.
Изменения в поведении клавиатуры
Вторая большая проблема касается клавиатуры. Привычная комбинация KeyboardAvoidingView с behavior="padding" и windowSoftInputMode="adjustResize" в сценариях с edge-to-edge перестает работать корректно. Контент может «пря» за клавиатурой, а стандартные методы исправления не помогают.
Это не баг React Native, а следствие того, как Android 16 теперь обрабатывает окно приложения и системные отступы.
Решение: для SDK 35+ и выше (включая 36) лучше обрабатывать отступ от клавиатуры (IME inset) нативно в MainActivity. Суть в том, чтобы слушать WindowInsets, получать нижний отступ от клавиатуры и применять его к корневому контенту. Это гораздо надежнее, чем пытаться решить проблему через стилизованные ScrollView для каждой формы.
Что тестировать после миграции?
После того как приложение собралось и установилось, работа только начинается. Необходим тщательный регрессионный тест:
- Системный интерфейс: проверьте поведение статус-бара, «челки», навигационной панели, жеста «назад», прицельность касаний.
- Клавиатуру и формы: особенно экранные входа, длинные формы, поля паролей.
- Ключевые функции: push-уведомления, deep links, камера, выбор файлов, разрешения.
- Навигацию: вложенные навигаторы, табы, стеки, работу кнопки «назад».
- На устройствах: обязательно тестируйте на устройстве с Android 16/API 36, на одном старом поддерживаемом Android и на устройстве с вырезом (notch). И сделайте быструю проверку на iOS, чтобы убедиться, что изменения не сломали что-то там.
Главный вывод
Миграция на новый target SDK в Android — это больше не просто изменение цифры. Это миграция поведения. Платформа развивается, и наши приложения должны подстраиваться.
Урок этой миграции прост: избегайте чрезмерной инженерии. Вам не нужен редизайн всего приложения. Вам нужно выявить, где Android 16 изменил ваши старые предположения (о системных отступах, о клавиатуре), и исправить эти предположения на правильном уровне — предпочтительно, на уровне платформы (нативный код) или корневого компонента, а не костылями в каждом экране.
Ключ к успешной поддержке production-приложений на React Native — это понимание границ между JavaScript-логикой, возможностями React Native и поведением самой Android-платформы.