اندروید ۱۶ (API سطح ۳۶) یه تغییر اساسی آورده که شاید در نگاه اول ساده بهنظر بیاد،
ولی واقعاً تأثیر زیادی روی ظاهر و رفتار اپهامون میذاره — مخصوصاً برای ما که با React Native کار میکنیم.
بیاین باهم ببینیم دقیقاً چی عوض شده، چرا مهمه، و چطور میتونیم باهاش کنار بیایم.
از اندروید ۱۵ (API 35) گوگل تصمیم گرفت که اپها باید edge-to-edge بشن.
یعنی محتوای اپ از بالا تا پایینِ واقعی صفحه (زیر نوار وضعیت و نوار ناوبری) کشیده بشه.
ولی اون موقع یه راه فرار وجود داشت — اگه توی تم اپ مینوشتی:
<item name="windowOptOutEdgeToEdgeEnforcement">true</item>
میتونستی بگی:
"نه گوگل! من نمیخوام محتوای اپم تا لبه بره، بذار پایینش فاصله باشه."
اما حالا توی Android 16 گوگل گفته:
تمومه. دیگه نمیتونی از edge-to-edge فرار کنی.
اون attribute بهطور کامل حذف و بیاثر شده.
یعنی اپت کل صفحه رو میگیره — از زیر نوار وضعیت تا زیر gesture bar (نوار پایین گوشی).
خود سیستم UI مثل نوار پایین یا بالای گوشی روی محتوای تو میافته، نه اینکه براش فضا نگه داره.
یه نگاه تصویری:
قدیما (Android 13 و پایینتر):
+--------------------------+ | Status Bar (24px) | |--------------------------| | App Content | |--------------------------| | Navigation Bar (48px) | +--------------------------+
الان (Android 16):
+--------------------------+ | Status Bar (overlay) | |--------------------------| | App Content (زیر status bar) | |--------------------------| | Gesture Bar (overlay) | +--------------------------+
یعنی محتوای اپت تا ته صفحه کشیده میشه
و اگه خودت فاصلهی لازم رو ندی، مثلاً متن یا دکمههات ممکنه برن زیر gesture bar.
توی React Native خیلی وقتها برای تشخیص ارتفاع صفحه از این دو استفاده میکنیم:
Dimensions.get('screen') Dimensions.get('window')
قبلاً فرق بین این دوتا معمولاً ارتفاع نوار ناوبری (Navigation Bar) بود.
اما از اندروید ۱۶ به بعد، چون اپ تا لبه میره، این دوتا یکی میشن.
یعنی این تیکه کد:
const screenHeight = Dimensions.get('screen').height; const windowHeight = Dimensions.get('window').height; const navBarHeight = screenHeight - windowHeight;
دیگه بهجای ۴۸ یا ۲۴، عدد صفر برمیگردونه 😅
و در نتیجه کلی از تنظیمات ظاهریمون مثل bottom sheet یا modal اشتباه کار میکنن.
من برای اینکه این مشکل رو توی کل پروژه بهصورت یکدست حل کنم، یه ساختار مرکزی طراحی کردم.
اول باید ارتفاع نوار وضعیت (StatusBar) و نوار ناوبری (NavigationBar) رو بگیریم:
NAVIGATION_BAR_HEIGHT: initialWindowMetrics?.insets?.bottom, STATUS_BAR_HEIGHT: StatusBar.currentHeight,
بعد دو بخش اصلی توی فایل کانفیگ ساختم:
SCREEN_METADATA → اطلاعات مربوط به وضعیت صفحه و دستگاه (مثل notch، fullscreen و API level)
OffsetCalculator → تابعی که با توجه به نوع کامپوننت و موقعیت (بالا/پایین) مقدار فاصله رو محاسبه میکنه
{ API_LEVEL: 36, SCREEN_METADATA: { HAS_NOTCH: DeviceInfo.hasNotch(), STATUS_BAR_HEIGHT: StatusBar.currentHeight, OS_NAVIGATION_BAR_HEIGHT: initialWindowMetrics?.insets?.bottom, IS_FULL_SCREEN: API_LEVEL > 34 ? initialWindowMetrics?.insets?.bottom < 48 : true, }, }
این اطلاعات به من کمک میکنه تا در همهجا بدون نگرانی از جزئیات دستگاه، بهصورت یکدست با layout کار کنم.
حالا یه تابع نوشتم که دوتا ورودی میگیره:
component: اسم بخشی از اپ (مثلاً drawer، tabbar، chat و غیره)
section: کدوم قسمت (بالا یا پایین)
نمونه سادهش:
export default function OffsetCalculator(component = '', section = 'bottom') { const { API_LEVEL, SCREEN_METADATA } = AppConfig; const { HAS_NOTCH, IS_FULL_SCREEN, OS_NAVIGATION_BAR_HEIGHT } = SCREEN_METADATA; let offset = 0; switch (component) { case 'drawer': offset = section === 'top' ? (HAS_NOTCH ? AppSizes[15] : 0) : (IS_FULL_SCREEN ? AppSizes[15] : AppSizes[45]); break; case 'tabBarContainer': if (API_LEVEL > 34) { offset = IS_FULL_SCREEN ? -OS_NAVIGATION_BAR_HEIGHT / 1.5 : OS_NAVIGATION_BAR_HEIGHT / 1.2; } else { offset = IS_FULL_SCREEN ? -OS_NAVIGATION_BAR_HEIGHT * 2.5 : -OS_NAVIGATION_BAR_HEIGHT * 1.2; } break; // ... و همینطور برای سایر کامپوننتها } return offset; }
حالا هر جای اپ (از Splash گرفته تا Chat و Drawer)، فقط کافیه این تابع رو صدا بزنم تا فاصله مناسب خودش رو حساب کنه.
✅ فقط یک نقطه مرکزی برای تنظیم Offsetها
✅ راحت برای آپدیت در نسخههای آینده Android
✅ سازگار با گوشیهای دارای Notch یا بدون اون
✅ یکپارچگی کامل بین Android 14، 15 و 16
✅ دیگه لازم نیست برای هر کامپوننت جداگانه safe area بنویسی
تغییر Edge-to-Edge در Android 16 فقط یه تغییر ظاهری نیست — یه تحول ساختاری در نحوه تعامل اپ با سیستم UI محسوب میشه.
با مدیریت برنامهنویسیشده و متمرکز Offsetها، میتونی اپ React Native خودت رو در برابر نسخههای آینده هم آیندهنگر و مقاوم کنی.
دفعه بعدی که دیدی یه دکمه یا تببار چسبیده به Gesture Bar، نترس 😄
فقط OffsetCalculator رو صدا بزن!