Flutter 5: Arxitektura va State Management
💙 Flutter — Mobile (iOS, Android) & Desktop (macOS, Windows)
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
- Rasmiy arxitektura yo'riqnomasi: MVVM va Command
- O'rnatilgan yechim: ChangeNotifier va ListenableBuilder
- Ekotizimdagi asosiy kutubxonalar: Riverpod, BLoC va Provider
- Taqqoslov: Flutter State vs SwiftUI @Observable
- Tarmoq integratsiyasi: http va qo'lda JSON parslash
- Mahalliy xotira: SharedPreferences va Drift
- Avtotestlar: Widget test va Mocktail
- Xulosa
- 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).
Unda tavsiya etilgan qatlamlar:
- UI Layer (Taqdimot qatlami): Widgetlar va foydalanuvchi bilan o'zaro aloqa.
- ViewModel (Boshqaruv qatlami): Biznes holatini ushlab turadi va UI uchun ma'lumotlarni tayyorlaydi.
- 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).
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:
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:
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:
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:
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):
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):
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'dagiUserDefaultsanalogi (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.2vilkasidan foydalanmoqda.
7. Avtotestlar: Widget test va Mocktail {#testing}
Flutter sinovlar infratuzilmasi juda kuchli bo'lib, simulyatorsiz ham widgetlarni millisekundlarda tekshirish imkonini beradi:
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
ChangeNotifiervaListenableBuilderorqali 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.