# Flutter 5: Arxitektura va State Management

> **Holat:** 2026-yil sentabr. Katta Flutter loyihalarida eng ko'p tortishuvlarga sabab bo'ladigan mavzu — bu arxitektura va holat boshqaruvi (State Management). Ushbu darsda Flutter jamoasining rasmiy MVVM qo'llanmasi (ChangeNotifier + ListenableBuilder), Riverpod 3.4.3 va BLoC 9.1.1 taqqoslanishi, REST API bilan ishlash, ma'lumotlar bazasi (Drift vs Isar) hamda avtotestlar tahlil qilinadi.

## Mundarija

1. [Rasmiy arxitektura yo'riqnomasi: MVVM va Command](#architecture)
2. [O'rnatilgan yechim: ChangeNotifier va ListenableBuilder](#listenable)
3. [Ekotizimdagi asosiy kutubxonalar: Riverpod, BLoC va Provider](#state-libraries)
4. [Taqqoslov: Flutter State vs SwiftUI @Observable](#comparison)
5. [Tarmoq integratsiyasi: http va qo'lda JSON parslash](#networking)
6. [Mahalliy xotira: SharedPreferences va Drift](#storage)
7. [Avtotestlar: Widget test va Mocktail](#testing)
8. [Xulosa](#xulosa)
9. [Manbalar](#manbalar)

---

## 1. Rasmiy arxitektura yo'riqnomasi: MVVM va Command {#architecture}

Google Flutter jamoasi 2026-yil may oyida o'zining rasmiy arxitektura qo'llanmasini yangiladi ([Flutter App Architecture Guide](https://docs.flutter.dev/app-architecture/guide)).

Unda tavsiya etilgan qatlamlar:
1. **UI Layer (Taqdimot qatlami):** Widgetlar va foydalanuvchi bilan o'zaro aloqa.
2. **ViewModel (Boshqaruv qatlami):** Biznes holatini ushlab turadi va UI uchun ma'lumotlarni tayyorlaydi.
3. **Data Layer (Ma'lumotlar qatlami):** Repository va ma'lumot manbalari (REST API, Mahalliy DB).

Google'ning rasmiy **Compass** namunaviy ilovasida Dependency Injection (DI) uchun `provider` (^6.1.2), marshrutlash uchun `go_router`, ma'lumot modellari uchun `freezed` va sinovlar uchun `mocktail` ishlatilgan ([Flutter Compass Sample](https://github.com/flutter/samples/tree/main/compass_app)).

---

## 2. O'rnatilgan yechim: ChangeNotifier va ListenableBuilder {#listenable}

Hech qanday tashqi paket o'rnatmasdan toza MVVM arxitekturasini qurish uchun Flutter'da **`ChangeNotifier`** va **`ListenableBuilder`** mavjud. Bu SwiftUI'dagi `@Observable` va avtomatik ko'rinish yangilanishiga juda yaqin:

```dart
import 'package:flutter/material.dart';

class CounterViewModel extends ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners(); // Tinglovchi widgetlarni ogohlantiradi
  }
}

class CounterScreen extends StatelessWidget {
  final CounterViewModel viewModel;
  const CounterScreen({super.key, required this.viewModel});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ListenableBuilder(
          listenable: viewModel,
          builder: (context, child) {
            return ElevatedButton(
              onPressed: viewModel.increment,
              child: Text('Qiymat: ${viewModel.count}'),
            );
          },
        ),
      ),
    );
  }
}
```

---

## 3. Ekotizimdagi asosiy kutubxonalar: Riverpod, BLoC va Provider {#state-libraries}

Katta jamoaviy loyihalarda odatda quyidagi uchta kutubxonadan biri tanlanadi:

### 1. Riverpod 3 (joriy `flutter_riverpod 3.4.3`)
Provider muallifi Remi Rousselet tomonidan noldan qayta yozilgan, kompilyatsiya vaqtida xavfsiz (compile-safe) va `BuildContext` ga bog'lanmagan zamonaviy reaktiv kutubxona:

```dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class CounterNotifier extends Notifier<int> {
  @override
  int build() => 0;

  void increment() => state++;
}

final counterProvider = NotifierProvider<CounterNotifier, int>(CounterNotifier.new);

class RiverpodCounter extends ConsumerWidget {
  const RiverpodCounter({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    return ElevatedButton(
      onPressed: () => ref.read(counterProvider.notifier).increment(),
      child: Text('Riverpod: $count'),
    );
  }
}
```

### 2. BLoC (joriy `flutter_bloc 9.1.1`)
Yirik korporatsiyalar, fintech va bank ilovalarida eng ommabop yondashuv. Barcha harakatlar qat'iy **Hodisalar (Events)** va **Holatlar (States)** oqimiga asoslanadi:

```dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

sealed class CounterEvent {}
class IncrementEvent extends CounterEvent {}

class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0) {
    on<IncrementEvent>((event, emit) => emit(state + 1));
  }
}

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

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<CounterBloc, int>(
      builder: (context, count) {
        return ElevatedButton(
          onPressed: () => context.read<CounterBloc>().add(IncrementEvent()),
          child: Text('Bloc: $count'),
        );
      },
    );
  }
}
```

---

## 4. Taqqoslov: Flutter State vs SwiftUI @Observable {#comparison}

| Xususiyat | SwiftUI (iOS 17+) | Flutter ChangeNotifier | Flutter Riverpod 3 | Flutter BLoC 9 |
| :--- | :--- | :--- | :--- | :--- |
| **Model e'loni** | `@Observable class Model` | `class Model extends ChangeNotifier` | `class Model extends Notifier<T>` | `class Bloc extends Bloc<E, S>` |
| **UI bog'lanishi** | Avtomatik kuzatuv | `ListenableBuilder` | `ref.watch(provider)` | `BlocBuilder<B, S>` |
| **Bog'liqliklar in'ektsiyasi (DI)** | `@Environment` | `InheritedWidget` / `Provider` | Global providerlar | `BlocProvider` |
| **Arxitektura qat'iyligi** | O'rtacha | Erkin (MVVM) | Erkin / Reaktiv | Juda qat'iy (MVI/Unidirectional) |

---

## 5. Tarmoq integratsiyasi: http va qo'lda JSON parslash {#networking}

Flutter'da rasmiy `package:http` paketi orqali REST so'rovlar yuboriladi. Swift'dagi `Decodable` ga o'xshash qulaylik uchun `factory .fromJson` konstruktorlari ishlatiladi:

```dart
import 'dart:convert';
import 'package:http/http.dart' as http;

class UserPost {
  final int id;
  final String title;

  const UserPost({required this.id, required this.title});

  factory UserPost.fromJson(Map<String, dynamic> json) {
    return UserPost(
      id: json['id'] as int,
      title: json['title'] as String,
    );
  }
}

Future<UserPost> fetchPost(http.Client client) async {
  final response = await client.get(
    Uri.parse('https://jsonplaceholder.typicode.com/posts/1'),
  );

  if (response.statusCode == 200) {
    final jsonMap = jsonDecode(response.body) as Map<String, dynamic>;
    return UserPost.fromJson(jsonMap);
  } else {
    throw Exception('Yuklashda xato: ${response.statusCode}');
  }
}
```

### 5.1 To'liq amaliy Mini-Ilova: Swift vs Flutter (Yonma-yon taqqoslash)

Quyida real internetdan ma'lumot yuklab, ro'yxatda aks ettiruvchi, xatoliklarni ushlovchi va **Pull-to-Refresh** qiluvchi to'liq modulning Swift (iOS 17+) va Flutter (Dart 3) ekvivalentlari keltirilgan:

#### Swift (SwiftUI + @Observable):
```swift
import SwiftUI

struct PostItem: Codable, Identifiable {
    let id: Int
    let title: String
}

@Observable
final class PostListViewModel {
    var posts: [PostItem] = []
    var isLoading = false
    var errorMessage: String?

    func fetchPosts() async {
        isLoading = true
        errorMessage = nil
        defer { isLoading = false }
        guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else { return }
        do {
            let (data, _) = try await URLSession.shared.data(from: url)
            posts = try JSONDecoder().decode([PostItem].self, from: data)
        } catch {
            errorMessage = error.localizedDescription
        }
    }
}
```

#### Flutter (ChangeNotifier + ListenableBuilder):
```dart
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

class PostModel {
  final int id;
  final String title;

  const PostModel({required this.id, required this.title});

  factory PostModel.fromJson(Map<String, dynamic> json) {
    return PostModel(
      id: json['id'] as int,
      title: json['title'] as String,
    );
  }
}

class PostListViewModel extends ChangeNotifier {
  List<PostModel> posts = [];
  bool isLoading = false;
  String? errorMessage;

  Future<void> fetchPosts(http.Client client) async {
    isLoading = true;
    errorMessage = null;
    notifyListeners();

    try {
      final response = await client.get(
        Uri.parse('https://jsonplaceholder.typicode.com/posts'),
      );

      if (response.statusCode == 200) {
        final List<dynamic> jsonList = jsonDecode(response.body) as List<dynamic>;
        posts = jsonList
            .map((item) => PostModel.fromJson(item as Map<String, dynamic>))
            .toList();
      } else {
        errorMessage = 'Xatolik: ${response.statusCode}';
      }
    } catch (e) {
      errorMessage = 'Tarmoq xatosi: $e';
    } finally {
      isLoading = false;
      notifyListeners();
    }
  }
}

class PostListWidget extends StatelessWidget {
  final PostListViewModel viewModel;
  final http.Client client;

  const PostListWidget({
    super.key,
    required this.viewModel,
    required this.client,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Postlar')),
      body: ListenableBuilder(
        listenable: viewModel,
        builder: (context, child) {
          if (viewModel.isLoading && viewModel.posts.isEmpty) {
            return const Center(child: CircularProgressIndicator());
          }
          if (viewModel.errorMessage != null && viewModel.posts.isEmpty) {
            return Center(child: Text(viewModel.errorMessage!));
          }
          return RefreshIndicator(
            onRefresh: () => viewModel.fetchPosts(client),
            child: ListView.builder(
              itemCount: viewModel.posts.length,
              itemBuilder: (context, index) {
                final post = viewModel.posts[index];
                return ListTile(
                  leading: CircleAvatar(child: Text('${post.id}')),
                  title: Text(post.title),
                );
              },
            ),
          );
        },
      ),
    );
  }
}
```

---

## 6. Mahalliy xotira: SharedPreferences va Drift {#storage}

Ma'lumotlarni saqlash talabiga qarab:
- **`shared_preferences` (2.5.x):** Swift'dagi `UserDefaults` analogi (tokenlar, til sozlamalari, flaglar uchun).
- **`drift` (2.35.0):** SQLite ustidagi tiplangan qatlam (Swift'dagi GRDB yoki Core Data/SwiftData analogi). SQL so'rovlarini kompilyatsiya vaqtida tekshiradi.
- **Isar holati bo'yicha ogohlantirish:** Ilgari mashhur bo'lgan Isar ma'lumotlar bazasining v4 versiyasi rasman *"NOT READY FOR PRODUCTION USE"* holatida qoldi. Hamjamiyat hozirda `isar_community 3.3.2` vilkasidan foydalanmoqda.

---

## 7. Avtotestlar: Widget test va Mocktail {#testing}

Flutter sinovlar infratuzilmasi juda kuchli bo'lib, simulyatorsiz ham widgetlarni millisekundlarda tekshirish imkonini beradi:

```dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets('Tugma bosilganda matn yangilanishi kerak', (tester) async {
    int counter = 0;
    await tester.pumpWidget(
      MaterialApp(
        home: StatefulBuilder(
          builder: (context, setState) {
            return ElevatedButton(
              onPressed: () => setState(() => counter++),
              child: Text('Soni: $counter'),
            );
          },
        ),
      ),
    );

    expect(find.text('Soni: 0'), findsOneWidget);
    await tester.tap(find.byType(ElevatedButton));
    await tester.pump();
    expect(find.text('Soni: 1'), findsOneWidget);
  });
}
```

---

## 8. Xulosa {#xulosa}

- Flutter uchun Google tavsiya etgan rasmiy arxitektura — MVVM bo'lib, o'rnatilgan `ChangeNotifier` va `ListenableBuilder` orqali qo'shimcha paketlarsiz ishlaydi.
- Riverpod 3 (3.4.3) zamonaviy reaktiv ilovalar uchun eng qulay va tip xavfsiz yechimdir.
- BLoC 9 (9.1.1) qat'iy hodisalar oqimiga ega bo'lib, yirik jamoalar va korporativ loyihalarda keng qo'llaniladi.
- Mahalliy relyatsion bazalar uchun `drift` (2.35.0) eng ishonchli tanlov hisoblanadi; Isar v4 esa ishlab chiqarishga tavsiya etilmaydi.
- Widget testlari orqali UI mantiqini simulyatorsiz o'ta yuqori tezlikda sinovdan o'tkazish mumkin.

---

## Manbalar {#manbalar}

1. [Application Architecture Guide — Flutter Docs](https://docs.flutter.dev/app-architecture/guide)
2. [Compass Sample Architecture — GitHub](https://github.com/flutter/samples/tree/main/compass_app)
3. [Flutter Riverpod Documentation](https://riverpod.dev)
4. [BLoC Library Documentation](https://bloclibrary.dev)
5. [Drift Database for Dart & Flutter](https://drift.simonbinder.eu)
