1-2 bo'lim mashqlari — profil kartasi

~40 daq · React Native · Asosiy komponentlar

Qanday ishlash kerak

Siz Expo loyihasini yaratishni, View, Text, Image, ScrollView, TextInput, Pressable, Modal, Alert va Switch ni bilasiz. Endi ularni birlashtirib, haqiqiy ekranlar yasaymiz.

  1. Har bir mashqni avval o'zingiz bajaring — kamida 15 daqiqa
  2. Telefonda sinab ko'ring — emulyatorda emas, haqiqiy qurilmada
  3. Keyin yechim bilan solishtiring

1-mashq: Profil kartasi ⭐

Quyidagi ekranni yasang:

  • Yumaloq avatar (tarmoqdagi rasm, 100×100)
  • Ism (katta, qalin), kasb (kulrang)
  • Uchta statistika yonma-yon: "Postlar 128", "Obunachilar 2.4K", "Obunalar 310"
  • "Obuna bo'lish" tugmasi — bosilganda "Obunasiz" ga o'zgaradi va rangi almashadi
Yechim
import { useState } from 'react';
import { View, Text, Image, Pressable, StyleSheet } from 'react-native';

const statlar = [
  { nom: 'Postlar', qiymat: '128' },
  { nom: 'Obunachilar', qiymat: '2.4K' },
  { nom: 'Obunalar', qiymat: '310' },
];

export default function Profil() {
  const [obuna, setObuna] = useState(false);

  return (
    <View style={styles.ekran}>
      <Image source={{ uri: 'https://i.pravatar.cc/200?img=12' }} style={styles.avatar} />
      <Text style={styles.ism}>Aziz Karimov</Text>
      <Text style={styles.kasb}>React Native dasturchi</Text>

      <View style={styles.statlar}>
        {statlar.map((s) => (
          <View key={s.nom} style={styles.stat}>
            <Text style={styles.statQiymat}>{s.qiymat}</Text>
            <Text style={styles.statNom}>{s.nom}</Text>
          </View>
        ))}
      </View>

      <Pressable
        onPress={() => setObuna(!obuna)}
        style={({ pressed }) => [
          styles.tugma,
          obuna && styles.tugmaObuna,
          pressed && { opacity: 0.8 },
        ]}
      >
        <Text style={[styles.tugmaMatn, obuna && { color: '#0284c7' }]}>
          {obuna ? 'Obunasiz' : "Obuna bo'lish"}
        </Text>
      </Pressable>
    </View>
  );
}

const styles = StyleSheet.create({
  ekran: { flex: 1, alignItems: 'center', paddingTop: 80, backgroundColor: 'white' },
  avatar: { width: 100, height: 100, borderRadius: 50, marginBottom: 12 },
  ism: { fontSize: 22, fontWeight: '700' },
  kasb: { fontSize: 15, color: '#64748b', marginTop: 4 },
  statlar: { flexDirection: 'row', gap: 32, marginVertical: 24 },
  stat: { alignItems: 'center' },
  statQiymat: { fontSize: 18, fontWeight: '700' },
  statNom: { fontSize: 13, color: '#64748b' },
  tugma: { backgroundColor: '#0284c7', paddingVertical: 12, paddingHorizontal: 40, borderRadius: 10 },
  tugmaObuna: { backgroundColor: 'white', borderWidth: 1, borderColor: '#0284c7' },
  tugmaMatn: { color: 'white', fontWeight: '600', fontSize: 16 },
});

borderRadius = kenglikning yarmi → doira. flexDirection: 'row' — elementlar yonma-yon (14-darsda batafsil).


2-mashq: Kirish formasi ⭐⭐

  • Email va parol maydonlari — to'g'ri keyboardType, autoCapitalize, autoComplete
  • Parolni ko'rsatish/yashirish ikonkasi
  • Klaviaturadagi "Keyingi" email dan parolga o'tkazsin, "Tayyor" — formani yuborsin
  • "Kirish" tugmasi: email yoki parol bo'sh bo'lsa — disabled
  • Bosilganda 1.5 soniya ActivityIndicator, keyin Alert: "Xush kelibsiz, {email}"
Yechim
import { useRef, useState } from 'react';
import { View, Text, TextInput, Pressable, ActivityIndicator, Alert, StyleSheet } from 'react-native';
import { Ionicons } from '@expo/vector-icons';

export default function Kirish() {
  const [email, setEmail] = useState('');
  const [parol, setParol] = useState('');
  const [korinsin, setKorinsin] = useState(false);
  const [yuklanmoqda, setYuklanmoqda] = useState(false);
  const parolRef = useRef<TextInput>(null);

  const tayyor = email.trim() !== '' && parol !== '';

  async function kirish() {
    if (!tayyor || yuklanmoqda) return;
    setYuklanmoqda(true);
    await new Promise((r) => setTimeout(r, 1500));   // server so'rovi o'rniga
    setYuklanmoqda(false);
    Alert.alert('Xush kelibsiz', email.trim());
  }

  return (
    <View style={styles.ekran}>
      <Text style={styles.sarlavha}>Kirish</Text>

      <TextInput
        style={styles.input}
        placeholder="Email"
        value={email}
        onChangeText={setEmail}
        keyboardType="email-address"
        autoCapitalize="none"
        autoCorrect={false}
        autoComplete="email"
        returnKeyType="next"
        onSubmitEditing={() => parolRef.current?.focus()}
      />

      <View style={styles.parolQator}>
        <TextInput
          ref={parolRef}
          style={[styles.input, { flex: 1, borderWidth: 0 }]}
          placeholder="Parol"
          value={parol}
          onChangeText={setParol}
          secureTextEntry={!korinsin}
          autoComplete="password"
          returnKeyType="done"
          onSubmitEditing={kirish}
        />
        <Pressable onPress={() => setKorinsin(!korinsin)} hitSlop={10} style={{ paddingHorizontal: 12 }}>
          <Ionicons name={korinsin ? 'eye-off-outline' : 'eye-outline'} size={22} color="#64748b" />
        </Pressable>
      </View>

      <Pressable
        onPress={kirish}
        disabled={!tayyor || yuklanmoqda}
        style={({ pressed }) => [
          styles.tugma,
          (!tayyor || yuklanmoqda) && { opacity: 0.5 },
          pressed && { opacity: 0.8 },
        ]}
      >
        {yuklanmoqda ? <ActivityIndicator color="white" /> : <Text style={styles.tugmaMatn}>Kirish</Text>}
      </Pressable>
    </View>
  );
}

const styles = StyleSheet.create({
  ekran: { flex: 1, justifyContent: 'center', padding: 24, gap: 14, backgroundColor: 'white' },
  sarlavha: { fontSize: 28, fontWeight: '800', marginBottom: 12 },
  input: { borderWidth: 1, borderColor: '#cbd5e1', borderRadius: 10, padding: 14, fontSize: 16 },
  parolQator: { flexDirection: 'row', alignItems: 'center', borderWidth: 1, borderColor: '#cbd5e1', borderRadius: 10 },
  tugma: { backgroundColor: '#0284c7', padding: 16, borderRadius: 12, alignItems: 'center', minHeight: 54, justifyContent: 'center' },
  tugmaMatn: { color: 'white', fontSize: 16, fontWeight: '600' },
});

3-mashq: Sozlamalar ekrani ⭐⭐

  • ScrollView ichida 3 ta guruh: "Hisob", "Bildirishnomalar", "Boshqa"
  • "Bildirishnomalar" guruhida 3 ta Switch (Push, Email, SMS) — har biri o'z holati bilan
  • "Boshqa" guruhida "Hisobni o'chirish" — qizil matn, bosilganda tasdiqlovchi Alert (destructive)
  • Har bir band — Pressable va android_ripple
Yechim
import { useState } from 'react';
import { ScrollView, View, Text, Pressable, Switch, Alert, StyleSheet } from 'react-native';

function Band({ matn, onPress, xavfli }: { matn: string; onPress?: () => void; xavfli?: boolean }) {
  return (
    <Pressable onPress={onPress} android_ripple={{ color: '#e2e8f0' }}
      style={({ pressed }) => [styles.band, pressed && { backgroundColor: '#f1f5f9' }]}>
      <Text style={[styles.bandMatn, xavfli && { color: '#dc2626' }]}>{matn}</Text>
    </Pressable>
  );
}

export default function Sozlamalar() {
  const [sozlama, setSozlama] = useState({ push: true, email: false, sms: true });
  const almashtir = (k: keyof typeof sozlama) => setSozlama((s) => ({ ...s, [k]: !s[k] }));

  return (
    <ScrollView style={{ flex: 1, backgroundColor: '#f1f5f9' }} contentContainerStyle={{ padding: 16, gap: 24 }}>
      <View style={styles.guruh}>
        <Text style={styles.guruhNom}>HISOB</Text>
        <Band matn="Profilni tahrirlash" onPress={() => {}} />
        <Band matn="Parolni o'zgartirish" onPress={() => {}} />
      </View>

      <View style={styles.guruh}>
        <Text style={styles.guruhNom}>BILDIRISHNOMALAR</Text>
        {(['push', 'email', 'sms'] as const).map((k) => (
          <View key={k} style={[styles.band, styles.qator]}>
            <Text style={styles.bandMatn}>{k.toUpperCase()}</Text>
            <Switch value={sozlama[k]} onValueChange={() => almashtir(k)} />
          </View>
        ))}
      </View>

      <View style={styles.guruh}>
        <Text style={styles.guruhNom}>BOSHQA</Text>
        <Band
          matn="Hisobni o'chirish"
          xavfli
          onPress={() =>
            Alert.alert("Hisobni o'chirish", "Barcha ma'lumotlaringiz o'chiriladi.", [
              { text: 'Bekor qilish', style: 'cancel' },
              { text: "O'chirish", style: 'destructive', onPress: () => console.log("o'chirildi") },
            ])
          }
        />
      </View>
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  guruh: { backgroundColor: 'white', borderRadius: 12, overflow: 'hidden' },
  guruhNom: { fontSize: 12, color: '#64748b', padding: 12, paddingBottom: 4, fontWeight: '600' },
  band: { paddingVertical: 14, paddingHorizontal: 16 },
  qator: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' },
  bandMatn: { fontSize: 16 },
});

overflow: 'hidden' — guruhning yumaloq burchaklari ichidagi bandlarni ham qirqadi.


O'zingizni tekshiring

  • View ichiga to'g'ridan-to'g'ri matn yozsa nima bo'ladi?
  • Tarmoqdagi rasm nima uchun ko'rinmasligi mumkin?
  • ScrollView ni qachon ishlatmaslik kerak?
  • Email maydoni uchun qaysi uchta xususiyat muhim?
  • Tugma ikki marta bosilishining oldini qanday olasiz?
  • Alert va Modal farqi nima?

Keyingi bo'lim

3-bo'lim: Stil va layout — StyleSheet va CSS farqlari, Flexbox, turli ekran o'lchamlari, qorong'i rejim va safe area.