14 β Expo Router asoslari¶
β¬ οΈ Oldingi: 13 β Custom hooks va loyiha tuzilishi Β· π Kitob boshi Β· Keyingi: 15 β Tabs va Drawer navigatsiya β‘οΈ
Bu bobda: ilovani bir nechta ekranli qilamiz. Navigatsiya nimaligini, Expo Routerning "fayl = marshrut" (file-based routing) g'oyasini,
_layout.tsxorqali Stack navigator o'rnatishni o'rganamiz. So'ng ekranlar orasidaLink(deklarativ) vauseRouter(imperativ) bilan o'tishni, sarlavha/header'ni sozlashni,Slot,usePathnameva+not-found(404) sahifasini ko'rib chiqamiz. Oxirida 3 ekranli to'liq ilova yig'amiz β kodi jonli Expo loyihadatscbilan tekshirilgan.
Kirish: bitta ekran kam keladi¶
Shu paytgacha biz yozgan ilovalarning hammasi bitta ekrandan iborat edi: hisoblagich, ro'yxat, forma. Lekin haqiqiy ilovalarni eslang. Telegram'ni ochasiz β chatlar ro'yxati ko'rinadi. Bir chatni bosasiz β yangi ekran ochiladi, suhbat ko'rinadi. Profil rasmini bosasiz β yana boshqa ekran. Instagram, bank ilovasi, do'kon ilovasi β barchasi ko'p ekranli.
Ekrandan ekranga o'tish, orqaga qaytish, qaysidir ekranni yopib boshqasini ochish β bularning hammasi navigatsiya deyiladi. Bu bob β kitobimizning yangi katta bo'limining boshlanishi: navigatsiya. Undan oldingi 13 bobda biz bitta ekran ichida ishladik; endi ilovani xonalarga ajratamiz.
Hayotiy o'xshatish. Ilovani uy deb tasavvur qiling. Hozirgacha biz bitta xonali uyda yashardik β hamma narsa bir joyda. Lekin haqiqiy uyda bir nechta xona bor: yotoqxona, oshxona, mehmonxona, hammom. Siz xonadan xonaga eshiklar orqali o'tasiz. Navigatsiya β aynan shu eshiklar tizimi: qaysi xonada turibsiz, qaysi xonaga o'tasiz, qanday qilib orqaga (chiqib ketgan xonangizga) qaytasiz. Har bir ekran β bitta xona; navigator β xonalarni bog'lab turuvchi yo'lak va eshiklar.
1. Navigatsiya nima¶
Navigatsiya (navigation) β bu ilovadagi bir ekrandan boshqasiga o'tish jarayoni va uni boshqaradigan tizim. Foydalanuvchi tugma bossa yoki biror element ustiga bossa β yangi ekran ochiladi; orqaga tugmasini bossa β avvalgi ekranga qaytadi.
Tipik oqim:
- Bosh ekran (chatlar ro'yxati, mahsulotlar, postlar) β foydalanuvchi bir elementni bosadi β
- Detal ekran (bitta chat, bitta mahsulot) β "Sozlamalar" tugmasini bosadi β
- Sozlamalar ekrani β orqaga β detal β orqaga β bosh.
Ko'p ekranli ilovada uchta narsa kerak bo'ladi:
- Ekranlarni e'lon qilish β qaysi ekranlar mavjud (bosh, profil, sozlamalar...).
- Ekranlar orasida o'tish β qaysi tugma qaysi ekranni ochadi.
- Tarixni saqlash β qayerdan kelganini eslab, orqaga qaytarish.
Bularning hammasini qo'lda yozish juda murakkab. Shuning uchun navigatsiya kutubxonasi ishlatamiz. Expo loyihalarida bu β Expo Router.
Eslatma
React Native dunyosida ikkita asosiy yondashuv bor: React Navigation (an'anaviy, kodda ekranlarni qo'lda ro'yxatlaysiz) va Expo Router (zamonaviy, fayllarga asoslangan). Expo Router aslida React Navigation ustiga qurilgan β ya'ni ichkarida o'sha kuchli dvigatel ishlaydi, lekin sizga ancha soddaroq, fayllarga asoslangan usulni beradi. Bu kitob β Expo asosida, shuning uchun biz Expo Router'ni o'rganamiz.
2. Expo Router: fayl = marshrut¶
Expo Router'ning bosh g'oyasi juda sodda va kuchli: har bir fayl β bitta marshrut (ekran). Bu file-based routing ("fayllarga asoslangan marshrutlash") deb ataladi.
Hayotiy o'xshatish. Buni web saytga o'xshating. Web saytda har bir sahifa β alohida fayl/manzil:
sayt.uz/(bosh sahifa),sayt.uz/haqida(haqida sahifasi),sayt.uz/aloqa(aloqa). Manzil qatoriga yo'lni yozasiz β mos sahifa ochiladi. Expo Router ilovangizni xuddi shunday qiladi: papkadagi har bir fayl β bitta "sahifa" (ekran), fayl nomi esa uning manzili (marshruti) bo'ladi.
Demak, yangi ekran qo'shish uchun biror joyda ro'yxatga olishingiz shart emas β shunchaki app/ papkasiga yangi fayl yaratasiz, va u avtomatik marshrutga aylanadi. Fayl nomi marshrutga qanday mos kelishini ko'ramiz:
Asosiy qoidalar:
| Fayl | Marshrut (manzil) | Izoh |
|---|---|---|
app/index.tsx |
/ |
index β papkaning bosh ekrani (ildiz) |
app/profil.tsx |
/profil |
Fayl nomi = marshrut nomi |
app/sozlamalar.tsx |
/sozlamalar |
|
app/profil/index.tsx |
/profil |
Papka + index = papkaning ildizi |
app/profil/tahrir.tsx |
/profil/tahrir |
Ichki (joylashgan) marshrut |
app/_layout.tsx |
β (marshrut emas) | Layout β ekranlarni o'raydi (3-bo'lim) |
app/+not-found.tsx |
har qanday topilmagan yo'l | 404 sahifasi (9-bo'lim) |
Diqqat qiling: index.tsx β bu maxsus nom. U turgan papkaning asosiy (ildiz) manzilini bildiradi. app/index.tsx β / (ilova ochilganda birinchi ko'rinadigan ekran). app/profil/index.tsx β /profil.
Ehtiyot bo'ling
_ (pastki chiziq) bilan boshlangan fayllar (_layout.tsx) va + bilan boshlanganlari (+not-found.tsx) β maxsus fayllar, ular oddiy marshrut emas. Oddiy ekran fayllarini oddiy nom bilan ataing (profil.tsx, sozlamalar.tsx), katta harf yoki bo'sh joy ishlatmang.
app/yokisrc/app/? SDK 56 standart shabloni marshrut ildizinisrc/app/papkasiga joylashtiradi (app/emas). Qoidalar aynan bir xil β faqat papka joyi boshqacha. Bu bobda qisqalik uchun bizapp/yozamiz; agar loyihangizdasrc/app/bo'lsa, shu papka ichida ishlayvering. Ikkalasida hamapp/index.tsxβsrc/app/index.tsxbir xil/marshrutiga aylanadi.
3. _layout.tsx β ekranlarni o'rab turuvchi qatlam¶
Faqat ekran fayllari yetarli emas. Bizga ekranlarni bog'lab, ular orasida o'tishni boshqaradigan narsa kerak β navigator. Navigatorni biz _layout.tsx faylida belgilaymiz.
_layout.tsx (layout fayli) β bu o'zi ekran emas. U turgan papkadagi barcha ekranlarni o'rab (qoplab) turuvchi qobiq. Unda navigator (Stack, Tabs va h.k.) joylashadi.
Hayotiy o'xshatish.
_layout.tsxβ bu ramka kabi. Tasavvur qiling, sizda bir nechta rasm (ekran) bor. Layout β bularni ushlab turadigan albom yoki devordagi ramkalar tizimi. Rasmlar almashadi, lekin ramka (yuqoridagi sarlavha, orqaga tugmasi, ranglar) o'sha-o'sha turadi. Layout har bir ekranni shu umumiy "qobiq" ichida ko'rsatadi.
Eng muhimi β ildiz layout: app/_layout.tsx. U butun ilovani o'raydi. Har bir ilovada kamida bitta _layout.tsx bo'ladi. Ichki papkalar ham o'z _layout.tsx'iga ega bo'lishi mumkin (masalan, app/profil/_layout.tsx β faqat profil bo'limini o'raydi).
app/
ββ _layout.tsx β ildiz layout (BUTUN ilovani o'raydi, navigator shu yerda)
ββ index.tsx β / (bosh ekran)
ββ profil.tsx β /profil
ββ sozlamalar.tsx β /sozlamalar
Endi layout ichiga qanaqa navigator qo'yishni ko'ramiz.
4. Stack navigator β asosiy navigator¶
Eng ko'p ishlatiladigan navigator β Stack ("dasta"). U ekranlarni ustma-ust joylashtiradi.
Hayotiy o'xshatish. Stack β bu stol ustidagi qog'ozlar dastasi yoki kitob sahifalari kabi. Yangi ekranga o'tganingizda β yangi qog'oz ustiga qo'yiladi (avvalgisi pastda turaveradi). Orqaga qaytganingizda β eng ustki qog'oz olib tashlanadi va ostidagi avvalgi ekran qaytadan ko'rinadi. "Stack" so'zi shuning uchun β qog'ozlar dastalanib boradi.
Diagrammada ko'rinib turibdiki:
- push β yangi ekranni dasta ustiga qo'yadi (oldinga o'tish).
- back β eng ustki ekranni olib tashlaydi (orqaga qaytish).
- Stack avtomatik ravishda yuqorida header (sarlavha paneli) va unda orqaga tugmasi (β yoki "<") chizadi.
Stack'ni ildiz layout'da o'rnatamiz. expo-routerdan Stackni import qilamiz:
// app/_layout.tsx β eng oddiy Stack
import { Stack } from 'expo-router';
export default function RootLayout() {
return <Stack />;
}
Shuncha! Bo'sh <Stack /> ham ishlaydi β u app/ papkadagi barcha ekranlarni avtomatik topadi va har biriga header + orqaga tugmasi beradi. Lekin odatda biz ekranlarni <Stack.Screen> bilan sozlaymiz β masalan, har biriga sarlavha (title) beramiz:
// app/_layout.tsx β ekranlarni sozlash bilan
import { Stack } from 'expo-router';
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="index" options={{ title: 'Bosh' }} />
<Stack.Screen name="profil" options={{ title: 'Profil' }} />
<Stack.Screen name="sozlamalar" options={{ title: 'Sozlamalar' }} />
</Stack>
);
}
Bu yerda:
<Stack.Screen name="index" .../>βnameqaysi faylga tegishli ekanini bildiradi (indexβindex.tsx). Kengaytmasiz (.tsxsiz) yoziladi.options={{ title: 'Bosh' }}β o'sha ekranning sozlamalari.titleβ header'da ko'rinadigan sarlavha.
Bo'sh Stack ham yetarli
Agar <Stack.Screen> yozmasangiz ham ilova ishlaydi β Expo Router ekranlarni o'zi topadi. <Stack.Screen>ni faqat sozlash kerak bo'lganda yozasiz (sarlavha, header rangi, header'ni yashirish va h.k.). Sarlavha berilmasa, fayl nomi sarlavha bo'ladi.
Endi ildiz layout va uchta ekran fayli bo'lsa β ilova ishga tushganda index.tsx ("Bosh") ekrani ko'rinadi, yuqorida "Bosh" sarlavhasi bilan. Faqat o'tish (navigatsiya) qoldi.
5. Ekranlar orasida o'tish¶
Bir ekrandan boshqasiga o'tishning ikki usuli bor:
- Deklarativ β
<Link>komponenti bilan (havola, web'dagi<a>kabi). - Imperativ β
useRouterhook'i bilan (kod ichidarouter.push(...)).
Ikkalasini ham ko'ramiz β qachon qaysi birini ishlatishni ham aytamiz.
5.1 Link β deklarativ o'tish¶
Link β bu bosilganda boshqa ekranga olib o'tadigan havola komponenti. Web saytdagi link (<a href="...">) kabi, lekin native ilova uchun. expo-routerdan import qilinadi:
// app/index.tsx
import { View, Text, StyleSheet } from 'react-native';
import { Link } from 'expo-router';
export default function Bosh() {
return (
<View style={styles.box}>
<Text style={styles.sarlavha}>Bosh ekran</Text>
{/* Bosilganda /profil ekraniga o'tadi */}
<Link href="/profil" style={styles.link}>
Profilga o'tish
</Link>
<Link href="/sozlamalar" style={styles.link}>
Sozlamalarga o'tish
</Link>
</View>
);
}
const styles = StyleSheet.create({
box: { flex: 1, alignItems: 'center', justifyContent: 'center', gap: 16, backgroundColor: '#f8fafc' },
sarlavha: { fontSize: 22, fontWeight: '700', color: '#1e293b' },
link: { fontSize: 18, color: '#4f46e5', fontWeight: '600' },
});
href="/profil" β qaysi marshrutga o'tishni bildiradi (/ bilan boshlanadi). Link matni esa ekranda ko'k havola bo'lib ko'rinadi. Bosilsa β /profil ekrani ochiladi.
asChild β istalgan komponentni havola qilish. Link o'zicha oddiy matn ko'rsatadi. Lekin ko'pincha biz tugma (Pressable) ni bosilganda o'tadigan qilmoqchimiz. Buning uchun asChild propini ishlatamiz β u "havola xususiyatini ichimdagi bolaga ber" deydi:
import { View, Text, Pressable, StyleSheet } from 'react-native';
import { Link } from 'expo-router';
export default function Bosh() {
return (
<View style={styles.box}>
{/* Link xususiyatini Pressable'ga beramiz */}
<Link href="/profil" asChild>
<Pressable style={styles.tugma}>
<Text style={styles.tugmaMatn}>Profilga o'tish</Text>
</Pressable>
</Link>
</View>
);
}
const styles = StyleSheet.create({
box: { flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor: '#f8fafc' },
tugma: { backgroundColor: '#4f46e5', paddingVertical: 12, paddingHorizontal: 24, borderRadius: 10 },
tugmaMatn: { color: '#ffffff', fontWeight: '600', fontSize: 16 },
});
Hayotiy o'xshatish.
asChildβ bu "men sehrli kuchimni (havola bo'lish) o'zimga emas, ichimdagi bolaga beraman" deyish kabi.Linko'zi ko'rinmas bo'lib qoladi, lekin ichidagiPressablebutunlay havolaga aylanadi β bosilsa o'tadi.
Maslahat
Link qachon eng qulay? β Statik, oldindan ma'lum o'tishlar uchun: "Profil", "Haqida", "Sozlamalar" kabi tugmalar har doim aynan o'sha ekranga olib boradi. Kod soddaroq va o'qiladi. Aytmoqchi, Link web ilovada haqiqiy <a> teg bo'lib chiqadi β bu SEO va "yangi oynada ochish" uchun foydali.
5.2 useRouter β imperativ o'tish¶
Ba'zan o'tishni kod ichida, biror shartdan keyin bajarish kerak bo'ladi. Masalan: foydalanuvchi formani to'ldirdi β tekshirdik β to'g'ri bo'lsa keyingi ekranga o'tamiz. Bu yerda Link qulay emas, chunki o'tish foydalanuvchi bosishidan emas, bizning mantiqimizdan kelib chiqadi.
Bunday holatda useRouter hook'ini ishlatamiz. U router obyektini beradi, undagi metodlar bilan o'tishni boshqaramiz:
import { View, Text, Pressable, StyleSheet } from 'react-native';
import { useRouter } from 'expo-router';
export default function Profil() {
const router = useRouter(); // router obyektini olamiz
return (
<View style={styles.box}>
<Text style={styles.sarlavha}>Profil ekrani</Text>
{/* push β yangi ekranni dasta ustiga qo'shadi */}
<Pressable style={styles.tugma} onPress={() => router.push('/sozlamalar')}>
<Text style={styles.tugmaMatn}>Sozlamalar</Text>
</Pressable>
{/* back β bir qadam orqaga qaytadi */}
<Pressable style={styles.tugma} onPress={() => router.back()}>
<Text style={styles.tugmaMatn}>Orqaga</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
box: { flex: 1, alignItems: 'center', justifyContent: 'center', gap: 12, backgroundColor: '#f8fafc' },
sarlavha: { fontSize: 22, fontWeight: '700', color: '#1e293b' },
tugma: { backgroundColor: '#4f46e5', paddingVertical: 12, paddingHorizontal: 24, borderRadius: 10 },
tugmaMatn: { color: '#ffffff', fontWeight: '600', fontSize: 16 },
});
routerning eng muhim uchta metodi:
| Metod | Nima qiladi | Qachon ishlatiladi |
|---|---|---|
router.push('/yol') |
Yangi ekranni dasta ustiga qo'shadi (orqaga qaytish mumkin) | Oddiy "oldinga o'tish" β detal ekran, ichki sahifa |
router.replace('/yol') |
Joriy ekranni boshqasiga almashtiradi (orqaga qaytib bo'lmaydi) | Login β bosh ekran; orqaga qaytish kerak bo'lmaganda |
router.back() |
Bir qadam orqaga qaytadi | "Bekor qilish", maxsus orqaga tugmasi |
pushvareplacefarqi.pushβ yangi sahifa qo'shadi, dasta o'sadi:Bosh β Profil β Sozlamalar(orqaga bossangiz Profil'ga qaytasiz).replaceβ joriy sahifani o'rnini almashtiradi, dasta o'smaydi. Eng tipik misol: login ekrani. Foydalanuvchi tizimga kirgach,router.replace('/')bilan bosh ekranga o'tasiz β endi orqaga bossa, login'ga qaytmasligi kerak (chunki allaqachon kirgan).
Maslahat β Link yoki useRouter?
Statik, foydalanuvchi bevosita bosadigan o'tishlar uchun β Link (oddiyroq, deklarativ). Mantiq ichida, shartdan keyin o'tish uchun β useRouter (router.push/replace/back). Ko'p hollarda Link yetarli; useRouter esa "formani saqlagandan keyin", "tekshiruvdan keyin" kabi vaziyatlarda kerak bo'ladi.
6. Header (sarlavha) sozlamalari¶
Stack navigator har bir ekran tepasiga header chizadi β sarlavha, orqaga tugmasi va boshqa elementlar bilan. Bu header'ni options orqali sozlaymiz. Eng ko'p ishlatiladigan sozlamalar:
| Sozlama | Nima qiladi |
|---|---|
title |
Header'dagi sarlavha matni |
headerShown |
false β header'ni butunlay yashiradi |
headerStyle |
Header foni stili ({ backgroundColor: '#...' }) |
headerTintColor |
Sarlavha va orqaga tugmasi rangi |
headerRight |
Header o'ng tomoniga komponent (tugma, ikonka) qo'yish |
Sozlashning ikki joyi bor.
6.1 Statik β layout ichida¶
Eng keng tarqalgan usul β _layout.tsxda, <Stack.Screen> ichida. Ekran sarlavhasi o'zgarmas bo'lsa, shu yerda yozing:
// app/_layout.tsx
import { Stack } from 'expo-router';
export default function RootLayout() {
return (
<Stack
// Barcha ekranlarga umumiy sozlama (screenOptions)
screenOptions={{
headerStyle: { backgroundColor: '#4f46e5' }, // header foni indigo
headerTintColor: '#ffffff', // sarlavha/tugma oq
}}
>
<Stack.Screen name="index" options={{ title: 'Bosh' }} />
<Stack.Screen name="profil" options={{ title: 'Profil' }} />
{/* Bu ekranda header butunlay yo'q */}
<Stack.Screen name="sozlamalar" options={{ headerShown: false }} />
</Stack>
);
}
E'tibor bering: <Stack>dagi screenOptions β barcha ekranlarga umumiy sozlama (bu yerda header rangi). Har bir <Stack.Screen>dagi options esa faqat o'sha ekranga tegishli (sarlavha, header'ni yashirish).
6.2 Dinamik β ekran ichida¶
Ba'zan sarlavha ekran ma'lumotidan kelib chiqadi (masalan, mahsulot nomi, foydalanuvchi ismi). Bunday holatda ekran faylining ichida <Stack.Screen> qo'yib, options beramiz:
// app/profil.tsx β sarlavhani ekran ichida belgilash
import { View, Text, StyleSheet } from 'react-native';
import { Stack } from 'expo-router';
export default function Profil() {
const ism = 'Oqil'; // odatda props/ma'lumotdan keladi
return (
<View style={styles.box}>
{/* Bu ekranning sarlavhasini dinamik beramiz */}
<Stack.Screen options={{ title: `${ism}ning profili` }} />
<Text style={styles.matn}>Salom, {ism}!</Text>
</View>
);
}
const styles = StyleSheet.create({
box: { flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor: '#f8fafc' },
matn: { fontSize: 20, color: '#1e293b' },
});
Bu yerda <Stack.Screen options={...} /> ekran ichida (komponent JSX'i ichida) yozildi β name kerak emas, chunki u allaqachon shu ekran. Sarlavha "Oqilning profili" bo'lib chiqadi.
Header'ga o'ng tomondan tugma qo'shish (headerRight) ham shu tarzda:
import { Stack } from 'expo-router';
import { Pressable, Text } from 'react-native';
// ... komponent ichida:
<Stack.Screen
options={{
title: 'Sozlamalar',
headerRight: () => (
<Pressable onPress={() => console.log('Saqlandi')}>
<Text style={{ color: '#ffffff', fontWeight: '600' }}>Saqlash</Text>
</Pressable>
),
}}
/>
headerRight β funksiya bo'lishi kerak (komponent qaytaradi), shuning uchun () => (...) yoziladi.
Yana bir usul β useNavigation().setOptions
Sarlavhani kod oqimida (masalan, ma'lumot yuklangandan keyin) o'zgartirish kerak bo'lsa, useNavigation hook'ini ishlatasiz:
import { useNavigation } from 'expo-router';
import { useLayoutEffect } from 'react';
// ...
const navigation = useNavigation();
useLayoutEffect(() => {
navigation.setOptions({ title: 'Yangilangan sarlavha' });
}, [navigation]);
<Stack.Screen options={...} /> usuli oddiyroq β odatda shuni ishlating.
7. Typed routes β TypeScript yo'llarni tekshiradi¶
SDK 56'da Expo Router'ning yoqimli xususiyati bor: typed routes ("tiplangan marshrutlar"). Bu nima degani? β href va router.push(...) ichidagi yo'llar TypeScript bilan tekshiriladi. Agar mavjud bo'lmagan yo'l yozsangiz, kod yozayotgan paytdayoq xato chiqadi.
import { Link, useRouter } from 'expo-router';
// To'g'ri β bu marshrutlar mavjud:
<Link href="/profil">Profil</Link>;
const router = useRouter();
router.push('/sozlamalar'); // β
// Xato β bunday marshrut yo'q:
router.push('/profill'); // β TypeScript xato beradi (imloda xato)
Hayotiy o'xshatish. Typed routes β bu manzilni xaritada tekshiruvchi kabi. Avval (typed routes'siz) noto'g'ri yo'l yozsangiz, ilova ishlayotganda "bunday sahifa yo'q" deb xato berardi β ya'ni xatoni faqat sinab ko'rganda topardingiz. Endi esa TypeScript yo'lni darrov tekshiradi: imlo xatosini kod yozayotgan paytda ko'rsatadi. Xato ilovaga yetib bormaydi.
Bu xususiyat SDK 56 shablonida avtomatik yoniq. Siz uchun hech narsa qilish shart emas β shunchaki noto'g'ri yo'l yozsangiz, muharrir (VS Code) qizil chiziq bilan ogohlantiradi.
Maslahat
Typed routes tufayli marshrut nomini o'zgartirsangiz (profil.tsx β mening-profil.tsx), eski href="/profil" darrov xato bo'lib chiqadi β barcha bog'liq joylarni topib tuzatasiz. Bu katta ilovada juda ko'p vaqt tejaydi.
8. Slot va usePathname¶
Yana ikkita foydali narsa bor β soddaroq layout va joriy yo'lni bilish.
8.1 Slot β eng oddiy layout¶
Ba'zan layout'da navigator (Stack, Tabs) kerak emas β faqat ekranlar o'rnini belgilash kerak bo'ladi. Bunday holatda <Slot /> ishlatamiz. U "joriy ekran shu yerga joylashsin" deydi, lekin header/orqaga tugmasi qo'shmaydi.
// app/_layout.tsx β eng oddiy layout (navigatorsiz)
import { View, Text, StyleSheet } from 'react-native';
import { Slot } from 'expo-router';
export default function RootLayout() {
return (
<View style={styles.qobiq}>
<Text style={styles.brend}>Mening Ilovam</Text>
{/* Joriy ekran shu yerga chiziladi */}
<Slot />
</View>
);
}
const styles = StyleSheet.create({
qobiq: { flex: 1, paddingTop: 60, backgroundColor: '#f8fafc' },
brend: { fontSize: 16, fontWeight: '700', color: '#4f46e5', textAlign: 'center', marginBottom: 8 },
});
Hayotiy o'xshatish.
Slotβ bu rasm ramkasidagi bo'sh joy kabi. Ramka (layout) bir xil turadi β yuqorida brend nomi bilan; ichidagi rasm (ekran) esa navigatsiyaga qarab almashadi.Slotβ "rasm shu teshikka tushadi" degan belgi. Stack/Tabs esaSlot'ning kuchaytirilgan versiyasi β ular header, tab paneli ham qo'shadi.
Slot vs Stack
<Slot /> β eng yalang'och layout: faqat joy, hech qanday navigatsiya bezagi yo'q. <Stack /> β Slot ustiga header + orqaga tugmasi + o'tish animatsiyasini qo'shadi. Boshlovchilar uchun odatda <Stack /> kerak bo'ladi; <Slot /> maxsus holatlarda (masalan, butunlay o'z dizayningizdagi qobiq) ishlatiladi.
8.2 usePathname β joriy yo'lni bilish¶
usePathname hook'i hozir qaysi marshrutda turganingizni qaytaradi (string ko'rinishida: '/', '/profil'...). Bu, masalan, qaysidir elementni "faol" qilib ko'rsatish uchun foydali:
import { Text } from 'react-native';
import { usePathname } from 'expo-router';
export default function Bosh() {
const yol = usePathname(); // masalan: '/'
return <Text>Hozir turgan yo'lingiz: {yol}</Text>;
}
9. 404 sahifasi β +not-found¶
Foydalanuvchi (yoki kod) mavjud bo'lmagan yo'lga o'tib qolsa nima bo'ladi? Buning uchun maxsus fayl bor: app/+not-found.tsx. Yo'l topilmasa, Expo Router avtomatik shu ekranni ko'rsatadi (web saytlardagi "404 β sahifa topilmadi" kabi).
// app/+not-found.tsx
import { View, Text, StyleSheet } from 'react-native';
import { Link, Stack } from 'expo-router';
export default function TopilmadiSahifasi() {
return (
<View style={styles.box}>
<Stack.Screen options={{ title: 'Topilmadi' }} />
<Text style={styles.katta}>404</Text>
<Text style={styles.matn}>Bunday sahifa mavjud emas.</Text>
<Link href="/" style={styles.link}>
Bosh ekranga qaytish
</Link>
</View>
);
}
const styles = StyleSheet.create({
box: { flex: 1, alignItems: 'center', justifyContent: 'center', gap: 12, backgroundColor: '#f8fafc' },
katta: { fontSize: 48, fontWeight: '800', color: '#dc2626' },
matn: { fontSize: 16, color: '#475569' },
link: { fontSize: 16, color: '#4f46e5', fontWeight: '600' },
});
+not-found.tsx β + bilan boshlanadigan maxsus fayl. Foydalanuvchini chalkash holatda qoldirmaslik uchun unda bosh ekranga qaytish havolasini qo'yish yaxshi odat.
Maslahat
+not-found.tsx'ni har doim qo'shing. Bu β foydalanuvchi tajribasi (UX) uchun muhim: noto'g'ri yo'lga tushib qolgan odam "ilova buzildimi?" deb o'ylamaydi, balki tushunarli xabar va chiqish yo'lini ko'radi.
10. To'liq misol β 3 ekranli ilova¶
Endi bobdagi hamma narsani birlashtiramiz: ildiz layout'da Stack, uchta ekran, Link va router.push bilan o'tish, header sozlash. Quyidagi kodlar jonli Expo loyihada tsc --noEmit bilan tekshirilgan (typed routes bilan).
Avval ildiz layout β navigator va header ranglari:
// app/_layout.tsx
import { Stack } from 'expo-router';
export default function RootLayout() {
return (
<Stack
screenOptions={{
headerStyle: { backgroundColor: '#4f46e5' },
headerTintColor: '#ffffff',
headerTitleStyle: { fontWeight: '700' },
}}
>
<Stack.Screen name="index" options={{ title: 'Bosh' }} />
<Stack.Screen name="detal" options={{ title: 'Detal' }} />
<Stack.Screen name="sozlamalar" options={{ title: 'Sozlamalar' }} />
</Stack>
);
}
Endi bosh ekran (index.tsx) β Link (deklarativ) va router.push (imperativ) ikkalasini ham ko'rsatamiz:
// app/index.tsx
import { View, Text, Pressable, StyleSheet } from 'react-native';
import { Link, useRouter, usePathname } from 'expo-router';
export default function Bosh() {
const router = useRouter();
const yol = usePathname();
return (
<View style={styles.box}>
<Text style={styles.sarlavha}>Bosh ekran</Text>
<Text style={styles.kichik}>Joriy yo'l: {yol}</Text>
{/* 1-usul: Link (deklarativ) β Pressable bilan */}
<Link href="/detal" asChild>
<Pressable style={styles.tugma}>
<Text style={styles.tugmaMatn}>Detalga o'tish (Link)</Text>
</Pressable>
</Link>
{/* 2-usul: useRouter (imperativ) */}
<Pressable style={styles.tugma} onPress={() => router.push('/sozlamalar')}>
<Text style={styles.tugmaMatn}>Sozlamalar (router.push)</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
box: { flex: 1, alignItems: 'center', justifyContent: 'center', gap: 14, backgroundColor: '#f8fafc' },
sarlavha: { fontSize: 24, fontWeight: '800', color: '#1e293b' },
kichik: { fontSize: 14, color: '#94a3b8' },
tugma: { backgroundColor: '#4f46e5', paddingVertical: 12, paddingHorizontal: 24, borderRadius: 10 },
tugmaMatn: { color: '#ffffff', fontWeight: '600', fontSize: 16 },
});
Detal ekrani (detal.tsx) β dinamik sarlavha (<Stack.Screen> ekran ichida) va orqaga qaytish:
// app/detal.tsx
import { View, Text, Pressable, StyleSheet } from 'react-native';
import { Stack, useRouter } from 'expo-router';
export default function Detal() {
const router = useRouter();
return (
<View style={styles.box}>
{/* Sarlavhani shu ekran ichida o'zgartiramiz */}
<Stack.Screen options={{ title: 'Mahsulot detali' }} />
<Text style={styles.sarlavha}>Detal ekrani</Text>
<Text style={styles.matn}>Bu yerda mahsulot haqida ma'lumot.</Text>
<Pressable style={styles.tugma} onPress={() => router.back()}>
<Text style={styles.tugmaMatn}>Orqaga qaytish</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
box: { flex: 1, alignItems: 'center', justifyContent: 'center', gap: 12, backgroundColor: '#f8fafc' },
sarlavha: { fontSize: 22, fontWeight: '700', color: '#1e293b' },
matn: { fontSize: 16, color: '#475569' },
tugma: { backgroundColor: '#0ea5e9', paddingVertical: 12, paddingHorizontal: 24, borderRadius: 10 },
tugmaMatn: { color: '#ffffff', fontWeight: '600', fontSize: 16 },
});
Sozlamalar ekrani (sozlamalar.tsx) β header'ga "Saqlash" tugmasi (headerRight) va bosh ekranga replace:
// app/sozlamalar.tsx
import { View, Text, Pressable, StyleSheet } from 'react-native';
import { Stack, useRouter } from 'expo-router';
export default function Sozlamalar() {
const router = useRouter();
return (
<View style={styles.box}>
<Stack.Screen
options={{
title: 'Sozlamalar',
// Header o'ng tomoniga tugma
headerRight: () => (
<Pressable onPress={() => console.log('Saqlandi')}>
<Text style={styles.headerTugma}>Saqlash</Text>
</Pressable>
),
}}
/>
<Text style={styles.sarlavha}>Sozlamalar ekrani</Text>
{/* replace β bosh ekranga, orqaga qaytib bo'lmaydi */}
<Pressable style={styles.tugma} onPress={() => router.replace('/')}>
<Text style={styles.tugmaMatn}>Boshga qaytish (replace)</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
box: { flex: 1, alignItems: 'center', justifyContent: 'center', gap: 12, backgroundColor: '#f8fafc' },
sarlavha: { fontSize: 22, fontWeight: '700', color: '#1e293b' },
tugma: { backgroundColor: '#16a34a', paddingVertical: 12, paddingHorizontal: 24, borderRadius: 10 },
tugmaMatn: { color: '#ffffff', fontWeight: '600', fontSize: 16 },
headerTugma: { color: '#ffffff', fontWeight: '600', fontSize: 15 },
});
Va nihoyat 404 sahifasi (+not-found.tsx) β yuqoridagi 9-bo'limdagi kod. Endi papka tuzilishi shunday:
app/
ββ _layout.tsx β Stack navigator + umumiy header rangi
ββ index.tsx β / (Bosh β Link va router.push)
ββ detal.tsx β /detal (dinamik sarlavha + back)
ββ sozlamalar.tsx β /sozlamalar (headerRight + replace)
ββ +not-found.tsx β 404 sahifasi
Bu kichik ilovada bobdagi barcha tushunchalar bor:
- File-based routing β har bir fayl bitta ekran/marshrut.
- Stack navigator β
_layout.tsxda, avtomatik header va orqaga tugmasi. - Link (deklarativ,
asChildbilan) va useRouter (push/replace/back) β ikki o'tish usuli. - Header sozlash β statik (
screenOptions/Stack.Screenlayout'da) va dinamik (ekran ichida),headerRight. - usePathname β joriy yo'l, +not-found β 404.
Tekshirib ko'ring
Bu fayllarni o'z loyihangizning app/ (yoki src/app/) papkasiga tering va npx expo start bilan oching. Sinab ko'ring: Bosh β Detal β orqaga; Bosh β Sozlamalar β "replace" β endi orqaga tugmasi yo'qligini sezing. So'ng manzilni qo'lda buzib (masalan, router.pushga noto'g'ri yo'l yozib) typed routes qanday xato berishini, va +not-found qachon ko'rinishini kuzating.
Xulosa¶
- Navigatsiya β ilovadagi bir ekrandan boshqasiga o'tish va uni boshqaradigan tizim. Ekran = "xona", navigator = "eshiklar tizimi".
- Expo Router = file-based routing: har bir fayl bitta marshrut.
app/index.tsxβ/,app/profil.tsxβ/profil,app/profil/index.tsxβ/profil. (SDK 56 shablonisrc/app/ishlatadi β qoidalar aynan bir xil.) _layout.tsxβ ekranlarni o'rab turuvchi qatlam; navigator shu yerda joylashadi. Ildizapp/_layout.tsxbutun ilovani o'raydi.- Stack navigator (
import { Stack } from 'expo-router') β eng asosiy navigator: ekranlar ustma-ust ("dasta"), avtomatik header + orqaga tugmasi.<Stack.Screen name="..." options={{ title }} />bilan sozlanadi. - O'tishning ikki usuli:
Link(deklarativ β<Link href="/profil">,asChildbilan tugmaga aylanadi) vauseRouter(imperativ βrouter.pushyangi ekran,router.replacealmashtirish,router.backorqaga). - Header
optionsorqali sozlanadi:title,headerShown,headerStyle,headerTintColor,headerRight. Statik β layout'da; dinamik β ekran ichida<Stack.Screen options={...} />yokiuseNavigation().setOptions. - Typed routes (SDK 56) β
href/router.pushyo'llari TypeScript bilan tekshiriladi; noto'g'ri yo'l kod yozayotganda xato beradi. <Slot />β eng oddiy layout (navigatorsiz, faqat joy);usePathname()β joriy yo'l;app/+not-found.tsxβ 404 sahifasi.
Amaliy mashqlar¶
-
Ikki ekranli ilova (oson).
app/papkasidaindex.tsx(Bosh) vahaqida.tsx(Haqida) ekranlarini yarating. Ildiz_layout.tsxda Stack o'rnating, har ikkala ekrangatitlebering. Bosh ekrandan Haqida'ga<Link href="/haqida">bilan o'ting, Haqida'da orqaga tugmasi avtomatik ishlashini ko'ring. -
Link vs router.push (oson). Yuqoridagi ilovaga uchinchi ekran (
aloqa.tsx) qo'shing. Bosh ekranda Haqida'gaLinkbilan, Aloqa'gauseRouter+router.pushbilan o'ting. Ikkala usul bir xil natija berishini, lekin kodi farqli ekanini his qiling. -
Header'ni sozlash (o'rta).
_layout.tsxdascreenOptionsorqali barcha header'ni bitta rangda qiling (fon va matn rangi). Bitta ekranda esaheaderShown: falsebilan header'ni butunlay yashiring. So'ng boshqa ekrandaheaderRightorqali o'ng tomonga "Yopish" tugmasini qo'ying (bosilgandarouter.back()qilsin). -
replace bilan oqim (o'rta). "Soxta login" oqimi yasang:
login.tsxekrani va Bosh ekran. Login ekranida "Kirish" tugmasi bo'lsin β bosilgandarouter.replace('/')qilib bosh ekranga o'tsin. Bosh ekranga o'tgach, orqaga tugmasi login'ga qaytmasligini tekshiring. So'ng bunirouter.pushbilan almashtirib, farqni kuzating. -
Dinamik sarlavha + 404 (qiyin).
mahsulot.tsxekrani yarating: ichidauseStatebilan mahsulot nomini saqlang va<Stack.Screen options={{ title: nom }} />bilan sarlavhani shu nomdan chiqaring (nom o'zgarsa, header ham o'zgaradimi β sinab ko'ring). Keyinapp/+not-found.tsxqo'shing varouter.pushorqali ataylab mavjud bo'lmagan yo'lga o'tib (typed routes o'chirilgan holatda yoki'/yoqyol' as anybilan), 404 ekranining ishlashini ko'ring. 404'da bosh ekranga qaytish havolasini qo'ying.
β¬ οΈ Oldingi: 13 β Custom hooks va loyiha tuzilishi Β· π Kitob boshi Β· Keyingi: 15 β Tabs va Drawer navigatsiya β‘οΈ