21 β Kamera, rasm va galereya¶
β¬ οΈ Oldingi: 20 β Formalar va validatsiya Β· π Kitob boshi Β· Keyingi: 22 β Joylashuv va xarita β‘οΈ
Bu bobda: Ilovangizga telefonning super-kuchlarini ulashni boshlaymiz. Avval native imkoniyatlar va ularning eng muhim qoidasi β ruxsat (permission) tushunchasini o'rganamiz. So'ng
expo-image-pickerbilan galereyadan rasm tanlash va kameradan suratga olishni,expo-camerabilan ilova ichidagi to'liq kamerani (QR skaner bilan),expo-imagebilan rasmni chiroyli ko'rsatish va keshlashni,expo-media-librarybilan galereyaga saqlashni ko'rib chiqamiz. Oxirida hammasini avatar tanlash ilovasiga birlashtiramiz.
Telefonning super-kuchlari¶
Hozirgacha biz ilova ichida ma'lumotlarni ko'rsatdik, formalar to'ldirdik, serverdan ma'lumot oldik. Lekin zamonaviy telefon β bu shunchaki ekran emas. U kamerasi, GPSi, mikrofoni, sensorlari (akselerometr, gyroskop, kompas), tebranishi va boshqa o'nlab apparat imkoniyatlari bo'lgan kuchli qurilma.
Hayotiy o'xshatish. Tasavvur qiling, sizda yangi sotib olingan, lekin hali ulanmagan kuchli ish stoli bor: stolda kamera, mikrofon, GPS-qabul qilgich β hammasi jismonan mavjud, lekin ular bilan ishlash uchun har biriga sim ulashingiz kerak. React Native va Expo β aynan shu ulagichlar to'plami: ular sizning JavaScript kodingizni telefonning apparat super-kuchlariga bog'laydi. Siz
expo-camera"simini" ulaysiz va kameradan surat olasiz;expo-location"simini" ulaysiz va GPS koordinatalarini olasiz.
Bu imkoniyatlarning aksariyati Expo'da alohida expo-* paketlar ko'rinishida keladi. Mana eng ko'p ishlatiladiganlari:
| Paket | Nima beradi |
|---|---|
expo-image-picker |
Galereyadan rasm tanlash yoki kameradan tezda surat olish |
expo-camera |
Ilova ichida to'liq kamera UI'si, QR/shtrix-kod skan |
expo-image |
Rasmlarni tez, keshlangan, chiroyli ko'rsatish |
expo-media-library |
Rasm/videoni qurilma galereyasiga saqlash, o'qish |
expo-location |
GPS koordinatalari (22-bobda) |
expo-sensors |
Akselerometr, gyroskop (23-bobda) |
expo-notifications |
Push-bildirishnomalar (23-bobda) |
Bu va keyingi ikki bob (22, 23) β to'liq native imkoniyatlar mavzusiga bag'ishlangan. Shu bobda biz rasm bilan ishlaymiz: tanlash, suratga olish, ko'rsatish va saqlash.
Bu paketlar nega alohida?
React Native "yadro" ichida faqat eng asosiy komponentlar (View, Text, Image...) bor. Kamera kabi apparat imkoniyatlari ilova hajmini oshiradi va har ilovaga kerak emas. Shuning uchun ular alohida paketlar β siz faqat kerakligini o'rnatasiz. Bu β "kerakli simni kerak bo'lganda ulash" tamoyili.
Ruxsat (permission) β eng muhim tushuncha¶
Native imkoniyatlarning HAR BIRIDA bitta umumiy va eng muhim qoida bor: foydalanuvchi ruxsat berishi kerak. Ilova kamerangizga, mikrofoningizga yoki rasm-galereyangizga avtomatik kira olmaydi β bu maxfiylik (privacy) va xavfsizlik uchun. iOS ham, Android ham foydalanuvchini har bir maxfiy imkoniyatdan himoya qiladi.
Hayotiy o'xshatish. Ruxsat β bu uy egasidan kalit so'rashga o'xshaydi. Siz mehmonning uyiga kirmoqchisiz (kamera = mehmon xonasi). Avval eshikni taqillatib, "Kirsam bo'ladimi?" deb so'raysiz. Uy egasi "Ha, marhamat" (granted) deyishi yoki "Yo'q" (denied) deyishi mumkin. Ruxsatsiz zo'rlik bilan kirib bo'lmaydi β va to'g'ri ilova rad etilganda ham odob bilan muomala qiladi: "Mayli, keyinroq".
Ruxsat bilan ishlashning umumiy oqimi har doim bir xil:
- Ruxsat so'rash β masalan
requestCameraPermissionsAsync(). - Tizim foydalanuvchiga dialog ko'rsatadi.
- Natijada holat (status) qaytadi:
granted(berildi) yokidenied(rad etildi). grantedbo'lsa β imkoniyatdan foydalanamiz.deniedbo'lsa β foydalanuvchiga muloyim tushuntirish ko'rsatamiz.
Ruxsat so'rovi Promise qaytaradi, shuning uchun await bilan ishlatamiz. Qaytgan obyektda eng muhim ikki maydon bor:
const ruxsat = await ImagePicker.requestCameraPermissionsAsync();
console.log(ruxsat.granted); // true yoki false β eng oson tekshiruv
console.log(ruxsat.status); // 'granted' | 'denied' | 'undetermined'
granted β bu oddiy boolean, shuning uchun uni ishlatish eng qulay. status esa batafsilroq: undetermined ("hali so'ralmagan") ham bo'lishi mumkin.
Ruxsatni HAR DOIM tekshiring
Eng keng tarqalgan xato β ruxsat berilganligini tekshirmasdan to'g'ridan-to'g'ri kameraga murojaat qilish. Foydalanuvchi rad etgan bo'lsa, ilova ishlamaydi yoki qulaydi. Har bir native imkoniyatdan oldin ruxsat holatini tekshiring va denied holatiga ham ishlov bering (tushuntirish ko'rsating).
Ruxsatni app.json ga e'lon qilish¶
Ruxsat so'rashning yana bir qismi bor: do'kon (App Store / Google Play) talabiga ko'ra, ilova nima uchun kameraga kerakligini oldindan e'lon qilishi shart. Buni app.json faylida config plugin orqali yozamiz. Masalan, expo-camera:
{
"expo": {
"plugins": [
[
"expo-camera",
{
"cameraPermission": "Avatar suratini olish uchun kameraga ruxsat kerak.",
"microphonePermission": "Video yozish uchun mikrofonga ruxsat kerak."
}
]
]
}
}
Bu matn β foydalanuvchi ruxsat dialogida ko'radigan tushuntirish. iOS uni majburiy talab qiladi: tushuntirishsiz ilova App Store'dan rad etiladi.
Expo o'zi qo'shadi
Yangi paketni npx expo install bilan o'rnatganingizda, Expo ko'p hollarda kerakli plugin'ni app.json ga avtomatik qo'shadi. Siz faqat tushuntirish matnini o'zingizning ilovangizga moslab tahrirlaysiz. O'zgartirishlar dev build yoki EAS Build da kuchga kiradi (oddiy Expo Go'da ba'zi ruxsatlar cheklangan).
iOS va Android farqi
iOS har bir tushuntirish matnini (Info.plist da) majburiy qiladi va foydalanuvchi rad etgach, ilova ichidan boshqa so'ray olmaydi β faqat Sozlamalarga yo'naltirish mumkin. Android ko'proq moslashuvchan: ba'zi ruxsatlarni qayta so'rash mumkin. Shuning uchun "Sozlamalarga o'tish" yo'lini har doim taklif qiling.
expo-image-picker β eng oson yo'l¶
Agar sizga shunchaki "foydalanuvchi bitta rasm tanlasin yoki bitta surat olsin" kerak bo'lsa, expo-image-picker β eng oddiy va tez yechim. U telefonning tayyor galereya/kamera ekranini ochadi; siz o'zingiz kamera UI'sini qurmaysiz.
O'rnatamiz:
expo-image-picker ikkita asosiy ishni qiladi:
- Galereyadan tanlash β
launchImageLibraryAsync(...) - Kameradan surat olish β
launchCameraAsync(...)
Galereyadan rasm tanlash¶
Avval galereyaga ruxsat so'raymiz, keyin galereyani ochamiz:
// app/galereya.tsx
import { useState } from 'react';
import { View, Text, Pressable, Image, Alert, StyleSheet } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
export default function GalereyaEkran() {
// tanlangan rasm manzili (URI) yoki hali tanlanmagan bo'lsa null
const [rasm, setRasm] = useState<string | null>(null);
async function rasmTanla() {
// 1) galereyaga ruxsat so'raymiz
const ruxsat = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (!ruxsat.granted) {
Alert.alert('Ruxsat kerak', 'Rasm tanlash uchun galereyaga ruxsat bering.');
return; // ruxsat yo'q β to'xtaymiz
}
// 2) galereyani ochamiz
const natija = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ['images'], // faqat rasmlar (video emas)
allowsEditing: true, // tanlagandan keyin kesish/kadrlash oynasi
aspect: [1, 1], // kvadrat kesish (avatar uchun qulay)
quality: 0.7, // 0..1 β sifat (kichikroq = yengilroq fayl)
});
// 3) natijani tekshiramiz
if (!natija.canceled) {
setRasm(natija.assets[0].uri); // tanlangan rasmning manzili
}
}
return (
<View style={styles.box}>
<Pressable style={styles.tugma} onPress={rasmTanla}>
<Text style={styles.tugmaMatn}>Galereyadan tanlash</Text>
</Pressable>
{rasm && <Image source={{ uri: rasm }} style={styles.rasm} />}
</View>
);
}
const styles = StyleSheet.create({
box: { flex: 1, alignItems: 'center', justifyContent: 'center', gap: 16 },
tugma: { backgroundColor: '#4f46e5', paddingVertical: 12, paddingHorizontal: 24, borderRadius: 10 },
tugmaMatn: { color: '#fff', fontWeight: '600' },
rasm: { width: 200, height: 200, borderRadius: 12 },
});
Eng muhim joylar:
mediaTypes: ['images']β endi bu massivdir ('images','videos','livePhotos'). Eski misollardaImagePicker.MediaTypeOptions.Imagesishlatilgan, lekin u eskirgan (deprecated) β yangi loyihalarda massiv shaklini yozing.natija.canceledβ foydalanuvchi hech narsa tanlamasdan orqaga qaytsa, butruebo'ladi. Bundaassetsnullbo'ladi, shuning uchun avvalcanceledni tekshirish shart.natija.assets[0].uriβ tanlangan rasmning manzili (URI). Bufile://...ko'rinishidagi yo'l. Aynan shu URI'ni<Image source={{ uri }} />ga beramiz.
MATN HAR DOIM Text ichida
Esda tuting: React Native'da har qanday matn <Text> ichida bo'lishi shart. Lekin rasm <Image> komponenti bilan ko'rsatiladi va u matn emas β uni to'g'ridan-to'g'ri <View> ichiga qo'yasiz.
Kameradan surat olish¶
Kameradan surat olish deyarli bir xil β faqat boshqa funksiya va boshqa ruxsat. Bu safar permission hook (useCameraPermissions) ishlatamiz β bu ruxsat holatini komponent ichida qulay kuzatishning zamonaviy usuli:
// app/surat.tsx
import { useState } from 'react';
import { View, Text, Pressable, Image, StyleSheet } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
export default function SuratEkran() {
// hook: [holat, so'rash funksiyasi]
const [ruxsat, soraRuxsat] = ImagePicker.useCameraPermissions();
const [rasm, setRasm] = useState<string | null>(null);
async function suratOl() {
// ruxsat hali yo'q bo'lsa β so'raymiz
if (!ruxsat?.granted) {
const yangi = await soraRuxsat();
if (!yangi.granted) return; // foydalanuvchi rad etdi
}
const natija = await ImagePicker.launchCameraAsync({
allowsEditing: true,
quality: 0.6,
});
if (!natija.canceled) {
setRasm(natija.assets[0].uri);
}
}
return (
<View style={styles.box}>
<Pressable style={styles.tugma} onPress={suratOl}>
<Text style={styles.tugmaMatn}>Kameradan surat olish</Text>
</Pressable>
{rasm && <Image source={{ uri: rasm }} style={styles.rasm} />}
</View>
);
}
const styles = StyleSheet.create({
box: { flex: 1, alignItems: 'center', justifyContent: 'center', gap: 16 },
tugma: { backgroundColor: '#0ea5e9', paddingVertical: 12, paddingHorizontal: 24, borderRadius: 10 },
tugmaMatn: { color: '#fff', fontWeight: '600' },
rasm: { width: 200, height: 200, borderRadius: 12 },
});
useCameraPermissions() β qulay tomoni shundaki, u [ruxsat, soraRuxsat] qaytaradi: birinchisi joriy holat (boshida null), ikkinchisi β ruxsat so'rash funksiyasi. Komponent qayta render bo'lganda holat avtomatik yangilanadi.
Galereya yoki kamera β birgalikda
Ko'pincha ilovalar foydalanuvchiga ikki tanlovni beradi: "Galereyadan tanlash" yoki "Suratga olish". Bunda ikkala funksiyani ham yozasiz va ikkita tugma qo'yasiz (yoki Alert bilan tanlov ko'rsatasiz). Bobning oxiridagi to'liq misolda aynan shunday qilamiz.
expo-camera β to'liq kamera ilova ichida¶
expo-image-picker telefonning tayyor kamera ekranini ochadi. Lekin ba'zan sizga o'z ilovangiz ichida kameraning jonli ko'rinishini ko'rsatish kerak bo'ladi: masalan, QR-kod skaner, maxsus tugmali kamera, real vaqtli filtr. Bunda expo-camera ishlatamiz.
Asosiy farq: bu yerda kamerani siz quryapsiz. <CameraView> komponenti β telefon kamerasining jonli oqimini ko'rsatadigan native komponent.
Hayotiy o'xshatish.
expo-image-pickerβ bu fotosalonga borib surat oldirish: salonchining o'z apparati, o'z fonida ishlaydi, sizga tayyor rasm beradi.expo-cameraesa β o'zingizning kamerangizni qo'lga olish: ko'rinishni siz boshqarasiz, qachon bosishni, qaysi tomonni (old/orqa) β hammasi sizning nazoratingizda.
expo-camera'ning asosiy qismlari:
useCameraPermissions()β kamera ruxsatini boshqaradigan hook.<CameraView>β jonli kamera ko'rinishi (facingbilan old/orqa).takePictureAsync()βCameraViewgareforqali murojaat qilib, surat oladi.barcodeScannerSettings+onBarcodeScannedβ QR/shtrix-kod skan qilish.
Jonli kamera va surat olish¶
// app/kamera.tsx
import { useState, useRef } from 'react';
import { View, Text, Pressable, Image, StyleSheet } from 'react-native';
import { CameraView, useCameraPermissions, type CameraType } from 'expo-camera';
export default function KameraEkran() {
const [ruxsat, soraRuxsat] = useCameraPermissions();
const [yon, setYon] = useState<CameraType>('back'); // 'back' = orqa, 'front' = old
const [rasm, setRasm] = useState<string | null>(null);
const kameraRef = useRef<CameraView>(null); // CameraView'ga "tutqich"
// 1) ruxsat hali yuklanmagan (null) β bo'sh ekran
if (!ruxsat) return <View />;
// 2) ruxsat yo'q β tushuntirish va so'rash tugmasi
if (!ruxsat.granted) {
return (
<View style={styles.markaz}>
<Text style={styles.matn}>Kameradan foydalanish uchun ruxsat kerak.</Text>
<Pressable style={styles.tugma} onPress={soraRuxsat}>
<Text style={styles.tugmaMatn}>Ruxsat berish</Text>
</Pressable>
</View>
);
}
// surat olish funksiyasi
async function suratOl() {
const surat = await kameraRef.current?.takePictureAsync({ quality: 0.7 });
if (surat) setRasm(surat.uri); // olingan suratning manzili
}
// kamerani aylantirish (old <-> orqa)
function aylantir() {
setYon((eski) => (eski === 'back' ? 'front' : 'back'));
}
// 3) ruxsat bor β jonli kamera
return (
<View style={{ flex: 1 }}>
<CameraView style={{ flex: 1 }} facing={yon} ref={kameraRef} />
<View style={styles.panel}>
<Pressable style={styles.tugma} onPress={aylantir}>
<Text style={styles.tugmaMatn}>Aylantirish</Text>
</Pressable>
<Pressable style={styles.tugma} onPress={suratOl}>
<Text style={styles.tugmaMatn}>Suratga olish</Text>
</Pressable>
</View>
{rasm && <Image source={{ uri: rasm }} style={styles.kichikRasm} />}
</View>
);
}
const styles = StyleSheet.create({
markaz: { flex: 1, alignItems: 'center', justifyContent: 'center', gap: 16, padding: 24 },
matn: { fontSize: 16, color: '#1e293b', textAlign: 'center' },
panel: { flexDirection: 'row', justifyContent: 'space-around', padding: 16, backgroundColor: '#0f172a' },
tugma: { backgroundColor: '#0ea5e9', paddingVertical: 12, paddingHorizontal: 20, borderRadius: 10 },
tugmaMatn: { color: '#fff', fontWeight: '600' },
kichikRasm: { position: 'absolute', top: 40, right: 16, width: 80, height: 80, borderRadius: 8 },
});
Bu yerda yangi va muhim narsalar:
useRef<CameraView>(null)βCameraViewga "tutqich" (ref).takePictureAsyncaynan shu ref orqali chaqiriladi:kameraRef.current?.takePictureAsync(...).?.β agar ref hali tayyor bo'lmasa, xato chiqmaydi.- Uchta holat: ruxsat
null(yuklanmoqda) -> bo'sh;denied-> tushuntirish;granted-> kamera. Bu β har bir native ekranning to'g'ri shabloni. facing={yon}β'back'(orqa) yoki'front'(old). State o'zgarsa, kamera avtomatik almashadi.
CameraView faqat haqiqiy qurilmada to'liq ishlaydi
Kamera jonli oqimini ko'rish uchun haqiqiy telefon (yoki kamera ulangan emulyator) kerak. Web brauzerda CameraView cheklangan, ba'zi emulyatorlarda umuman ishlamaydi. Sinab ko'rishni Expo Go yoki dev build orqali telefonda qiling.
QR-kod / shtrix-kod skan qilish¶
expo-camera'ning eng foydali imkoniyatlaridan biri β QR va shtrix-kodlarni skan qilish. Buni alohida kutubxonasiz, CameraView'ning o'zida ikkita prop bilan qilamiz:
barcodeScannerSettings={{ barcodeTypes: ['qr'] }}β qaysi turdagi kodlarni qidirish.onBarcodeScannedβ kod topilganda chaqiriladigan funksiya. Unga{ type, data }keladi;dataβ kod ichidagi ma'lumot (masalan URL).
// app/qr-skaner.tsx
import { useState } from 'react';
import { View, Text, Pressable, StyleSheet } from 'react-native';
import { CameraView, useCameraPermissions, type BarcodeScanningResult } from 'expo-camera';
export default function QrSkanerEkran() {
const [ruxsat, soraRuxsat] = useCameraPermissions();
const [skanlandi, setSkanlandi] = useState(false); // takror skanning oldini olamiz
const [malumot, setMalumot] = useState('');
function ozSkan({ type, data }: BarcodeScanningResult) {
if (skanlandi) return; // bir marta yetarli
setSkanlandi(true);
setMalumot(`${type}: ${data}`);
}
if (!ruxsat?.granted) {
return (
<View style={styles.markaz}>
<Text style={styles.matn}>Skaner uchun kameraga ruxsat kerak.</Text>
<Pressable style={styles.tugma} onPress={soraRuxsat}>
<Text style={styles.tugmaMatn}>Ruxsat berish</Text>
</Pressable>
</View>
);
}
return (
<View style={{ flex: 1 }}>
<CameraView
style={{ flex: 1 }}
barcodeScannerSettings={{ barcodeTypes: ['qr', 'ean13'] }}
// skanlangan bo'lsa, qayta tinglashni to'xtatamiz (undefined)
onBarcodeScanned={skanlandi ? undefined : ozSkan}
/>
<View style={styles.panel}>
{malumot ? <Text style={styles.natija}>Topildi -> {malumot}</Text> : <Text style={styles.natija}>QR-kodni kameraga tuting...</Text>}
{skanlandi && (
<Pressable style={styles.tugma} onPress={() => setSkanlandi(false)}>
<Text style={styles.tugmaMatn}>Yana skan qilish</Text>
</Pressable>
)}
</View>
</View>
);
}
const styles = StyleSheet.create({
markaz: { flex: 1, alignItems: 'center', justifyContent: 'center', gap: 16, padding: 24 },
matn: { fontSize: 16, color: '#1e293b', textAlign: 'center' },
panel: { padding: 16, gap: 12, backgroundColor: '#0f172a' },
natija: { color: '#e2e8f0', fontSize: 15, textAlign: 'center' },
tugma: { backgroundColor: '#0ea5e9', paddingVertical: 12, paddingHorizontal: 20, borderRadius: 10, alignSelf: 'center' },
tugmaMatn: { color: '#fff', fontWeight: '600' },
});
Bu yerdagi muhim nozik joy β takror skan muammosi. Kamera har sekundda o'nlab marta tekshiradi, shuning uchun bitta QR-kod soniyada bir necha bor "topiladi". skanlandi flagi va onBarcodeScanned={skanlandi ? undefined : ozSkan} β funksiyani vaqtincha o'chirib qo'yib, faqat bir marta ishlashini ta'minlaydi.
Qachon image-picker, qachon camera?¶
| Sizga kerak | Tanlang |
|---|---|
| Foydalanuvchi galereyadan rasm tanlasin | expo-image-picker |
| Tezda bitta surat olish (tayyor kamera) | expo-image-picker |
| Ilova ichida jonli kamera ko'rinishi | expo-camera |
| QR / shtrix-kod skan | expo-camera |
| Maxsus tugmali, filterli kamera | expo-camera |
Oddiy qilib aytganda: "shunchaki rasm kerak" -> image-picker; "kamera ustidan nazorat kerak" -> camera.
Rasmni serverga yuklash¶
Tanlangan yoki olingan rasmni ko'pincha serverga yuklash kerak bo'ladi (masalan, profil avatarini saqlash). Buning uchun 17-bobda ko'rgan fetch va FormData ishlatamiz. FormData β fayllarni jo'natishning standart usuli; rasmni uning urisi orqali qo'shamiz:
// rasmni serverga yuklash
async function rasmYukla(uri: string) {
const forma = new FormData();
forma.append('avatar', {
uri, // rasm manzili (file://...)
name: 'avatar.jpg', // server ko'radigan fayl nomi
type: 'image/jpeg', // MIME-tur
} as any);
const javob = await fetch('https://example.com/api/avatar', {
method: 'POST',
body: forma,
// DIQQAT: Content-Type'ni QO'LDA yozmang!
});
return javob.json();
}
Content-Type'ni qo'lda yozmang
FormData bilan ishlaganda Content-Type sarlavhasini o'zingiz belgilamang. fetch uni avtomatik multipart/form-data qilib, kerakli "boundary" (chegara) bilan to'g'ri qo'yadi. Qo'lda yozsangiz β yuklash buziladi. Bu β keng tarqalgan xato.
as any (yoki aniqroq as unknown as Blob) bu yerda kerak, chunki React Native'da FormDataga fayl-obyekt qo'shish veb-standartdan biroz farq qiladi va TypeScript tiplari to'liq mos kelmaydi. Bu β React Native'da odatiy va xavfsiz amaliyot.
expo-image β zamonaviy rasm ko'rsatish¶
react-native'ning o'rnatilgan <Image> komponenti oddiy ishlar uchun yetarli. Lekin expo-image β undan ancha kuchli: u tezroq, rasmlarni keshlaydi (bir marta yuklab, qayta-qayta ishlatadi), yuklanayotganda placeholder (o'rin egasi) ko'rsatadi va silliq o'tish (transition) beradi.
// expo-image bilan rasm ko'rsatish
import { Image } from 'expo-image'; // DIQQAT: react-native'dan EMAS!
<Image
source="https://example.com/avatar.jpg"
style={{ width: 150, height: 150, borderRadius: 75 }}
contentFit="cover" // rasm qutiga qanday sig'sin (cover/contain/fill)
transition={300} // paydo bo'lishda 300ms silliq o'tish
cachePolicy="memory-disk" // xotirada va diskda keshlash
placeholder={{ blurhash: 'L6PZfSi_.AyE_3t7t7R**0o#DgR4' }} // yuklanayotganda xiralashgan o'rin
/>
Eng foydali xususiyatlar:
contentFitβ'cover'(qutini to'ldiradi, kerak bo'lsa kesadi),'contain'(butun rasm ko'rinadi),'fill'(cho'zadi). Bu β veb'dagiobject-fito'xshashi.cachePolicyβ keshlash strategiyasi.'memory-disk'β rasm bir marta yuklanib, keyin doim keshdan keladi (juda tez).placeholder+blurhashβ blurhash rasmning juda kichik, xiralashgan "old ko'rinishi"ni ifodalovchi qisqa matn. Rasm to'liq yuklanguncha foydalanuvchi bo'sh joy emas, chiroyli xira surat ko'radi.transitionβ rasm paydo bo'lishidagi silliq animatsiya (millisekundda).
Lokal va onlayn rasm
expo-image ham onlayn rasmlarni (URL string), ham galereyadan olingan rasmlarni ({ uri }), ham loyihadagi rasmlarni (require('./logo.png')) qabul qiladi. Avatar ilovangizda galereyadan/kameradan olingan urini shu komponentga berib, keshlash va silliq o'tishdan foydalanishingiz mumkin.
expo-media-library β galereyaga saqlash¶
Foydalanuvchi ilovada surat oldi yoki tahrir qildi β uni qurilma galereyasiga saqlash kerak bo'lishi mumkin. Buning uchun expo-media-library ishlatamiz.
// rasmni galereyaga saqlash
import * as MediaLibrary from 'expo-media-library';
import { Alert } from 'react-native';
export function SaqlashTugma({ uri }: { uri: string }) {
// saqlash uchun YOZISH ruxsati kerak
const [ruxsat, soraRuxsat] = MediaLibrary.usePermissions();
async function galereyagaSaqla() {
if (!ruxsat?.granted) {
const yangi = await soraRuxsat();
if (!yangi.granted) return;
}
await MediaLibrary.saveToLibraryAsync(uri); // galereyaga saqlaydi
Alert.alert('Saqlandi', 'Rasm galereyangizga saqlandi.');
}
// ... tugma JSX'i
}
saveToLibraryAsync(uri) β eng oddiy yo'l: berilgan uri'dagi rasmni galereyaga qo'shadi. Agar siz rasmni maxsus albomga joylamoqchi bo'lsangiz, MediaLibrary.createAssetAsync(uri) va albom funksiyalaridan foydalanasiz. Saqlash uchun yozish ruxsati kerakligini unutmang β shuning uchun bu yerda ham usePermissions ishlatamiz.
To'liq misol: avatar tanlash ilovasi¶
Endi hammasini birlashtiramiz. Foydalanuvchi galereyadan rasm tanlashi yoki kameradan surat olishi mumkin bo'lgan oddiy avatar tanlash ekranini yozamiz. Ruxsatlarni to'g'ri so'raymiz, tanlangan rasmni expo-image bilan chiroyli ko'rsatamiz.
// app/avatar.tsx
import { useState } from 'react';
import { View, Text, Pressable, Alert, StyleSheet } from 'react-native';
import { Image } from 'expo-image';
import * as ImagePicker from 'expo-image-picker';
export default function AvatarEkran() {
const [avatar, setAvatar] = useState<string | null>(null);
// galereyadan tanlash
async function galereyadan() {
const ruxsat = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (!ruxsat.granted) {
Alert.alert('Ruxsat kerak', 'Galereyaga ruxsat bering.');
return;
}
const natija = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ['images'],
allowsEditing: true,
aspect: [1, 1], // kvadrat avatar
quality: 0.7,
});
if (!natija.canceled) setAvatar(natija.assets[0].uri);
}
// kameradan surat olish
async function kameradan() {
const ruxsat = await ImagePicker.requestCameraPermissionsAsync();
if (!ruxsat.granted) {
Alert.alert('Ruxsat kerak', 'Kameraga ruxsat bering.');
return;
}
const natija = await ImagePicker.launchCameraAsync({
allowsEditing: true,
aspect: [1, 1],
quality: 0.7,
});
if (!natija.canceled) setAvatar(natija.assets[0].uri);
}
return (
<View style={styles.box}>
<Text style={styles.sarlavha}>Profil rasmi</Text>
{/* avatar doirasi: rasm bor bo'lsa rasm, yo'q bo'lsa bo'sh o'rin */}
{avatar ? (
<Image source={{ uri: avatar }} style={styles.avatar} contentFit="cover" transition={250} />
) : (
<View style={[styles.avatar, styles.bosh]}>
<Text style={styles.boshMatn}>Rasm yo'q</Text>
</View>
)}
<View style={styles.panel}>
<Pressable style={styles.tugma} onPress={galereyadan}>
<Text style={styles.tugmaMatn}>Galereya</Text>
</Pressable>
<Pressable style={[styles.tugma, styles.tugmaKok]} onPress={kameradan}>
<Text style={styles.tugmaMatn}>Kamera</Text>
</Pressable>
</View>
{avatar && (
<Pressable onPress={() => setAvatar(null)}>
<Text style={styles.olib}>Rasmni olib tashlash</Text>
</Pressable>
)}
</View>
);
}
const styles = StyleSheet.create({
box: { flex: 1, alignItems: 'center', justifyContent: 'center', gap: 24, padding: 24 },
sarlavha: { fontSize: 22, fontWeight: '700', color: '#1e293b' },
avatar: { width: 160, height: 160, borderRadius: 80, backgroundColor: '#e2e8f0' },
bosh: { alignItems: 'center', justifyContent: 'center' },
boshMatn: { color: '#94a3b8', fontSize: 14 },
panel: { flexDirection: 'row', gap: 12 },
tugma: { backgroundColor: '#4f46e5', paddingVertical: 12, paddingHorizontal: 28, borderRadius: 10 },
tugmaKok: { backgroundColor: '#0ea5e9' },
tugmaMatn: { color: '#fff', fontWeight: '600' },
olib: { color: '#dc2626', fontSize: 14, fontWeight: '600' },
});
Bu ilova hamma o'rgangan narsalarni birlashtiradi:
- Ikki manba β galereya va kamera, har biri o'z ruxsatini so'raydi.
canceledtekshiruvi β foydalanuvchi bekor qilsa, eski avatar saqlanadi.expo-imageβcontentFit="cover"vatransitionbilan chiroyli ko'rsatish.- Bo'sh holat β rasm tanlanmaganda muloyim "Rasm yo'q" o'rin egasi.
Bu kod jonli Expo loyihada npx tsc --noEmit bilan tekshirilgan β tip xatosisiz ishlaydi. Endi siz uni serverga yuklash (yuqoridagi FormData misoli) yoki galereyaga saqlash bilan kengaytirishingiz mumkin.
Sinab ko'ring
Bu ekranni app/avatar.tsx ga joylashtiring va telefoningizda Expo Go orqali oching. Galereya va kamera tugmalarini bosib, ruxsat dialoglarini va natijani kuzating. Avatar doirasi ichida rasm qanday "kesilishini" (cover) ko'ring.
Xulosa¶
- Native imkoniyatlar (kamera, GPS, sensorlar) β telefonning "super-kuchlari"; Expo ularni
expo-*paketlar orqali JavaScript'ga ulaydi. - Ruxsat (permission) β har bir native imkoniyatning eng muhim qoidasi: foydalanuvchi
granted(berdi) yokidenied(rad etdi). Ruxsatni HAR DOIM tekshiring vadeniedholatiga ishlov bering. app.jsonplugins ga ruxsat tushuntirish matnini yozish shart β ayniqsa iOS uchun (do'kon talabi).expo-image-pickerβ eng oson yo'l:launchImageLibraryAsync(galereya),launchCameraAsync(kamera). Natija:result.canceledvaresult.assets[0].uri.mediaTypesendi massiv (['images']).expo-cameraβ ilova ichidagi to'liq kamera:useCameraPermissions,<CameraView facing>,takePictureAsync(ref orqali), QR skan (barcodeScannerSettings+onBarcodeScanned).- Rasmni serverga yuklash β
FormData+fetch;Content-Type'ni qo'lda YOZMANG. expo-imageβ zamonaviy ko'rsatish:contentFit, keshlash (cachePolicy),placeholder/blurhash,transition.expo-media-libraryβsaveToLibraryAsyncbilan galereyaga saqlash (yozish ruxsati kerak).
Amaliy mashqlar¶
-
Avatar tanlash (asosiy). Yuqoridagi
AvatarEkranmisolini o'z loyihangizga ko'chiring va ishlatib ko'ring. Tugma matnlarini va ranglarini o'zingizga moslang. Rasm tanlanmaganda "Rasm yo'q" o'rniga foydalanuvchi ismining bosh harfini ko'rsating. -
Kameradan surat (camera).
expo-camerabilan jonli kamerali ekran yarating: old/orqa almashtirish tugmasi, suratga olish tugmasi va olingan suratni ekranning bir burchagida kichik ko'rinishda ko'rsating. Ruxsatning uchta holatini (null,denied,granted) to'g'ri ishlang. -
QR skaner.
CameraView+barcodeScannerSettingsbilan QR-kod skaneri yozing. Topilgan ma'lumotni ekranda ko'rsating va "Yana skan qilish" tugmasi qo'ying. Qo'shimcha: agardatahttpbilan boshlansa, uni havola sifatida ajratib ko'rsating. -
Rasm yuklash (server). Tanlangan rasmni
FormDatabilan biror test serveriga (masalanhttps://httpbin.org/post) yuborib ko'ring. Yuklash davomidaActivityIndicator(spinner) ko'rsating va natijada server javobini ekranga chiqaring.Content-Type'ni qo'lda yozmaganingizga ishonch hosil qiling. -
Galereyaga saqlash + kesh (qiyin). Kameradan olingan suratni
expo-media-librarybilan galereyaga saqlang. So'ngexpo-imageyordamida bir nechta onlayn rasmni ro'yxatda ko'rsating:placeholder/blurhashvatransitionqo'shib, keshlash ta'sirini (ikkinchi marta tezroq yuklanishini) kuzating.
β¬ οΈ Oldingi: 20 β Formalar va validatsiya Β· π Kitob boshi Β· Keyingi: 22 β Joylashuv va xarita β‘οΈ