Tarkibga o'tish

15 β€” Asosiy UI widgetlar

⬅️ Oldingi: 14 β€” Material 3, Cupertino va theming Β· 🏠 README Β· Keyingi: 16 β€” StatefulWidget va setState ➑️


Bu bobda: endi qurilish g'ishtlari bilan tanishamiz β€” deyarli har bir ilova ishlatadigan asosiy UI widgetlar katalogi. Material 3 tugmalar ierarxiyasini (FilledButton, ElevatedButton, FilledButton.tonal, OutlinedButton, TextButton, IconButton, FloatingActionButton) va qaysi birini qachon ishlatishni o'rganamiz; TextField va uning InputDecoration qismlarini bo'lak-bo'lak ko'ramiz; tanlov boshqaruvlari β€” Checkbox, Switch, Radio, Slider; tarkibni guruhlovchi Card; ro'yxatlarning ishchi oti ListTile; Chip oilasi; va foydalanuvchiga fikr-mulohaza (feedback) berish β€” SnackBar, AlertDialog, progress indikatorlari. Oxirida bularni birlashtirib sozlamalar ekrani va login formasi layout'ini quramiz. Muhim ogohlantirish: bu yerda widgetlarning shaklini ko'rsatamiz; ularni jonli (bosilganda haqiqatan o'zgaradigan) qilish uchun holat kerak β€” bu keyingi, 16-bob mavzusi.


Katalog boshlanishidan oldin: nega bu boblar muhim

Oldingi boblarda layout'ni (Row, Column, Container, Stack) va Material 3 mavzusini (14-bob) ko'rdik β€” ya'ni narsalarni qayerga qo'yishni va ular qanday rang olishini bilamiz. Endi esa shu joylarga nima qo'yishni o'rganamiz: tugmalar, matn maydonlari, kalitlar (switch), kartochkalar.

Buni oshxonadagi idishlar to'plamiga o'xshating. Sizda allaqachon javon (layout) va dizayn (theme) bor. Bu bob β€” idishlarning o'zi: piyola, tarelka, qoshiq. Har birini bir marta tanisangiz, keyin har bir ovqatda (ilovada) qayta-qayta ishlatasiz. Shuning uchun bu bobni lug'at sifatida o'qing β€” hozir bir o'qib chiqing, keyin kerak bo'lganda qaytib kelib qaraysiz.

πŸ’‘ Bitta tushunchani oldindan aytib qo'yamiz. Quyidagi ko'p widgetlarda onPressed:, onChanged:, onTap: kabi parametrlar bor β€” bular foydalanuvchi tegiganda chaqiriladigan funksiya. Bu yerda biz ularning ichida ko'pincha print(...) yozamiz yoki "16-bobda jonli qilamiz" deymiz. Sababi: tugma bosilganda ekrandagi qiymatni haqiqatan o'zgartirish uchun StatefulWidget va setState kerak β€” bu 16-bobning mavzusi. Hozircha shaklni o'rganamiz.

Tugmalar β€” Material 3 ierarxiyasi

Tugma β€” foydalanuvchi bosadigan, biror harakatni boshlaydigan element. Flutter'da bitta "tugma" emas, balki bir nechta turdagi tugma bor. Nega? Chunki ekrandagi har bir tugma bir xil darajada muhim emas. "Saqlash" tugmasi "Bekor qilish"dan muhimroq; "Batafsil" havolasi ulardan ham past. Material 3 shu muhimlik darajasini β€” urg'u (emphasis) β€” tugma ko'rinishi orqali ifodalashni taklif qiladi.

Material 3 tugmalar urg'u ierarxiyasi

Qoida sodda: ekrandagi eng muhim, asosiy harakatni eng baland urg'uli tugma bilan ko'rsating, qolganlarini pastroq bilan. Bitta ekranda odatda faqat bitta yuqori urg'uli (to'la rangli) tugma bo'lishi kerak β€” aks holda foydalanuvchining ko'zi qayerga qarashni bilmaydi.

FilledButton β€” yuqori urg'u (asosiy harakat)

Eng muhim, birlamchi harakat uchun. To'la rangli foni bilan ko'zga eng yaqqol tashlanadi.

FilledButton(
  onPressed: () {
    print('Saqlandi!');
  },
  child: const Text('Saqlash'),
)

Eng muhim parametr β€” onPressed:. Bu tugma bosilganda ishga tushadigan funksiya. Va mana bitta juda muhim qoida:

πŸ’‘ onPressed: null β€” tugmani o'chiradi (disabled). Agar onPressed ga null bersangiz, tugma kulrang va bosilmaydigan bo'lib qoladi. Bu juda foydali: masalan, forma to'liq to'ldirilmaguncha "Yuborish" tugmasini null qilib o'chirib qo'yasiz. (Buni dinamik qilish β€” qachon null, qachon funksiya β€” ham holatga bog'liq, 16-bob.)

const FilledButton(
  onPressed: null,            // o'chirilgan β€” kulrang, bosilmaydi
  child: Text('Hozircha mumkin emas'),
)

ElevatedButton va FilledButton.tonal β€” o'rta urg'u

ElevatedButton β€” biroz soyali (ko'tarilgan) tugma. Rangli yoki rasmli fon ustida joylashganda yaxshi ajraladi.

ElevatedButton(
  onPressed: () => print('bosildi'),
  child: const Text('Qo\'shish'),
)

FilledButton.tonal β€” FilledButton'ning yumshoqroq, ochroq rangli ko'rinishi. Ikkilamchi, lekin baribir e'tiborni tortadigan harakatlar uchun ("yuqori"dan past, "outlined"dan baland).

FilledButton.tonal(
  onPressed: () => print('tonal bosildi'),
  child: const Text('Saralash'),
)

OutlinedButton β€” o'rta/past urg'u

Faqat hoshiya (chiziq) bilan, ichi bo'sh. Ko'pincha "Bekor qilish" yoki ikkilamchi tanlov uchun, FilledButton yonida juftlik bo'lib turadi.

OutlinedButton(
  onPressed: () => print('bekor qilindi'),
  child: const Text('Bekor qilish'),
)

TextButton β€” past urg'u

Eng kam ko'zga tashlanadigan tugma β€” faqat matn, fonsiz va hoshiyasiz. "Batafsil", "Parolni unutdingizmi?", dialog ichidagi tugmalar uchun.

TextButton(
  onPressed: () => print('batafsil'),
  child: const Text('Batafsil'),
)

Ikonkali tugmalar (.icon konstruktori)

Yuqoridagi har bir tugmaning .icon ko'rinishi bor β€” matn yoniga ikonka qo'shadi. Bu harakatning ma'nosini tezroq tushuntiradi.

FilledButton.icon(
  onPressed: () => print('yuklab olinmoqda'),
  icon: const Icon(Icons.download),
  label: const Text('Yuklab olish'),   // bu yerda child emas, label!
)

πŸ’‘ E'tibor bering: oddiy konstruktorda child: bor edi, .icon konstruktorida esa icon: va label: bo'ladi.

IconButton β€” faqat ikonka

Matnsiz, faqat ikonka. Joy tejaydi β€” shuning uchun AppBarda, satr ichida, ro'yxat oxirida ko'p ishlatiladi (qidiruv, sozlamalar, yopish belgisi).

IconButton(
  onPressed: () => print('qidiruv'),
  icon: const Icon(Icons.search),
  tooltip: 'Qidirish',     // ustiga bosib turganda chiqadigan maslahat
)

FloatingActionButton (FAB) β€” ekranning asosiy harakati

Dumaloq, suzib turadigan tugma β€” odatda ekranning pastki-o'ng burchagida. Ekrandagi eng asosiy harakat uchun ishlatiladi (masalan "yangi xabar", "+"). U Scaffoldning maxsus uyasiga joylanadi:

Scaffold(
  appBar: AppBar(title: const Text('Vazifalar')),
  body: const Center(child: Text('Ro\'yxat shu yerda')),
  floatingActionButton: FloatingActionButton(
    onPressed: () => print('yangi vazifa'),
    tooltip: 'Qo\'shish',
    child: const Icon(Icons.add),
  ),
)

Tugma ko'rinishini sozlash β€” styleFrom

Tugma rangi yoki o'lchamini o'zgartirmoqchi bo'lsangiz, har bir tugma turining styleFrom yordamchisi bor. U ButtonStyle obyektini qulay tarzda yasab beradi:

FilledButton(
  onPressed: () {},
  style: FilledButton.styleFrom(
    backgroundColor: Colors.green,
    padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
  ),
  child: const Text('Yashil tugma'),
)

πŸ’‘ Ko'pincha buning kerak ham bo'lmaydi: 14-bobdagi ColorScheme allaqachon barcha tugmalarga uyg'un ranglar beradi. styleFromni faqat alohida tugmani ajratmoqchi bo'lganingizda ishlating.

TextField β€” matn kiritish maydoni

TextField β€” foydalanuvchi klaviaturadan matn yozadigan maydon: ism, email, parol, izoh. Bu deyarli har bir formada bor.

Eng muhim parametr β€” decoration:, unga InputDecoration(...) beriladi. Aynan shu maydonning ko'rinishini β€” yorliq, maslahat matni, ikonkalar, hoshiya β€” boshqaradi. Quyidagi rasmda har bir qism qaysi xususiyatdan kelishini ko'ring:

TextField anatomiyasi va InputDecoration qismlari

TextField(
  decoration: InputDecoration(
    labelText: 'Email',                 // suzuvchi yorliq
    hintText: 'misol@pochta.uz',        // bo'sh holatdagi maslahat
    helperText: 'Emailingizni kiriting', // pastdagi yordam matni
    prefixIcon: const Icon(Icons.email), // oldidagi ikonka
    border: const OutlineInputBorder(),  // to'liq hoshiya
  ),
)

InputDecorationning eng ko'p ishlatiladigan qismlari:

Xususiyat Nima qiladi
labelText Maydon ustida turuvchi yorliq (fokuslanganda suzib yuqoriga chiqadi).
hintText Maydon bo'sh bo'lganda ichida ko'rinadigan kulrang maslahat.
helperText Maydon ostidagi kichik yordam matni.
prefixIcon / suffixIcon Maydon boshidagi / oxiridagi ikonka.
border: OutlineInputBorder() Maydonni to'liq ramka (hoshiya) bilan o'raydi.

Klaviatura turi va parol

keyboardType β€” qaysi turdagi klaviatura chiqishini belgilaydi (raqamlar uchun raqamli klaviatura qulayroq). obscureText: true esa yozilgan matnni nuqtalar bilan yashiradi β€” parol uchun shart.

// Raqam kiritish uchun
TextField(
  keyboardType: TextInputType.number,
  decoration: const InputDecoration(labelText: 'Yosh'),
)

// Email uchun
TextField(
  keyboardType: TextInputType.emailAddress,
  decoration: const InputDecoration(labelText: 'Email'),
)

// Parol uchun β€” yozilgan belgilar yashiriladi
TextField(
  obscureText: true,
  decoration: const InputDecoration(
    labelText: 'Parol',
    prefixIcon: Icon(Icons.lock),
  ),
)

Kiritilgan matnni qanday o'qiymiz?

Foydalanuvchi yozgan matnni olishning ikki yo'li bor:

  • onChanged: β€” har bir belgi o'zgarganda chaqiriladigan funksiya, yangi matnni argument sifatida beradi:
TextField(
  onChanged: (matn) {
    print('Hozir yozilgan: $matn');
  },
)
  • controller: β€” TextEditingController orqali maydonni boshqarish (matnni o'qish, tozalash, oldindan to'ldirish).

πŸ’‘ TextEditingController, forma validatsiyasi (Form + TextFormField bilan "email noto'g'ri" kabi tekshiruvlar) β€” bularning hammasini 17-bobda keyin, formalarga bag'ishlangan bo'limda batafsil ko'ramiz. Hozircha onChanged bilan matnni "eshitishni" biling β€” bu yetarli.

Tanlov boshqaruvlari β€” Checkbox, Switch, Radio, Slider

Bular foydalanuvchi tanlov qiladigan widgetlar: belgilash katakchasi, yoqish/o'chirish kaliti, variantlardan birini tanlash, qiymatni surg'ich bilan o'rnatish.

Hammasining umumiy shakli bir xil: ular value (hozirgi qiymat) va onChanged (qiymat o'zgarganda chaqiriladigan funksiya) oladi.

⚠️ Juda muhim: bu widgetlar o'zicha o'zgarmaydi. Siz value: ga bergan qiymatni ko'rsatadi, xolos. Foydalanuvchi bosganda onChanged chaqiriladi β€” lekin ekranda haqiqatan belgi qo'yilishi uchun siz valueni yangilab, qaytadan qurishingiz kerak. Buning uchun esa holat (state) lozim. Quyida widgetlarning shaklini ko'rsatamiz; ularni jonli qilishni 16-bobda o'rganamiz. Shuning uchun onChanged ichida hozircha print yozamiz.

Checkbox β€” belgilash katakchasi

Ha/yo'q tanlovi uchun (masalan "Shartlarga roziman").

Checkbox(
  value: true,                      // hozirgi holat (belgilangan)
  onChanged: (yangi) {
    print('Checkbox: $yangi');      // 16-bobda value'ni yangilaymiz
  },
)

Switch β€” yoqish/o'chirish kaliti

Sozlamalarda biror narsani yoqish/o'chirish uchun (Wi-Fi, bildirishnomalar).

Switch(
  value: false,
  onChanged: (yangi) => print('Switch: $yangi'),
)

Radio / RadioGroup β€” bir nechtadan bittasini tanlash

Radio β€” bir-birini istisno qiladigan variantlar uchun (faqat bittasi tanlangan bo'ladi: masalan "Erkak / Ayol"). Material 3'da bir guruh radiolarni RadioGroup bilan o'rab, umumiy tanlangan qiymatni boshqariladi:

RadioGroup<String>(
  groupValue: 'erkak',                  // hozir tanlangan variant
  onChanged: (yangi) => print('Tanlandi: $yangi'),
  child: const Column(
    children: [
      RadioListTile<String>(
        value: 'erkak',
        title: Text('Erkak'),
      ),
      RadioListTile<String>(
        value: 'ayol',
        title: Text('Ayol'),
      ),
    ],
  ),
)

Slider β€” surg'ich bilan qiymat tanlash

Diapazondagi qiymatni (ovoz balandligi, yorqinlik) tanlash uchun.

Slider(
  value: 0.4,           // 0.0 dan 1.0 gacha (standart diapazon)
  onChanged: (yangi) => print('Slider: $yangi'),
)

Qulay ko'rinishlar β€” CheckboxListTile va SwitchListTile

Amalda siz ko'pincha katakcha yoniga yorliq matni ham qo'yishni istaysiz. Buni qo'lda Row qurmasdan, tayyor CheckboxListTile va SwitchListTile widgetlari hal qiladi β€” ular Checkbox/Switchni ListTile (matnli satr) ichiga joylab beradi:

SwitchListTile(
  value: true,
  onChanged: (yangi) => print('Bildirishnoma: $yangi'),
  title: const Text('Bildirishnomalar'),
  subtitle: const Text('Yangiliklar haqida xabar berish'),
  secondary: const Icon(Icons.notifications),
)

Sozlamalar ekranida aynan shu ko'rinish eng ko'p kerak bo'ladi.

Card β€” tarkibni guruhlash

Card β€” burchaklari yumaloqlangan, biroz soyali "kartochka". U bog'liq ma'lumotlarni bir guruh qilib, qolganidan vizual ajratadi (mahsulot, profil, xabar kartochkasi).

Card(
  elevation: 2,                       // soya darajasi (balandlik)
  child: Padding(
    padding: const EdgeInsets.all(16),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: const [
        Text('Flutter kursi', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
        SizedBox(height: 8),
        Text('0 dan ekspertgacha. 26 ta bob.'),
      ],
    ),
  ),
)

πŸ’‘ Cardning o'zi ichki bo'shliq (padding) bermaydi β€” shuning uchun ichiga ko'pincha Padding qo'yiladi. shape: orqali burchak shaklini, elevation: orqali soyani sozlashingiz mumkin.

ListTile β€” ro'yxatlarning ishchi oti

ListTile β€” eng ko'p ishlatiladigan satr widget. Ro'yxatlarda, menyularda va ayniqsa sozlamalar ekranlarida har bir qatorni shu yasaydi. U tayyor, standart joylashuvga ega bo'lgan to'rt uyani beradi:

ListTile anatomiyasi β€” sozlamalar satri

ListTile(
  leading: const Icon(Icons.person),    // chapdagi ikonka/avatar
  title: const Text('Profil'),          // asosiy matn
  subtitle: const Text('Ism, rasm, bio'), // qo'shimcha matn
  trailing: const Icon(Icons.chevron_right), // o'ngdagi belgi (o'q)
  onTap: () => print('Profil ochildi'),  // butun satr bosiladi
)

ListTilening uyalari:

Uya Odatda nima qo'yiladi
leading Chapdagi ikonka yoki avatar.
title Asosiy matn (sarlavha).
subtitle Title ostidagi qo'shimcha matn.
trailing O'ngdagi element β€” o'q (Icons.chevron_right), Switch yoki Chip.
onTap Butun satr bosilganda chaqiriladi.

πŸ’‘ onTap β€” ListTileni butunlay bosiladigan qiladi: foydalanuvchi satrning istalgan joyiga tegsa ishlaydi, faqat ikonkaga emas. Sozlamalar va menyularda navigatsiya uchun shu juda qulay.

Chip oilasi β€” ixcham yorliq/tanlovlar

Chip β€” kichik, dumaloq burchakli "tabletka" shaklidagi element. Teglar, filtrlar yoki kichik harakatlar uchun ishlatiladi.

// Oddiy yorliq
const Chip(label: Text('Flutter'))

// Bosiladigan harakat chip'i
ActionChip(
  label: const Text('Ulashish'),
  avatar: const Icon(Icons.share, size: 18),
  onPressed: () => print('ulashildi'),
)

// Filtr chip'i β€” tanlanadigan/o'chiriladigan (holat kerak β€” 16-bob)
FilterChip(
  label: const Text('Bepul'),
  selected: true,
  onSelected: (tanlandi) => print('Filtr: $tanlandi'),
)

ActionChip bosilganda biror harakat qiladi; FilterChip esa tanlanadigan (belgilanadigan) bo'lib, filtrlar uchun ishlatiladi β€” uning ham haqiqiy tanlanishi holatga bog'liq.

Fikr-mulohaza (feedback) widgetlari

Foydalanuvchiga "men sizni eshitdim" deb bildirish ham UI'ning muhim qismi. Eng ko'p ishlatiladiganlari:

SnackBar β€” pastdan chiqadigan qisqa xabar

Ekran pastida bir necha soniya ko'rinib yo'qoladigan kichik xabar ("Saqlandi", "Internet yo'q"). Uni ScaffoldMessenger orqali ko'rsatasiz:

ElevatedButton(
  onPressed: () {
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('Saqlandi!')),
    );
  },
  child: const Text('Saqlash'),
)

πŸ’‘ ScaffoldMessenger.of(context) β€” contextdan eng yaqin Scaffoldning "xabarchisi"ni topadi va unga SnackBar ko'rsatishni buyuradi. context haqida 10-bobda gaplashgandik β€” bu widgetning daraxtdagi o'rni.

AlertDialog β€” modal dialog

Foydalanuvchidan tasdiq so'raydigan oyna ("O'chirishni xohlaysizmi?"). showDialog bilan chiqariladi:

showDialog(
  context: context,
  builder: (context) => AlertDialog(
    title: const Text('O\'chirish'),
    content: const Text('Rostdan ham o\'chirilsinmi?'),
    actions: [
      TextButton(
        onPressed: () => Navigator.pop(context),  // dialogni yopish
        child: const Text('Yo\'q'),
      ),
      FilledButton(
        onPressed: () => Navigator.pop(context),
        child: const Text('Ha'),
      ),
    ],
  ),
);

E'tibor bering β€” dialog ichidagi tugmalar ham aynan o'sha tanish TextButton va FilledButton (past urg'u "Yo'q", yuqori urg'u "Ha"). Dialoglar va navigatsiyani to'liq 19-bobda ko'ramiz.

Progress indikatorlari β€” kutilmoqda

Biror narsa yuklanayotganini bildiradi:

const CircularProgressIndicator()   // aylanadigan dumaloq
const LinearProgressIndicator()     // gorizontal chiziq

Qiymat bermasangiz, ular cheksiz (qancha vaqt qolganini bilmaganda) animatsiya qiladi. Aniq foiz ma'lum bo'lsa, value: 0.7 kabi bersa, to'lib boradigan ko'rsatkich bo'ladi.

Hammasini birlashtiramiz

Endi bu widgetlarni alohida emas, birga β€” haqiqiy ekranlarga yig'amiz. Ana shunda ularning kuchini his qilasiz.

1) Sozlamalar ekrani β€” ListTile + SwitchListTile

Sozlamalar ekrani β€” bu deyarli butunlay ListTile va SwitchListTilelardan tuzilgan ro'yxat. Card ichiga joylab, chiroyli guruh qilamiz:

import 'package:flutter/material.dart';

class SozlamalarEkrani extends StatelessWidget {
  const SozlamalarEkrani({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Sozlamalar')),
      body: ListView(
        children: [
          const Card(
            margin: EdgeInsets.all(12),
            child: Column(
              children: [
                SwitchListTile(
                  value: true,
                  onChanged: _stub,           // 16-bobda jonli qilamiz
                  secondary: Icon(Icons.notifications),
                  title: Text('Bildirishnomalar'),
                ),
                SwitchListTile(
                  value: false,
                  onChanged: _stub,
                  secondary: Icon(Icons.dark_mode),
                  title: Text('Tungi rejim'),
                ),
              ],
            ),
          ),
          Card(
            margin: const EdgeInsets.all(12),
            child: Column(
              children: [
                ListTile(
                  leading: const Icon(Icons.person),
                  title: const Text('Profil'),
                  subtitle: const Text('Ism, rasm'),
                  trailing: const Icon(Icons.chevron_right),
                  onTap: () => print('Profil'),
                ),
                ListTile(
                  leading: const Icon(Icons.lock),
                  title: const Text('Maxfiylik'),
                  trailing: const Icon(Icons.chevron_right),
                  onTap: () => print('Maxfiylik'),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  // Switch hozircha qiymatni o'zgartira olmaydi (holat 16-bobda) β€”
  // shuning uchun shunchaki "tegildi" deb chop etamiz.
  static void _stub(bool yangi) => print('Switch: $yangi');
}

πŸ’‘ ListView β€” bolalarni vertikal aylantiriladigan (scroll) ro'yxat qilib joylaydi (ko'p elementlarda Columndan farqli ravishda chetiga sig'maganda aylantirib ko'rsatadi). Ro'yxatlarni keyingi bobda batafsil ko'ramiz.

2) Login formasi layout'i β€” TextField + FilledButton

Login ekrani β€” ikkita TextField (email va parol) va bitta yuqori urg'uli FilledButton. Pastida past urg'uli TextButton ("Parolni unutdingizmi?"):

import 'package:flutter/material.dart';

class LoginEkrani extends StatelessWidget {
  const LoginEkrani({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Kirish')),
      body: Padding(
        padding: const EdgeInsets.all(24),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const TextField(
              keyboardType: TextInputType.emailAddress,
              decoration: InputDecoration(
                labelText: 'Email',
                prefixIcon: Icon(Icons.email),
                border: OutlineInputBorder(),
              ),
            ),
            const SizedBox(height: 16),
            const TextField(
              obscureText: true,
              decoration: InputDecoration(
                labelText: 'Parol',
                prefixIcon: Icon(Icons.lock),
                border: OutlineInputBorder(),
              ),
            ),
            const SizedBox(height: 24),
            // To'la kenglikdagi asosiy tugma
            SizedBox(
              width: double.infinity,
              child: FilledButton(
                onPressed: () => print('Kirish bosildi'),
                child: const Text('Kirish'),
              ),
            ),
            TextButton(
              onPressed: () => print('Parolni tiklash'),
              child: const Text('Parolni unutdingizmi?'),
            ),
          ],
        ),
      ),
    );
  }
}

πŸ’‘ SizedBox(width: double.infinity, child: FilledButton(...)) β€” bu tugmani ota maydonning to'liq kengligiga cho'zadi. Login tugmalari odatda shunday keng bo'ladi. Hozircha yozilgan matnni o'qimaymiz (controller β€” 17-bob), faqat layout va tugmani ko'rsatyapmiz.

3) Kartochkalar ro'yxati

Card + ListTile birikmasi β€” eng ko'p uchraydigan "kontent ro'yxati" namunasi (mahsulotlar, maqolalar, kontaktlar):

ListView(
  children: const [
    Card(
      margin: EdgeInsets.symmetric(horizontal: 12, vertical: 6),
      child: ListTile(
        leading: Icon(Icons.book),
        title: Text('Flutter kitobi'),
        subtitle: Text('26 bob Β· boshlovchilar uchun'),
        trailing: Icon(Icons.chevron_right),
      ),
    ),
    Card(
      margin: EdgeInsets.symmetric(horizontal: 12, vertical: 6),
      child: ListTile(
        leading: Icon(Icons.code),
        title: Text('Dart asoslari'),
        subtitle: Text('9 bob Β· til poydevori'),
        trailing: Icon(Icons.chevron_right),
      ),
    ),
  ],
)

Keyingi qadam

Endi sizda ilovaning asosiy qurilish g'ishtlari bor: tugmalar, matn maydonlari, kalitlar, kartochkalar, ro'yxat satrlari va fikr-mulohaza widgetlari. Ekranni "tashqi" tarzda qurishni β€” qanday ko'rinishini β€” to'liq bilasiz.

Lekin bir narsa hali yetishmaydi: bu ekranlar jonli emas. Switchni bossangiz o'zgarmaydi, TextFieldga yozsangiz tugma yonmaydi, Sliderni sursangiz qiymat qotib turadi. Buning sababi β€” biz hech qayerda holatni (state) yangilamadik.

Aynan shu β€” keyingi 16-bobning mavzusi: StatelessWidgetdan StatefulWidgetga o'tib, setState orqali holatni o'zgartirishni va shu bilan UI'ni qaytadan qurishni o'rganamiz. Ana shunda bu bobdagi barcha widgetlar haqiqatan tirik bo'ladi.


Mashqlar

Oson

  1. Material 3 tugmalarini urg'u (emphasis) bo'yicha eng balanddan eng pastgacha tartiblang: TextButton, FilledButton, OutlinedButton. Har biri uchun bittadan haqiqiy misol harakat yozing (masalan "Saqlash").
  2. onPressed: null tugmaga nima qiladi? Bu qaysi vaziyatda foydali bo'ladi?
  3. InputDecorationning quyidagi xususiyatlari nima qilishini bir jumlada yozing: labelText, hintText, prefixIcon, border.
  4. ListTilening to'rt uyasini (leading, title, subtitle, trailing) ayting va har biriga odatda nima qo'yilishini yozing.
  5. Parol uchun TextFieldda qaysi parametrni true qilish kerak, va u nima qiladi?

O'rta

  1. To'liq, kompilyatsiya bo'ladigan Scaffold yozing: AppBarda "Mahsulot" sarlavhasi, bodyda markazda bitta Card, kartochka ichida ListTile (leading β€” Icons.shopping_cart, title β€” "Telefon", subtitle β€” "1 200 000 so'm").
  2. Login formasidagi "Kirish" tugmasi nega SizedBox(width: double.infinity, ...) ichiga o'ralgan? Buni olib tashlasak nima o'zgaradi?
  3. Checkbox, Switch, Slider widgetlarining umumiy ikkita parametri qaysi? Nega bu kitobning hozirgi bosqichida ularning onChanged ichiga print yozyapmiz, qiymatni o'zgartirmayapmiz?

Qiyin

  1. Bitta sozlamalar Cardi yozing: ichida ikkita SwitchListTile ("Bildirishnomalar", "Tungi rejim") va bitta ListTile ("Til", trailing β€” o'q-belgi, onTap bilan). onChanged/onTap ichida print ishlating va kod nega hozir jonli emasligini bir izoh bilan tushuntiring.
  2. FilledButton, FilledButton.tonal, OutlinedButton, TextButton β€” bularning hammasi bitta "Buyurtma berish" ekranida bo'lsa, qaysi harakatga qaysi birini berasiz? Misol harakatlarni o'zingiz tanlab, tanlovingizni urg'u ierarxiyasi bilan asoslang.
Yechimlar

1. Eng balanddan eng pastgacha: FilledButton (yuqori urg'u β€” asosiy harakat, masalan "Saqlash") β†’ OutlinedButton (o'rta urg'u β€” ikkilamchi, masalan "Bekor qilish") β†’ TextButton (past urg'u β€” masalan "Batafsil"). To'la rangli fon eng ko'p e'tibor tortadi, faqat hoshiya o'rtacha, faqat matn eng kam.

2. onPressed: null tugmani o'chiradi (disabled) β€” u kulrang bo'lib qoladi va bosilmaydi. Bu, masalan, forma to'liq to'ldirilmaguncha "Yuborish" tugmasini faolsiz qilib turish uchun foydali β€” foydalanuvchi xato vaziyatda tugmani bosa olmaydi.

3. - labelText β€” maydon ustida turuvchi yorliq (fokuslanganda yuqoriga suzib chiqadi). - hintText β€” maydon bo'sh bo'lganda ichida ko'rinadigan kulrang maslahat matni. - prefixIcon β€” maydon boshidagi (chap tomondagi) ikonka. - border β€” maydonni o'rab turuvchi hoshiya (OutlineInputBorder() to'liq ramka beradi).

4. - leading β€” chapdagi ikonka yoki avatar. - title β€” asosiy matn (sarlavha). - subtitle β€” title ostidagi qo'shimcha matn. - trailing β€” o'ngdagi element: o'q-belgi (Icons.chevron_right), Switch yoki Chip.

5. obscureText: true. U yozilgan har bir belgini nuqta bilan yashiradi, shunda parol ekranda ochiq ko'rinmaydi.

6.

import 'package:flutter/material.dart';

class MahsulotEkrani extends StatelessWidget {
  const MahsulotEkrani({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Mahsulot')),
      body: const Center(
        child: Card(
          child: ListTile(
            leading: Icon(Icons.shopping_cart),
            title: Text('Telefon'),
            subtitle: Text('1 200 000 so\'m'),
          ),
        ),
      ),
    );
  }
}

7. SizedBox(width: double.infinity, ...) tugmani ota maydonning to'liq kengligiga cho'zadi β€” login tugmalari odatda shunday keng bo'ladi (bosish oson va vizual jihatdan asosiy harakat ekani bilinadi). Uni olib tashlasak, FilledButton faqat o'z matni sig'adigancha (matn kengligida) qisqaradi va o'rtada kichik tugma bo'lib qoladi.

8. Umumiy ikkita parametr β€” value (hozirgi qiymat) va onChanged (qiymat o'zgarganda chaqiriladigan funksiya). print yozyapmiz, chunki bu widgetlar o'zicha o'zgarmaydi: ekranda haqiqatan belgi qo'yilishi uchun valueni yangilab, widgetni qaytadan qurish kerak β€” buning uchun esa holat (StatefulWidget + setState) lozim, u esa 16-bobda. Hozircha faqat shaklni ko'rsatyapmiz.

9.

Card(
  margin: const EdgeInsets.all(12),
  child: Column(
    children: [
      SwitchListTile(
        value: true,
        onChanged: (yangi) => print('Bildirishnoma: $yangi'),
        secondary: const Icon(Icons.notifications),
        title: const Text('Bildirishnomalar'),
      ),
      SwitchListTile(
        value: false,
        onChanged: (yangi) => print('Tungi rejim: $yangi'),
        secondary: const Icon(Icons.dark_mode),
        title: const Text('Tungi rejim'),
      ),
      ListTile(
        leading: const Icon(Icons.language),
        title: const Text('Til'),
        trailing: const Icon(Icons.chevron_right),
        onTap: () => print('Til tanlash'),
      ),
    ],
  ),
)
Bu kod jonli emas, chunki SwitchListTilening valuesi qat'iy (true/false) yozib qo'yilgan. Foydalanuvchi switch'ni bossa onChanged ishlaydi (print chiqadi), lekin value o'zgarmagani uchun switch ekranda ko'tarilgan holatda qoladi. Uni jonli qilish uchun valueni o'zgaruvchidan olib, setState bilan yangilash kerak β€” 16-bob.

10. Tipik tanlov (urg'u ierarxiyasiga ko'ra): - FilledButton β†’ "Buyurtma berish" β€” ekranning asosiy harakati, eng baland urg'u (faqat bitta bo'lishi kerak). - FilledButton.tonal yoki OutlinedButton β†’ "Savatga qo'shish" β€” muhim, lekin birlamchi emas (o'rta urg'u). - OutlinedButton β†’ "Bekor qilish" β€” ikkilamchi, faqat hoshiya bilan. - TextButton β†’ "Mahsulot tafsilotlari" yoki "Yetkazib berish shartlari" β€” past urg'uli, havola kabi.

Asos: bitta ekranda foydalanuvchini bitta asosiy harakatga (FilledButton) yo'naltirish kerak; qolganlari pastroq urg'u bilan e'tiborni o'g'irlamasligi lozim.


⬅️ Oldingi: 14 β€” Material 3, Cupertino va theming Β· 🏠 README Β· Keyingi: 16 β€” StatefulWidget va setState ➑️