Drawer va modal ekranlar
Muammodan boshlaylik
Ikki vazifa:
- Admin panel ilovasi — 9 ta bo'lim. Tab panelga sig'maydi (5 tadan ko'p — 27-dars)
- "Filtr" yoki "Yangi manzil qo'shish" — joriy ishni to'xtatmasdan, ustida ochiladigan ekran. Tugatgach — yopiladi va foydalanuvchi aynan qolgan joyiga qaytadi
Birinchisi — drawer (yon menyu). Ikkinchisi — modal ekran.
Modal ekranlar
11-darsda Modal komponentini ko'rdik. Expo Router da yaxshiroq yo'l bor — modalni alohida ekran sifatida ochish:
app/
├── _layout.tsx
├── (tabs)/...
└── filtr.tsx ← modal sifatida ochiladi
// app/_layout.tsx
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen
name="filtr"
options={{
presentation: 'modal',
title: 'Filtr',
}}
/>
</Stack>
Ochish — oddiy navigatsiya:
<Link href="/filtr">Filtr</Link>
// yoki
router.push('/filtr');
Yopish:
router.back();
// yoki
router.dismiss();
presentation turlari
| Qiymat | Ko'rinishi |
|---|---|
card |
Oddiy stack ekran (standart) |
modal |
Pastdan chiqadi; iOS da orqadagi ekran ko'rinib turadigan "varaq" |
fullScreenModal |
Butun ekranni egallaydi |
formSheet |
iOS: kichikroq varaq; balandlikni sozlash mumkin |
transparentModal |
Shaffof fon — o'z dizayningizdagi oyna uchun |
Komponent Modal dan afzalligi
- O'z URL i bor — deep link bilan ochish mumkin (
mening-dokon://filtr) - Holat va mantiq alohida faylda — asosiy ekran "toza"
- iOS da pastga tortib yopish, Android "orqaga" — avtomatik
- Ichida o'z stack i bo'lishi mumkin (ko'p qadamli forma)
Modaldan natija qaytarish
Filtr tanlandi — katalog ekrani buni bilishi kerak. Eng toza yo'l — URL parametrlari (29-dars) yoki umumiy holat (Zustand, 33-dars):
// app/filtr.tsx
function qollash() {
router.dismissTo({ pathname: '/katalog', params: { minNarx: '100000', toifa: 'telefonlar' } });
}
// app/(tabs)/katalog.tsx
const { minNarx, toifa } = useLocalSearchParams<{ minNarx?: string; toifa?: string }>();
Shaffof modal — o'z dialogingiz
<Stack.Screen name="tasdiqlash" options={{ presentation: 'transparentModal', animation: 'fade', headerShown: false }} />
// app/tasdiqlash.tsx
export default function Tasdiqlash() {
return (
<Pressable style={{ flex: 1, backgroundColor: 'rgba(0,0,0,0.5)', justifyContent: 'center', padding: 24 }} onPress={() => router.back()}>
<Pressable style={{ backgroundColor: 'white', borderRadius: 16, padding: 24, gap: 16 }}>
<Text style={{ fontSize: 18, fontWeight: '700' }}>Buyurtmani bekor qilasizmi?</Text>
{/* tugmalar */}
</Pressable>
</Pressable>
);
}
Drawer — yon menyu
npx expo install @react-navigation/drawer react-native-gesture-handler react-native-reanimated
app/
├── _layout.tsx
└── (drawer)/
├── _layout.tsx ← Drawer
├── index.tsx ← Boshqaruv paneli
├── buyurtmalar.tsx
├── mahsulotlar.tsx
├── mijozlar.tsx
└── sozlamalar.tsx
// app/(drawer)/_layout.tsx
import { Drawer } from 'expo-router/drawer';
import { Ionicons } from '@expo/vector-icons';
export default function DrawerLayout() {
return (
<Drawer
screenOptions={{
drawerActiveTintColor: '#0284c7',
drawerActiveBackgroundColor: '#e0f2fe',
drawerLabelStyle: { marginLeft: -8 },
}}
>
<Drawer.Screen
name="index"
options={{
title: 'Boshqaruv paneli',
drawerIcon: ({ color, size }) => <Ionicons name="speedometer-outline" size={size} color={color} />,
}}
/>
<Drawer.Screen
name="buyurtmalar"
options={{
title: 'Buyurtmalar',
drawerIcon: ({ color, size }) => <Ionicons name="receipt-outline" size={size} color={color} />,
}}
/>
{/* ... */}
</Drawer>
);
}
Drawer avtomatik:
- Sarlavhada "☰" tugmasi
- Ekranning chap chetidan surib ochish
- Faol bo'limni belgilash
Kodda ochish:
import { useNavigation } from 'expo-router';
import { DrawerActions } from '@react-navigation/native';
const navigation = useNavigation();
navigation.dispatch(DrawerActions.openDrawer());
O'z menyu dizayni
import { DrawerContentScrollView, DrawerItemList } from '@react-navigation/drawer';
<Drawer
drawerContent={(props) => (
<DrawerContentScrollView {...props}>
<View style={{ padding: 16, alignItems: 'center', gap: 8 }}>
<Avatar ism="Admin" olcham={64} />
<Text style={{ fontWeight: '700' }}>Admin</Text>
</View>
<DrawerItemList {...props} />
<Pressable onPress={chiqish} style={{ padding: 16 }}>
<Text style={{ color: '#dc2626' }}>Chiqish</Text>
</Pressable>
</DrawerContentScrollView>
)}
>
Tepada profil, o'rtada bandlar, pastda "Chiqish".
Qaysi biri qachon
| Holat | Tanlov |
|---|---|
| 3–5 asosiy bo'lim, tez-tez almashtiriladi | Tabs |
| Ko'p bo'lim, kam almashtiriladi (admin, sozlamalar) | Drawer |
| Joriy ishni to'xtatmaydigan qisqa vazifa (filtr, tanlash, forma) | Modal |
| Ketma-ket chuqurlashish (katalog → mahsulot → sharh) | Stack |
Ko'pincha birga: ildiz Stack → ichida Tabs → har bir tab ichida Stack, ustida modallar.
[!INFO] Drawer zamonaviy iste'molchi ilovalarida kamroq ishlatiladi: undagi bandlar "yashirin" — foydalanuvchi ularni kam topadi. Asosiy bo'limlar uchun tablar afzal; drawer — ichki, professional ilovalar uchun.
Nima uchun shunday?
Nima uchun modalni komponent emas, alohida ekran qilish yaxshiroq?
Chunki modal ko'pincha mustaqil vazifa: o'z ma'lumot yuklashi, formasi, validatsiyasi va hatto ichki qadamlari bor. Uni asosiy ekran ichida useState va Modal bilan saqlash — asosiy ekranni shishirib yuboradi va modalni boshqa joydan ochishni qiyinlashtiradi. Alohida ekran sifatida modal — o'z fayli, o'z URL i, tizim imo-ishoralari bilan; uni ilovaning istalgan joyidan bitta router.push bilan ochish mumkin.
Xulosa
Eslab qoling:
- Modal ekran: alohida fayl +
presentation: 'modal'; ochish —push, yopish —back/dismiss presentation:modal,fullScreenModal,formSheet,transparentModal- Natija qaytarish — URL parametrlari yoki umumiy holat
- Drawer:
expo-router/drawer,drawerIcon, chapdan surish,drawerContent— o'z dizayni - Tabs — asosiy bo'limlar; Drawer — ko'p, kam ishlatiladigan bo'limlar; Modal — qisqa vazifa; Stack — chuqurlashish
Keyingi dars
29-dars: Dinamik yo'llar va parametrlar — [id].tsx, deep link