2-qism · Flutter arxitekturasi·04-bob
Flutter arxitekturasi — Widget, Element va RenderObject daraxtlari
Flutter'ning ichki mexanizmi: nima uchun uchta alohida daraxt bor, BuildContext aslida nima, va setState chaqirilganda aniq nima sodir bo'ladi.
Ko'pchilik Flutter dasturchisi "widget" tushunchasi bilan ishlaydi, lekin senior
darajadagi intervyularda ko'proq savol beriladigan narsa — widget aslida
nima emasligi. Widget — ekranga chizilgan narsaning o'zi emas, balki uni
qanday chizish kerakligi haqidagi konfiguratsiya, xolos. Shu farqni
tushunmaslik — Flutter intervyularida eng ko'p uchraydigan bilim bo'shlig'i.
Bu bobda Flutter'ning uchta daraxti, BuildContext aslida nima ekani va
setState chaqirilganda framework ichida aniq nima ro'y berishini ko'ramiz.
1. Nega bitta emas, uchta daraxt bor#
Flutter UI'ni uchta parallel daraxt orqali boshqaradi, va har biri boshqa vazifani bajaradi:
Widget'lar — engil, o'zgarmas obyektlar. Har safar build() metodi
chaqirilganda, butun widget daraxti qaytadan yaratiladi — bu arzon, chunki
widget'lar shunchaki ma'lumot tashuvchi klasslar (ko'pincha bir nechta
final maydon bilan). Lekin ekranga chiqarish (layout hisoblash, canvas'ga
chizish) qimmat operatsiya. Agar har build()da butun ekran qayta chizilsa,
ilova sekinlashadi.
Shu sababli Flutter ikkita qo'shimcha daraxt yaratadi:
class MyButton extends StatelessWidget {
final String label;
const MyButton({super.key, required this.label});
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {},
child: Text(label), // Har build()da yangi Text widget yaratiladi
);
}
}MyButton qayta build bo'lganda, yangi Text(label) obyekti yaratiladi —
lekin agar ekrandagi RenderParagraph (matnni chizadigan haqiqiy render
obyekti) allaqachon mavjud bo'lsa va faqat matn o'zgargan bo'lsa, Flutter
eski RenderObject'ni saqlab qolib, unga yangi ma'lumotni beradi, uni
qaytadan yaratmaydi. Aynan shu optimallashtirish — Element daraxtining asosiy
vazifasi.
2. Element — widget bilan render orasidagi "xotira"#
Element — Flutter arxitekturasining eng kam tushuniladigan, lekin eng muhim
qismi. Har bir widget createElement() metodiga ega, va u widget uchun mos
Element obyektini yaratadi:
abstract class Widget {
Element createElement();
}
class StatelessWidget extends Widget {
@override
StatelessElement createElement() => StatelessElement(this);
}
class StatefulWidget extends Widget {
@override
StatefulElement createElement() => StatefulElement(this);
}Element uchta narsani biladi:
- Qaysi widget uni yaratgani (
widgetmaydoni) — bu widget daraxti qayta build bo'lganda yangilanadi. - O'zining RenderObject'i (agar bo'lsa) yoki farzand Element'lari.
- BuildContext sifatida o'zi ishlaydi —
BuildContextinterfeysi vaElementklassi Flutter'da bir xil obyekt, faqat ikki xil interfeys orqali ko'rinadi.
3. Reconciliation — Flutter qachon yangi Element yaratadi, qachon eskisini saqlaydi#
Bu — arxitektura savollarining markazi. Widget qayta build bo'lganda, Flutter
eski widget bilan yangi widgetni solishtiradi (Widget.canUpdate):
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}Ikki shart: bir xil turdagi klass va bir xil key (yoki ikkalasida
ham key yo'q). Agar bu shart bajarilsa, Element saqlab qolinadi — uning
State obyekti (agar StatefulWidget bo'lsa) va RenderObject'i o'zgarmaydi,
faqat update() chaqirilib, ichidagi widget ko'rsatkichi yangilanadi.
Agar shart bajarilmasa, eski Element butunlay yo'q qilinadi
(unmount(), State.dispose() chaqiriladi) va o'rniga yangi Element
yaratiladi (State.initState() qaytadan ishlaydi).
// Ikkala holatda ham Column bir xil bo'lib qoladi (Column === Column, key yo'q)
// lekin ichidagi widget turi o'zgarsa, mos Element almashtiriladi:
Column(
children: [
showIcon ? const Icon(Icons.star) : const Text('Yulduz'),
// ^ turi o'zgarganda: eski Element yo'q qilinadi, yangisi yaratiladi
],
)Bu — nega ro'yxat elementlari almashtirilganda (masalan, filtrlashdan keyin)
State "aralashib ketishi" mumkinligining sababi: Flutter faqat turi va
pozitsiyasi bo'yicha solishtiradi, key bo'lmasa mazmuni bo'yicha emas.
Buni to'liq Keys mavzusida keyingi bobda ko'ramiz.
4. setState chaqirilganda aniq nima sodir bo'ladi#
setState(() { ... }) — sehrli funksiya emas, oddiy mexanizm:
Callback ishga tushadi
setState'ga berilgan funksiya darhol, sinxron ravishda chaqiriladi — u ichida State maydonlarini o'zgartirasiz (masalan,count++).Element 'dirty' deb belgilanadi
State._element!.markNeedsBuild()chaqiriladi. Bu Element'ni framework'ning "qayta build kerak" ro'yxatiga qo'shadi. Hech qanday build hozircha sodir bo'lmaydi — bu faqat belgi.Keyingi frame'gacha kutiladi
Flutter darhol qayta chizmaydi.
markNeedsBuild()WidgetsBinding'ga "menga keyingi frame kerak" deb signal beradi, va framework barcha "dirty" Element'larni bitta frame ichida,scheduleFrameorqali qayta quradi.Faqat shu Element va uning bolalar daraxti qayta build bo'ladi
Butun ilova emas — faqat
setStatechaqirilgan State'ga tegishli Element va undan pastdagilar. Ota (parent) widget'lar qayta build bo'lmaydi (agar ular ham alohida o'zgarmagan bo'lsa).Yangi widget daraxti eski Element daraxti bilan solishtiriladi
3-bo'limdagi
canUpdatemantig'i ishga tushadi: mos RenderObject'lar saqlanadi, faqat o'zgargan xususiyatlar yangilanadi.
class Counter extends StatefulWidget {
const Counter({super.key});
@override
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int _count = 0;
void _increment() {
setState(() {
_count++; // Faqat holatni o'zgartiradi — build() shu yerda chaqirilmaydi
});
}
@override
Widget build(BuildContext context) {
return Text('$_count'); // setState keyin, keyingi frame'da chaqiriladi
}
}5. Rendering pipeline — build'dan pikselgacha#
Bitta frame ichida Flutter to'rtta bosqichdan o'tadi:
- 1
- Build'Dirty' Element'lar uchun build() chaqiriladi, yangi widget subtree'lar yaratiladi va Element/RenderObject daraxtlariga moslashtiriladi
- 2
- LayoutHar bir RenderObject ota-onasidan constraints (cheklovlar) oladi, o'z o'lchamini hisoblaydi va bolalarini joylashtiradi — bitta pastga-yuqoriga o'tish (keyingi bobda batafsil)
- 3
- PaintHar bir RenderObject o'zini Layer'lar (Canvas buyruqlari) ro'yxatiga chizadi — bu haligacha piksel emas, chizish buyruqlari
- 4
- CompositeGPU Layer'larni birlashtirib, haqiqiy piksellarni ekranga chiqaradi (Skia/Impeller renderer orqali)
Muhim nuans: Layout va Paint har doim ham to'liq daraxt bo'ylab
takrorlanmaydi. Agar faqat bitta RenderObject'ning rangi o'zgargan bo'lsa
(o'lchami emas), Flutter faqat shu obyektni qayta chizadi (markNeedsPaint),
layout'ni butunlay qayta hisoblamaydi (markNeedsLayout). Bu ikkalasi —
alohida, mustaqil optimallashtirish yo'llari.
6. Element lifecycle — mount, update, unmount, deactivate#
// Konseptual, soddalashtirilgan Element lifecycle:
element.mount(parent, slot); // Birinchi marta daraxtga qo'shiladi
element.update(newWidget); // Mos widget bilan qayta ishlatiladi
element.deactivate(); // Daraxtdan vaqtincha olib tashlanadi (masalan, GlobalKey orqali ko'chirilmoqda)
element.activate(); // Boshqa joyga qayta qo'shilsa
element.unmount(); // Butunlay yo'q qilinadi — State.dispose() shu yerda chaqiriladideactivate/activate jufti — kam ma'lum, lekin muhim holat: agar widget
GlobalKey bilan bir frame ichida daraxtning boshqa joyiga ko'chirilsa,
Flutter uni yo'q qilib qayta yaratmaydi, balki shu Elementni yangi joyga
ko'chiradi (deactivate → activate), State saqlanib qoladi. Aynan shu —
GlobalKeyning eng kuchli, lekin qimmat xususiyati.
Tez-tez so'raladigan intervyu savollari#
Savol: Widget bilan Element o'rtasidagi farq nima?
Javob: Widget — o'zgarmas (immutable) konfiguratsiya obyekti, har
build()da qaytadan yaratiladi va arzon. Element — shu konfiguratsiyaning
daraxtdagi "joniga" mos keluvchi, hayot davomiyligi (lifecycle) bor obyekt:
u Stateni, RenderObject bilan bog'lanishni va daraxtdagi joyini eslab
qoladi. Widget'lar tez-tez yaratiladi va tashlanadi, lekin ularga mos
Element ko'pincha reconciliation orqali saqlanib qoladi.
Savol: BuildContext va Element bir xil narsami?
Javob: Ha — Element klassi BuildContext interfeysini implement
qiladi. build(BuildContext context) parametri sifatida kelayotgan narsa —
aynan shu widget uchun yaratilgan Element, faqat "faqat ma'lumot o'qish"
uchun cheklangan interfeys ko'rinishida.
Savol: Nega Flutter uchta alohida daraxt ishlatadi, ikkita emas?
Javob: Widget daraxti — arzon, tez-tez qayta yaratiladigan konfiguratsiya. RenderObject daraxti — qimmat, layout/paint mantig'ini bajaradigan qatlam. Agar bu ikkitasi to'g'ridan-to'g'ri bog'langan bo'lsa, har build'da RenderObject'larni ham qayta yaratishga to'g'ri kelardi. Element daraxti — ular orasidagi vositachi: widget o'zgarganda RenderObject'ni qayta ishlatish yoki yangilashga qaror qiluvchi qatlam.
Savol: setState chaqirilgach, build() darhol ishga tushadimi?
Javob: Yo'q. setState faqat Element'ni "dirty" deb belgilaydi va
keyingi frame'da qayta build so'raladi. Haqiqiy build() chaqiruvi Flutter
engine keyingi frame'ni chizishga tayyor bo'lganda sodir bo'ladi — odatda
millisekundlar ichida, lekin sinxron emas.
Savol: Widget.canUpdate qanday ishlaydi va nega muhim?
Javob: U ikkita shartni tekshiradi: runtimeType bir xilmi va key
bir xilmi. Ikkalasi ham to'g'ri bo'lsa, Element saqlanadi va faqat
yangilanadi; aks holda eski Element yo'q qilinib (dispose chaqirilib),
yangisi yaratiladi. Bu — reconciliationning yuragi va nega noto'g'ri/yo'q
key ishlatish Statening yo'qolishiga olib kelishining sababi.
Savol: Agar parent widget qayta build bo'lsa, uning barcha farzandlari ham majburan qayta build bo'ladimi?
Javob: Yo'q, shart emas. Agar farzand const konstruktor bilan
yaratilgan bo'lsa va parametrlar o'zgarmagan bo'lsa, Flutter bir xil widget
instansini qayta ishlatadi va uni qayta build qilmaydi. const bo'lmagan
farzandlar odatda qayta yaratiladi (yangi widget obyekti), lekin bu ularning
RenderObject'i albatta qayta chizilishini anglatmaydi — reconciliation
baribir ishlaydi.
Savol: Layout va Paint bosqichlari doim birga qayta ishlaydimi?
Javob: Yo'q. Bular mustaqil: markNeedsLayout() faqat o'lcham/joylashuv
o'zgarganda, markNeedsPaint() faqat vizual ko'rinish (masalan, rang)
o'zgarganda chaqiriladi. Faqat rangi o'zgargan widget uchun butun daraxtni
qayta layout qilish shart emas — bu ikkita alohida optimallashtirish yo'li.
Savol: deactivate() va unmount() orasidagi farq nima?
Javob: deactivate() — Element daraxtdan vaqtincha olib tashlanganda
chaqiriladi (masalan, GlobalKey orqali boshqa joyga ko'chirilayotganda);
agar shu frame ichida qayta activate() qilinmasa, keyin unmount()
chaqiriladi va State.dispose() ishga tushadi. Ya'ni deactivate — vaqtinchalik,
unmount — yakuniy va qaytarib bo'lmaydigan holat.
Savol: Nega RenderObject widget'dan ancha qimmatroq hisoblanadi?
Javob: Widget — bir nechta final maydonli oddiy Dart obyekti,
yaratish narxi deyarli nol. RenderObject esa layout hisoblash (constraint
solve), paint buyruqlarini generatsiya qilish va GPU bilan bog'liq
resurslarni boshqarish kabi og'ir ishlarni bajaradi — shuning uchun Flutter
uni iloji boricha qayta ishlatishga harakat qiladi, qaytadan yaratishga emas.
Savol: InheritedWidget orqali ma'lumot olish qanday reconciliation
bilan bog'liq?
Javob: context.dependOnInheritedWidgetOfExactType<T>() chaqirilganda,
Element o'zini shu InheritedElementning "bog'liqlar" (dependents)
ro'yxatiga yozdiradi. InheritedWidget yangilanganda (updateShouldNotify
true qaytarsa), faqat shu ro'yxatdagi Element'lar markNeedsBuild()
qilinadi — butun daraxt emas. Bu alohida, InheritedWidget'ga xos rebuild
mexanizmi, keyingi bobda batafsil ko'riladi.
Savol: Hot reload Element daraxtiga qanday ta'sir qiladi?
Javob: Hot reload kodni almashtiradi, lekin Element va State daraxtini
saqlashga harakat qiladi — shuning uchun initStateda bajarilgan ishlar
(masalan, animatsiya boshlash) hot reload paytida qayta ishga tushmaydi.
Faqat build() metodlari qayta chaqiriladi. Agar klass strukturasi tubdan
o'zgarsa (masalan, State klassi generic parametri o'zgarsa), Flutter buni
avtomatik aniqlay olmay, to'liq restart talab qilishi mumkin.
Ko'p uchraydigan xato javoblar#
| Savol / vaziyat | Junior darajadagi noto'g'ri javob | Senior darajadagi to'g'ri javob |
|---|---|---|
| "Widget nima?" | "Ekrandagi UI elementi" | "UI elementining o'zgarmas konfiguratsiyasi; haqiqiy ekran holatini RenderObject ushlab turadi" |
| "setState nima qiladi?" | "Ekranni qayta chizadi" | "Element'ni 'dirty' deb belgilaydi va keyingi frame'da qayta build so'raydi — darhol chizmaydi" |
| "BuildContext nima?" | "build() metodining parametri, unchalik muhim emas" | "Element'ning o'zi — daraxt bo'ylab yuqoriga qarab ma'lumot qidirish uchun interfeys" |
| "Har build()da nima sodir bo'ladi?" | "Butun ekran qayta chiziladi" | "Faqat yangi widget obyektlari yaratiladi; RenderObject'lar reconciliation orqali saqlanadi yoki yangilanadi, qayta chizish shart emas" |
Amaliyot#
Rebuild'ni kuzatib chiqing
Bitta StatefulWidget yarating, ichida ikkita farzand widget bo'lsin: biri
const bilan belgilangan, ikkinchisi — yo'q. Har ikkalasining build()
metodiga debugPrint qo'shing. setState chaqirib, qaysi biri qayta
build bo'lishini, qaysi biri bo'lmasligini kuzating va nega shunday
bo'lishini tushuntiring.
Maslahatni ko'rsatish
StatelessWidget'ning build() metodi ichiga print() yoki debugPrint() qo'shing va qachon chaqirilishini konsolda kuzating — parent qayta build bo'lganda farzand ham chaqiriladimi?