Tablar — pastki panel, ikonkalar, badge

~15 daq · React Native · Navigatsiya — Expo Router

Muammodan boshlaylik

Do'kon ilovasining asosiy bo'limlari: Bosh sahifa, Katalog, Savat, Profil. Foydalanuvchi ular orasida tez-tez o'tadi — va har biri o'z holatini saqlashi kerak: katalogdagi scroll, savatdagi mahsulotlar.

Deyarli har bir mobil ilovada — ekran pastida tab panel. Instagram, Telegram, Uzum Market — hammasida.


Tuzilma

app/
├── _layout.tsx           ← Stack (ildiz)
├── (tabs)/
│   ├── _layout.tsx       ← Tabs
│   ├── index.tsx         ← Bosh
│   ├── katalog.tsx
│   ├── savat.tsx
│   └── profil.tsx
└── mahsulot/[id].tsx     ← tablar ustida, Stack da
// app/_layout.tsx
import { Stack } from 'expo-router';

export default function RootLayout() {
  return (
    <Stack>
      <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
      <Stack.Screen name="mahsulot/[id]" options={{ title: 'Mahsulot' }} />
    </Stack>
  );
}

(tabs) ning o'z sarlavhasi bor (Tabs dagi), shuning uchun Stack sarlavhasi yashiriladi — aks holda ikki qavat sarlavha chiqadi.


Tabs layout

// app/(tabs)/_layout.tsx
import { Tabs } from 'expo-router';
import { Ionicons } from '@expo/vector-icons';

export default function TabLayout() {
  return (
    <Tabs
      screenOptions={{
        tabBarActiveTintColor: '#0284c7',
        tabBarInactiveTintColor: '#94a3b8',
        tabBarLabelStyle: { fontSize: 11, fontWeight: '600' },
      }}
    >
      <Tabs.Screen
        name="index"
        options={{
          title: 'Bosh',
          tabBarIcon: ({ color, size, focused }) => (
            <Ionicons name={focused ? 'home' : 'home-outline'} size={size} color={color} />
          ),
        }}
      />
      <Tabs.Screen
        name="katalog"
        options={{
          title: 'Katalog',
          tabBarIcon: ({ color, size, focused }) => (
            <Ionicons name={focused ? 'grid' : 'grid-outline'} size={size} color={color} />
          ),
        }}
      />
      <Tabs.Screen
        name="savat"
        options={{
          title: 'Savat',
          tabBarIcon: ({ color, size, focused }) => (
            <Ionicons name={focused ? 'cart' : 'cart-outline'} size={size} color={color} />
          ),
        }}
      />
      <Tabs.Screen
        name="profil"
        options={{
          title: 'Profil',
          tabBarIcon: ({ color, size, focused }) => (
            <Ionicons name={focused ? 'person' : 'person-outline'} size={size} color={color} />
          ),
        }}
      />
    </Tabs>
  );
}
  • tabBarIcon — funksiya: color (faol/nofaol rang), size, focused
  • Faol tab — to'ldirilgan ikonka, nofaol — chiziqli. iOS va Android dizayn tavsiyasi
  • Tablar tartibi — Tabs.Screen lar tartibida
  • Tab panel safe area ni (pastki indikator) o'zi hisobga oladi (18-dars)

Badge — savatdagi son

const soni = useSavat((s) => s.mahsulotlar.length);   // Zustand, 33-dars

<Tabs.Screen
  name="savat"
  options={{
    title: 'Savat',
    tabBarBadge: soni > 0 ? soni : undefined,
    tabBarBadgeStyle: { backgroundColor: '#dc2626', fontSize: 10 },
    tabBarIcon: ...,
  }}
/>

undefined — badge ko'rinmaydi. 0 yozsangiz — "0" ko'rinadi.


Tab ichida stack

Katalog → toifa → mahsulot: tab panel ko'rinib turishi kerakmi?

Variant 1 — tablar ustida (yuqoridagi tuzilma). Mahsulot ekrani ildiz Stack da — u tablarni yopib ochiladi. Diqqat mahsulotga qaratiladi. Ko'p do'kon ilovalari shunday.

Variant 2 — tab ichida. Har bir tab o'z stack iga ega, tab panel doim ko'rinadi:

app/(tabs)/
├── _layout.tsx
├── katalog/
│   ├── _layout.tsx      ← Stack
│   ├── index.tsx        ← /katalog
│   └── [toifa].tsx      ← /katalog/telefonlar
└── profil.tsx
// app/(tabs)/katalog/_layout.tsx
import { Stack } from 'expo-router';
export default function KatalogStack() {
  return <Stack />;
}
// app/(tabs)/_layout.tsx
<Tabs.Screen name="katalog" options={{ headerShown: false, title: 'Katalog', tabBarIcon: ... }} />

headerShown: false — Tabs sarlavhasini yashirish, chunki ichki Stack o'z sarlavhasini ko'rsatadi.

Har bir tab o'z stack ini eslab qoladi: katalogda 3 ekran chuqur kirdingiz, profilga o'tib, qaytdingiz — yana o'sha 3-ekrandasiz. Instagram aynan shunday.


Tab bosish hodisalari

Faol tabni qayta bosganda — ro'yxat tepasiga qaytish (Instagram, Telegram):

import { useScrollToTop } from '@react-navigation/native';

export default function Katalog() {
  const ref = useRef<FlatList>(null);
  useScrollToTop(ref);
  return <FlatList ref={ref} ... />;
}

Bitta qator — va tab ikkinchi marta bosilganda avtomatik tepaga.

Tabni bosishni ushlash (masalan, "+" tabi ekran emas, modal ochishi kerak):

<Tabs.Screen
  name="yangi"
  options={{ title: '', tabBarIcon: () => <Ionicons name="add-circle" size={36} color="#0284c7" /> }}
  listeners={{
    tabPress: (e) => {
      e.preventDefault();          // tabga o'tmaslik
      router.push('/yangi-elon');  // modal ochish
    },
  }}
/>

Tabni yashirish

<Tabs.Screen name="maxfiy" options={{ href: null }} />

href: null — fayl (tabs)/ da, lekin tab panelda ko'rinmaydi.


Dizayn maslahatlari

  • 3–5 ta tab. Ko'proq bo'lsa — "Yana" tabi yoki drawer
  • Har bir tab — mustaqil, muhim bo'lim. "Sozlamalar" odatda tab emas — profil ichida
  • Ikonka va matn — faqat ikonka tushunarsiz bo'lishi mumkin
  • Tab paneli har doim bir xil. Ekranga qarab tablarni almashtirish — chalkashtiradi

Nima uchun shunday?

Nima uchun tab panel mobil ilovalarda pastda, vebda esa navigatsiya tepada?

Ergonomika. Telefonni bir qo'lda ushlaganda bosh barmoq ekranning pastki qismiga oson yetadi, yuqori burchaklarga esa yetmaydi. Katta ekranli zamonaviy telefonlarda bu yanada muhim. Shuning uchun eng ko'p ishlatiladigan amallar — pastda. Veb esa sichqoncha va katta ekran uchun loyihalangan — u yerda "qo'l yetishi" muammo emas.


Xulosa

Eslab qoling:

  • (tabs)/_layout.tsx da <Tabs>; ildiz Stack da (tabs) sarlavhasi yashiriladi
  • tabBarIcon: ({ color, size, focused }) — faol: to'ldirilgan, nofaol: chiziqli
  • tabBarBadge — son; undefined — yashirish
  • Tablar ustida (ildiz Stack) yoki tab ichida (ichki Stack) — dizayn qarori
  • useScrollToTop(ref) — tabni qayta bosganda tepaga
  • listeners.tabPress + preventDefault — tabni maxsus amalga aylantirish
  • href: null — tabni yashirish; 3–5 tab

Keyingi dars

28-dars: Drawer va modal ekranlar