20 β go_router bilan deklarativ navigatsiya¶
β¬ οΈ Oldingi: 19 β Navigatsiya: Navigator 1.0 Β· π README Β· Keyingi: 21 β Tarmoq (networking) va API β‘οΈ
Bu bobda: oldingi bobda
Navigator.pushbilan ekrandan ekranga qo'lda o'tishni o'rgandingiz. Bu kichik ilovalar uchun yaxshi, lekin ilova kattalashganda β chuqur havolalar (deep link), veb-manzillar, login himoyasi, tablar bilan murakkablashadi. Endi rasmiy, deklarativ yo'naltirish paketi β go_router bilan tanishamiz. Siz nega uni ishlatishni, yo'l jadvali (route table) qanday tuzilishini,context.govacontext.pushfarqini, yo'l parametrlari (/user/:id), so'rov parametrlari (?q=...) vaextraorqali ma'lumot uzatishni, nomli yo'llarni, ichki (nested) yo'llarni, doimiy pastki menyu uchun StatefulShellRouteni, login uchun redirect himoyasini va deep linkingni o'rganasiz. Oxirida bularning hammasini birlashtirib kichik ilova quramiz.
Nega go_router kerak? (Navigator 1.0 ning chegaralari)¶
19-bobda Navigator bilan ishladik:
Bu imperativ usul: siz "hozir, mana shu ekranni stekka qo'shgin" deb qadam-baqadam buyruq berasiz. Hisoblang β bu 10-bobdagi imperativ UI muammosiga juda o'xshaydi: hamma narsani qo'lda boshqarasiz.
Ilova kichik bo'lsa, bu yetarli. Lekin ilova o'sgan sari muammolar paydo bo'ladi:
- Chuqur havolalar (deep linking). Foydalanuvchi bildirishnomani bossa, to'g'ridan-to'g'ri
/user/42ekraniga tushishi kerak. Imperativ usulda buni qo'lda yig'ish β bosh og'rig'i. - Veb (web) manzillar. Flutter veb-da ishlaganda brauzer manzil qatorida
myapp.com/user/42ko'rinishi kerak, "Orqaga" tugmasi ham ishlashi lozim.Navigator.pushbunga tabiiy mos kelmaydi. - Login himoyasi (auth guard). "Agar foydalanuvchi kirmagan bo'lsa, har qanday himoyalangan ekrandan login sahifasiga yo'naltir" β buni har bir
pusholdida qo'lda tekshirish charchatadi. - Tablar va ichki navigatsiya. Pastki menyuli ilovada har bir tabning o'z tarixi bo'lishi kerak β buni Navigator 1.0 bilan qurish murakkab.
go_router β Flutter jamoasi tomonidan tavsiya etilgan rasmiy yo'naltirish paketi. Uning g'oyasi oddiy va kuchli: siz ilovaning barcha ekranlarini bir jadvalda β qaysi URL yo'li β qaysi ekran β deb e'lon qilasiz. Shundan keyin navigatsiya URL orqali boshqariladi: context.go('/user/42') deysiz, qolganini go_router qiladi.
π‘ Imperativ va deklarativ β yana o'sha farq. Navigator 1.0 da siz "qadam" (push, pop) buyurasiz. go_router da siz "manzil" (
/user/42) aytasiz β go_router o'zi qaysi ekranlarni ko'rsatishni hisoblab chiqaradi. Bu xuddi 10-bobdagiUI = f(holat)g'oyasiga o'xshaydi: bu yerda UI = f(URL).
O'rnatish (setup)¶
Avval paketni qo'shamiz. Terminalda loyiha papkasida:
Bu pubspec.yaml ga eng so'nggi versiyani qo'shadi:
π‘
^17.3.0dagi^(karet) belgisi "17.3.0 va undan yuqori, lekin 18.0.0 dan past" degani β kichik yangilanishlarni avtomatik oladi, lekin katta (buzuvchi) o'zgarishlardan himoya qiladi.
Endi GoRouter obyektini yaratamiz va uni MaterialApp.router ga ulaymiz. Diqqat: oddiy MaterialApp emas, balki MaterialApp.router ishlatiladi:
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
final router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => const HomePage(),
),
GoRoute(
path: '/about',
builder: (context, state) => const AboutPage(),
),
],
);
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: 'go_router demo',
routerConfig: router,
);
}
}
Mana shu β eng kichik to'liq go_router ilovasi. Uchta bo'lakni ajratib oling:
GoRouter(routes: [...])β yo'l jadvali. Har birGoRoutebittapath(URL yo'li) va bittabuilder(shu yo'l uchun quriladigan ekran) ga ega.builder: (context, state) => ...β bu funksiya shu yo'l ochilganda qaysi widgetni qaytarishni aytadi.stateβ joriy yo'l haqidagi ma'lumot (parametrlar va h.k.); pastda ishlatamiz.MaterialApp.router(routerConfig: router)β ilovaga "navigatsiyani endi shu router boshqaradi" deb aytadi.
Yuqoridagi rasmda ko'rganingizdek, GoRouter β ildiz, undan GoRoutelar tarmoqlanadi va har biri bitta ekranga olib boradi. Bu ilovangizning xaritasi.
Asosiy navigatsiya: go va push farqi¶
Endi ekranlar orasida o'tamiz. go_router ikkita asosiy usul beradi va ular muhim farqqa ega:
// 1) go β butun stekni almashtiradi (replace)
ElevatedButton(
onPressed: () => context.go('/about'),
child: const Text('About ga o\'tish'),
)
// 2) push β joriy ekran ustiga yangisini QO'YADI (stack)
ElevatedButton(
onPressed: () => context.push('/about'),
child: const Text('About ni ochish'),
)
context.go(...) va context.push(...) β bular go_router qo'shadigan qulay (extension) metodlar. Ishlatish uchun import 'package:go_router/go_router.dart'; yetarli.
Farqni tushunish uchun navigatsiya stekini (ustun qilib taxlangan ekranlar) eslang:
| Metod | Nima qiladi | Qachon ishlatiladi |
|---|---|---|
context.go('/about') |
Stekni tozalab, /about ni asosiy qiladi. "Orqaga" bosib qaytib bo'lmaydi (chunki ostida hech narsa qolmaydi). |
Tublar orasida, login dan keyin, "bosh sahifaga qayt" kabi holatlarda. |
context.push('/about') |
/about ni joriy ekran ustiga qo'yadi. "Orqaga" bosib qaytish mumkin. |
Detal sahifasini ochish, ro'yxatdan elementga kirish kabi holatlarda. |
Orqaga qaytish uchun:
π‘ Oddiy qoida: chuqurroq kirayotgan bo'lsangiz (ro'yxat β detal) β
push. Bir butun bo'limni boshqasiga almashtirayotgan bo'lsangiz (login β bosh sahifa) βgo. Ikkilanib qolsangiz: ko'pincha ro'yxatdan detalgapush, tublar orasidago.
Yo'l parametrlari: /user/:id¶
Ko'pincha ekran biror ma'lumotga bog'liq bo'ladi: "42-raqamli foydalanuvchi sahifasi". Buni URL yo'lining bir qismi qilib o'tkazamiz β bu yo'l parametri (path parameter). Yo'lda : bilan boshlanadigan qism β o'rin tutuvchi:
GoRoute(
path: '/user/:id',
builder: (context, state) {
final id = state.pathParameters['id']!; // "42"
return UserPage(userId: id);
},
),
:id β bu "bu yerda istalgan qiymat kelishi mumkin" degani. Navigatsiya:
Ekran ichida shu qiymatni state.pathParameters['id'] orqali olamiz. U doim String bo'ladi β agar son kerak bo'lsa, int.parse(id) qiling.
Rasmda ko'rsatilganidek, context.go('/user/42') chaqirilganda go_router uni /user/:id shabloniga moslaydi, id = "42" ni ajratib oladi va builder orqali ekranni quradi. Mana shu β URL ning ekranga aylanishi.
So'rov parametrlari: ?q=...¶
Qidiruv yoki filtr kabi ixtiyoriy qiymatlar uchun so'rov parametrlari (query parameters) qulay. Ular yo'l shablonida e'lon qilinmaydi β to'g'ridan-to'g'ri URL ga ? dan keyin qo'shiladi:
Ekran ichida ularni state.uri.queryParameters dan o'qiymiz:
GoRoute(
path: '/search',
builder: (context, state) {
final query = state.uri.queryParameters['q'] ?? ''; // "flutter"
final sort = state.uri.queryParameters['sort'] ?? ''; // "new"
return SearchPage(query: query, sort: sort);
},
),
π‘ Yo'l parametri vs so'rov parametri.
/user/:idβ zarur, ekranni aniqlaydigan qism (qaysi foydalanuvchi).?q=...β ixtiyoriy, qo'shimcha sozlama (qidiruv matni, saralash). Yo'q bo'lsanullqaytadi, shuning uchun?? ''bilan standart qiymat berish odat.
extra: butun obyektni uzatish¶
Ba'zan parametr matn emas, balki butun bir obyekt (masalan Product nusxasi). Uni URL ga sig'dirib bo'lmaydi β buning uchun extra bor:
Ekran ichida:
GoRoute(
path: '/detail',
builder: (context, state) {
final product = state.extra as Product;
return DetailPage(product: product);
},
),
β οΈ Diqqat:
extraveb-da brauzer yangilansa (reload) yo'qoladi va deep link bilan kelmaydi (chunki u URL da emas, xotirada uzatiladi). Shuning uchunextrani faqat ilova ichidagi tez uzatish uchun ishlating; ekran qayta yuklanganda ham kerak bo'ladigan ma'lumotni yo'l yoki so'rov parametri qiling (/detail/:id), keyin ma'lumotniidbo'yicha qaytadan yuklang.
Nomli yo'llar: URL satrlaridan ajralish¶
Kodingiz bo'ylab '/user/42' kabi matn yozish xavfli β yo'l o'zgarsa (/user/ β /users/), hamma joyni qo'lda tuzatish kerak. Nomli yo'llar bu bog'liqlikni uzadi: yo'lga name beriladi, navigatsiyada esa matn emas, nom ishlatiladi:
GoRoute(
name: 'user',
path: '/user/:id',
builder: (context, state) =>
UserPage(userId: state.pathParameters['id']!),
),
Navigatsiya:
context.goNamed('user', pathParameters: {'id': '42'});
// query bilan:
context.goNamed(
'search',
queryParameters: {'q': 'flutter'},
);
Endi /user/:id yo'lini kelajakda o'zgartirsangiz, faqat bitta joyni (GoRoute dagi path) o'zgartirasiz β goNamed('user', ...) chaqiruvlari o'zgarmaydi.
Ichki (nested) yo'llar¶
Yo'llar daraxt kabi joylasha oladi: bir GoRoute ichida routes: orqali bola yo'llar e'lon qilinadi. Bola yo'lning path i ota yo'lga qo'shiladi:
GoRoute(
path: '/settings',
builder: (context, state) => const SettingsPage(),
routes: [
GoRoute(
path: 'profile', // boshida '/' YO'Q β chunki u nisbiy
builder: (context, state) => const ProfileSettingsPage(),
),
],
),
Bu yerda bola yo'lning to'liq manzili β /settings/profile. E'tibor bering: bola path i 'profile' deb yoziladi (boshida / yo'q), chunki u otaga nisbiy. Navigatsiya odatdagidek: context.go('/settings/profile').
π‘ Ichki yo'llar URL ierarxiyasini tabiiy aks ettiradi:
/settingsβ/settings/profileβ/settings/profile/edit. Bu, ayniqsa, veb va deep link uchun toza tuzilma beradi.
Doimiy pastki menyu: StatefulShellRoute¶
Ko'pgina ilovalarda pastda NavigationBar (tablar: Uy, Ro'yxat, Profil) bo'ladi. U doimo ko'rinib turishi, tab almashganda esa faqat o'rtadagi ekran o'zgarishi kerak. Bundan ham muhimi: har bir tab o'z navigatsiya tarixini saqlashi kerak β Ro'yxat tabida ichkari kirib, Profilga o'tib, qaytib kelganingizda yana o'sha joyda bo'lishingiz lozim.
Buni StatefulShellRoute.indexedStack hal qiladi. "Shell" (qobiq) β doimiy ramka (NavigationBar), uning ichidagi "branch"lar β har bir tabning o'z stagi:
final router = GoRouter(
initialLocation: '/home',
routes: [
StatefulShellRoute.indexedStack(
builder: (context, state, navigationShell) {
// navigationShell β joriy tab tarkibini ko'rsatadigan widget.
// Uni doimiy qobiq (Scaffold + NavigationBar) ichiga joylaymiz.
return ScaffoldWithNavBar(navigationShell: navigationShell);
},
branches: [
// 1-tab: Uy
StatefulShellBranch(
routes: [
GoRoute(path: '/home', builder: (c, s) => const HomePage()),
],
),
// 2-tab: Ro'yxat (ichida detal yo'li ham bor)
StatefulShellBranch(
routes: [
GoRoute(
path: '/list',
builder: (c, s) => const ListPage(),
routes: [
GoRoute(
path: 'detail/:id',
builder: (c, s) =>
DetailPage(id: s.pathParameters['id']!),
),
],
),
],
),
// 3-tab: Profil
StatefulShellBranch(
routes: [
GoRoute(path: '/profile', builder: (c, s) => const ProfilePage()),
],
),
],
),
],
);
Doimiy qobiq widgeti β pastida NavigationBar bo'lgan oddiy Scaffold:
class ScaffoldWithNavBar extends StatelessWidget {
const ScaffoldWithNavBar({super.key, required this.navigationShell});
final StatefulNavigationShell navigationShell;
@override
Widget build(BuildContext context) {
return Scaffold(
body: navigationShell, // joriy tabning ekrani shu yerda ko'rinadi
bottomNavigationBar: NavigationBar(
selectedIndex: navigationShell.currentIndex,
onDestinationSelected: (index) {
// shu indeksli tabga o'tish (o'sha tab tarixini saqlab)
navigationShell.goBranch(
index,
initialLocation: index == navigationShell.currentIndex,
);
},
destinations: const [
NavigationDestination(icon: Icon(Icons.home), label: 'Uy'),
NavigationDestination(icon: Icon(Icons.list), label: 'Ro\'yxat'),
NavigationDestination(icon: Icon(Icons.person), label: 'Profil'),
],
),
);
}
}
Asosiy g'oyalar:
navigationShellβ go_router beradigan widget; u joriy tanlangan tabning tarkibini ko'rsatadi. UniScaffoldningbody:iga qo'yasiz.navigationShell.currentIndexβ qaysi tab tanlanganini bildiradi;NavigationBarni shu bilan moslaymiz.navigationShell.goBranch(index)β tablar orasida o'tadi va har bir tabning o'z stekini saqlaydi (indexedStackshuning uchun).initialLocation:ni shunday berish β allaqachon ochiq tab qayta bosilganda uni boshiga (ildiziga) qaytaradi, bu odatiy xulq.
π‘ Mana shu β 2026-yilda tabli (pastki menyuli) Flutter ilova qurishning zamonaviy, tavsiya etilgan usuli.
StatefulShellBranchhar bir tabning navigatsiya holatini mustaqil tutgani uchun foydalanuvchi tajribasi tabiiy bo'ladi.
Login himoyasi: redirect¶
Himoyalangan ekranlarni qo'riqlash uchun GoRouter ga redirect funksiyasi beriladi. U har bir navigatsiyadan oldin chaqiriladi va: String? qaytaradi β agar null bo'lsa "davom et", agar yo'l qaytarsa "o'sha yo'lga yo'naltir".
final router = GoRouter(
refreshListenable: authState, // auth holati o'zgarsa, redirect qayta baholanadi
redirect: (context, state) {
final loggedIn = authState.isLoggedIn;
final goingToLogin = state.matchedLocation == '/login';
// Kirmagan va login sahifasiga ketmayotgan bo'lsa -> login ga
if (!loggedIn && !goingToLogin) return '/login';
// Allaqachon kirgan va login sahifasiga ketayotgan bo'lsa -> bosh sahifaga
if (loggedIn && goingToLogin) return '/home';
return null; // hech qanday yo'naltirish kerak emas
},
routes: [
GoRoute(path: '/login', builder: (c, s) => const LoginPage()),
GoRoute(path: '/home', builder: (c, s) => const HomePage()),
// ... qolgan himoyalangan yo'llar
],
);
Muhim qismlar:
state.matchedLocationβ foydalanuvchi qaysi yo'lga bormoqchi ekani. Uni tekshirib, login sahifasining o'zini bloklab qo'ymaslikka e'tibor bering (aks holda cheksiz yo'naltirish bo'ladi).nullqaytarish β "yo'naltirish shart emas, davom et" degani.refreshListenableβ bu yergaListenable(masalanChangeNotifierbo'lganauthState) berasiz. Foydalanuvchi kirgan/chiqqandaauthStateo'zgarishi haqida xabar berishi bilan go_routerredirectni qayta baholaydi va ekranni avtomatik yangilaydi. Holat boshqaruvini keyingi boblarda batafsil ko'ramiz; hozircha g'oya: auth o'zgardi β router qayta tekshiradi.
404 sahifasi: errorBuilder¶
Mavjud bo'lmagan yo'lga (/mavjud-emas) o'tilganda nima ko'rsatishni errorBuilder belgilaydi:
final router = GoRouter(
errorBuilder: (context, state) => Scaffold(
body: Center(child: Text('Sahifa topilmadi: ${state.uri}')),
),
routes: [ /* ... */ ],
);
Deep linking β URL to'g'ridan-to'g'ri ekranga¶
go_router ning eng katta yutug'i shundaki, har bir ekran URL ga ega. Bu deep linkingni deyarli tekin qiladi:
- Veb-da: brauzer manzil qatorida
myapp.com/user/42ko'rinadi; uni nusxalab, boshqa odamga yuborsangiz, u to'g'ri ekranni ochadi; brauzerning "Orqaga/Oldinga" tugmalari ishlaydi. - Mobil-da: push-bildirishnoma yoki tashqi havola ilovani
/user/42da ochishi mumkin β chunki bu URL aynan o'sha ekranga moslanadi.
Siz buning uchun qo'shimcha kod yozmaysiz β yo'l jadvalini to'g'ri tuzganingiz uchun bu avtomatik ishlaydi. (Platformaga bog'lash β Android'da intent-filter, iOS'da universal links β alohida sozlash talab qiladi, lekin bu loyiha sozlamasi, go_router mantig'i emas.)
Birgalikda: kichik ilova¶
Endi o'rgangan narsalarni birlashtiramiz: bosh sahifa, ro'yxat, detal (:id), profil β pastki menyuli StatefulShellRoute bilan, va kirmagan foydalanuvchini login ga yo'naltiruvchi redirect bilan. Quyida router qismi (ekran widgetlari sodda, joy tejash uchun qisqartirilgan):
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
// Oddiy auth holati (real ilovada bu to'liqroq bo'ladi)
final authState = AuthState();
class AuthState extends ChangeNotifier {
bool isLoggedIn = false;
void login() { isLoggedIn = true; notifyListeners(); }
void logout() { isLoggedIn = false; notifyListeners(); }
}
final router = GoRouter(
initialLocation: '/home',
refreshListenable: authState,
redirect: (context, state) {
final loggedIn = authState.isLoggedIn;
final goingToLogin = state.matchedLocation == '/login';
if (!loggedIn && !goingToLogin) return '/login';
if (loggedIn && goingToLogin) return '/home';
return null;
},
routes: [
GoRoute(path: '/login', builder: (c, s) => const LoginPage()),
StatefulShellRoute.indexedStack(
builder: (c, s, navigationShell) =>
ScaffoldWithNavBar(navigationShell: navigationShell),
branches: [
StatefulShellBranch(routes: [
GoRoute(path: '/home', builder: (c, s) => const HomePage()),
]),
StatefulShellBranch(routes: [
GoRoute(
path: '/list',
builder: (c, s) => const ListPage(),
routes: [
GoRoute(
name: 'item',
path: 'item/:id',
builder: (c, s) => DetailPage(id: s.pathParameters['id']!),
),
],
),
]),
StatefulShellBranch(routes: [
GoRoute(path: '/profile', builder: (c, s) => const ProfilePage()),
]),
],
),
],
);
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: 'go_router demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
),
routerConfig: router,
);
}
}
Ro'yxatdan detalga o'tish (nomli yo'l bilan, /list tabi ichida):
// ListPage ichida, biror element bosilganda:
onTap: () => context.goNamed('item', pathParameters: {'id': '42'}),
Login sahifasi shunchaki authState.login() ni chaqiradi β refreshListenable tufayli router darhol redirect ni qayta baholaydi va bosh sahifaga o'tkazadi:
// LoginPage ichida:
ElevatedButton(
onPressed: () => authState.login(), // redirect bizni /home ga olib boradi
child: const Text('Kirish'),
)
Bu ilova endi: deep link orqali ochiladi (har ekran URL ga ega), tablar har biri o'z tarixini saqlaydi va himoyalangan ekranlar login bilan qo'riqlanadi β hammasi deklarativ jadval orqali.
Qachon ortiqcha o'ylamaslik kerak?¶
go_router kuchli, lekin u har doim ham shart emas. Agar ilovangizda bor-yo'g'i ikki-uchta ekran bo'lsa va deep link, veb yoki tablar kerak bo'lmasa β 19-bobdagi oddiy Navigator.push/pop butunlay yetarli va soddaroq.
go_router o'zining haqiqiy qiymatini ilova o'sgan sari ko'rsatadi: ko'p ekran, chuqur havolalar, veb-qo'llab-quvvatlash, login oqimlari, tablar paydo bo'lganda. Shunda "qo'lda push qilish" tarqoq bo'lib ketadi, jadval esa tartibni saqlaydi.
π‘ Qoida: kichik boshlang β Navigator 1.0 bilan. Ilova bir necha ekrandan oshib, navigatsiya murakkablashganda go_router ga o'ting. Vaqtidan oldin murakkablik ham xato.
Keyingi qadam¶
Bu bobda ilovangiz ekranlari orasida URL asosida, deklarativ harakatlanishni o'rgandingiz: yo'l jadvali, go/push, yo'l/so'rov/extra parametrlari, nomli va ichki yo'llar, doimiy StatefulShellRoute menyusi, login uchun redirect va deep linking.
Endi ilovangiz ko'p ekranli bo'lib, har biri ma'lumot ko'rsatishi kerak. Lekin bu ma'lumot qayerdan keladi? Keyingi 21-bobda tarmoq (networking) bilan ishlashni β internetdan http orqali ma'lumot olish, JSON ni o'qish va uni ekranga chiqarishni o'rganamiz. O'sha paytda /user/:id yo'liga kelgan id bo'yicha haqiqiy foydalanuvchi ma'lumotini serverdan yuklaymiz.
Mashqlar¶
Oson¶
- O'z so'zlaringiz bilan ayting:
context.go('/about')vacontext.push('/about')orasidagi farq nima? Qaysi biridan keyin "Orqaga" bosib qaytib bo'lmaydi? - Quyidagi yo'l uchun:
GoRoute(path: '/product/:id', ...).context.go('/product/9')chaqirilgandaidning qiymati nima bo'ladi va uni ekran ichida qanday o'qiysiz? MaterialAppo'rniga go_router bilan qaysi konstruktor ishlatiladi vaGoRouterobyekti unga qaysi parametr orqali beriladi?- Yo'l parametri (
/user/:id) bilan so'rov parametri (?q=...) orasidagi farqni bir-ikki jumlada tushuntiring. Qaysi biri "ixtiyoriy" deb hisoblanadi?
O'rta¶
- Quyidagi yo'l jadvalini tuzing (faqat
routes:qismi):/βHomePage,/cartβCartPage, va/cartning ichki yo'licheckoutβCheckoutPage. Ichki yo'lning to'liq URL i qanday bo'ladi? name: 'user',path: '/user/:id'bo'lgan yo'lga nomli navigatsiya yozing:id = 100bo'lgan foydalanuvchi sahifasiga o'ting. Nima uchun nomli yo'l matnlicontext.go('/user/100')dan yaxshiroq bo'lishi mumkin?redirectfunksiyasinullqaytarsa nima bo'ladi? Yo'l (masalan/login) qaytarsa-chi? Login sahifasining o'zida cheksiz yo'naltirishni qanday oldini olasiz?
Qiyin¶
StatefulShellRoute.indexedStacknima uchun "stateful" (holatli) deb ataladi? Uch tabli ilovada (Uy/Ro'yxat/Profil) foydalanuvchi Ro'yxat tabida ichki sahifaga kirib, Profilga o'tib, keyin Ro'yxatga qaytsa nima ko'radi va nega?- Bir o'quvchi
extraorqaliProductobyektini uzatib, veb-da sahifani yangilagach (reload) ilova qulab tushganidan shikoyat qilyapti. Muammo nimada va uni qanday hal qilish kerak? - Kichik (2 ekranli) ilova uchun go_router shartmi? Qachon Navigator 1.0 dan go_router ga o'tish mantiqan to'g'ri bo'ladi? Javobingizni asoslang.
Yechimlar
1. context.go('/about') β butun navigatsiya stekini almashtiradi: /about asosiy (va odatda yagona) ekran bo'lib qoladi, ostida boshqa ekran qolmaydi, shuning uchun "Orqaga" bosib qaytib bo'lmaydi. context.push('/about') esa /about ni joriy ekran ustiga qo'yadi β ostidagi ekran saqlanadi va "Orqaga" (yoki context.pop()) bilan qaytish mumkin. Qaytib bo'lmaydigani β go.
2. id ning qiymati β "9" (matn/String, son emas). Ekran ichida builder ichida shunday o'qiysiz:
int.parse(id) qiling.
3. MaterialApp.router ishlatiladi va GoRouter obyekti unga routerConfig: parametri orqali beriladi:
4. Yo'l parametri (/user/:id) β ekranni aniqlaydigan, zarur qism; u yo'l shablonida : bilan e'lon qilinadi (qaysi foydalanuvchi). So'rov parametri (?q=...) β qo'shimcha, ixtiyoriy sozlama (qidiruv matni, saralash); u shablonda e'lon qilinmaydi va state.uri.queryParameters dan o'qiladi. Ixtiyoriy deb hisoblanadigani β so'rov parametri (yo'q bo'lsa null qaytadi).
5.
routes: [
GoRoute(path: '/', builder: (c, s) => const HomePage()),
GoRoute(
path: '/cart',
builder: (c, s) => const CartPage(),
routes: [
GoRoute(
path: 'checkout', // boshida '/' yo'q β nisbiy
builder: (c, s) => const CheckoutPage(),
),
],
),
],
/cart/checkout.
6.
Nomli yo'l yaxshiroq, chunki u kodni URL satrining shaklidan ajratadi: kelajakdapath ni o'zgartirsangiz (/user/:id β /users/:id), faqat GoRoute dagi bitta joyni tuzatasiz; goNamed('user', ...) chaqiruvlari o'zgarishsiz ishlaydi. Matnli context.go('/user/100') esa har joyda qo'lda tuzatishni talab qiladi va yozuv xatosiga moyil.
7. redirect null qaytarsa β yo'naltirish kerak emas, navigatsiya so'ralgan yo'lda davom etadi. Agar yo'l (masalan '/login') qaytarsa β foydalanuvchi o'sha yo'lga yo'naltiriladi. Cheksiz yo'naltirishni state.matchedLocation ni tekshirib oldini olasiz: agar foydalanuvchi allaqachon /login ga ketayotgan bo'lsa, yana /login qaytarmaslik kerak:
final goingToLogin = state.matchedLocation == '/login';
if (!loggedIn && !goingToLogin) return '/login';
return null;
8. "Stateful" deyiladi, chunki u har bir tab (branch) ning navigatsiya holatini/stekini alohida xotirada saqlaydi (indexedStack β barcha tablarni qurib, faqat tanlanganini ko'rsatadi). Foydalanuvchi Ro'yxat tabida ichki sahifaga kirsa, keyin Profilga o'tib, so'ng Ro'yxatga qaytsa β u yana o'sha ichki sahifada bo'ladi, boshidan emas. Sababi: Ro'yxat branchining steki yo'qotilmagan, tab almashganda u shunchaki ko'rinmas bo'lib turgan, qaytganda o'sha holatda tiklanadi.
9. Muammo: extra ma'lumotni URL da emas, xotirada uzatadi. Veb-da sahifa yangilanganda (reload) ilova qaytadan ishga tushadi va xotiradagi extra yo'qoladi β state.extra as Product null bo'lib, null ni Product ga keltirish qulashga olib keladi. Yechim: qayta yuklashda ham kerak bo'ladigan ma'lumotni extra orqali emas, yo'l parametri orqali uzatish (/detail/:id), keyin ekran ichida id bo'yicha Product ni qaytadan yuklash (masalan serverdan yoki lokal saqlovdan). Shunda URL o'zi yetarli ma'lumotni saqlaydi.
10. Yo'q, kichik (2 ekranli) ilova uchun go_router shart emas β bunday holatda 19-bobdagi Navigator.push/pop soddaroq va yetarli. go_router ga o'tish quyidagilar paydo bo'lganda mantiqan to'g'ri bo'ladi: ko'p ekran, chuqur havolalar (bildirishnoma/havola to'g'ridan-to'g'ri ekran ochishi), veb-qo'llab-quvvatlash (brauzer URL/Orqaga tugmasi), login himoyasi va tabli navigatsiya. Qisqasi: navigatsiya qo'lda boshqarish murakkablashganda. Vaqtidan oldin go_router qo'shish ortiqcha murakkablik β kerakli payt kelganda o'ting.
β¬ οΈ Oldingi: 19 β Navigatsiya: Navigator 1.0 Β· π README Β· Keyingi: 21 β Tarmoq (networking) va API β‘οΈ