Declarative Routing
ทำไม imperative push/pop ถึงพังเมื่อเจอ deep link
หัวข้อที่มีชื่อว่า “ทำไม imperative push/pop ถึงพังเมื่อเจอ deep link”navigation แบบ imperative ทำงานได้ดีจนกว่าคุณต้องทำ deep link เมื่อ user เปิด myapp.com/orders/42 จาก cold start ไม่มีลำดับการ push ที่เกิดขึ้นมาก่อน — คุณต้องประกอบ stack ทั้งหมด (Home → Orders → Order 42) ขึ้นมาใหม่จาก URL การทำแบบนั้นด้วยมือด้วย Navigator.push เจ็บปวดและผิดพลาดง่าย
ทางแก้คือทำให้ route stack เป็น ฟังก์ชันของ state แบบ declarative: กำหนด URL หรือ app state ปัจจุบันมา แล้วคำนวณ stack ออกมา นี่คือ Navigator 2.0 และเพราะ API ดิบ ๆ ของตัวเอง verbose เกือบทุกคนจึงใช้ go_router ที่เป็น package ทางการที่สร้างทับ Navigator 2.0 อีกที
flowchart LR state["app state / URL /orders/42"] --> router["go_router (route table)"] router --> stack["computed stack Home then Orders then Order 42"]
การตั้งค่า go_router
หัวข้อที่มีชื่อว่า “การตั้งค่า go_router”คุณ declare route ครั้งเดียวในรูปของ data go_router map path ไป builder, รองรับ path parameter ที่มี type และ sync URL ของ browser ให้อัตโนมัติบนเว็บ
final router = GoRouter( routes: [ GoRoute(path: '/', builder: (context, state) => const HomeScreen()), GoRoute( path: '/orders/:id', // typed path parameter builder: (context, state) { final id = state.pathParameters['id']!; return OrderScreen(orderId: id); }, ), ],);
// Navigate by path — no manual stack juggling:context.go('/orders/42');context.go('/orders/42') set location; router คำนวณ stack ใหม่ บนเว็บ URL bar อัปเดตให้ตรง และการ paste URL นั้นจะประกอบหน้าจอเดิมขึ้นมาใหม่ — ได้ deep linking มาฟรี
nested / shell route
หัวข้อที่มีชื่อว่า “nested / shell route”shell route ห่อชุดของ route ไว้ใน UI ร่วมกัน — แอปแบบ bottom-navigation คลาสสิกที่ tab bar อยู่คงที่ ส่วนเนื้อหาข้างในเปลี่ยนไป shell ยังคง mount อยู่ มีแต่ child route ที่สลับ
ShellRoute( builder: (context, state, child) => ScaffoldWithNavBar(child: child), routes: [ GoRoute(path: '/feed', builder: (context, state) => const FeedScreen()), GoRoute(path: '/profile', builder: (context, state) => const ProfileScreen()), ],);redirect เป็น auth guard
หัวข้อที่มีชื่อว่า “redirect เป็น auth guard”redirect รันก่อน route จะ build และส่ง user ไปที่อื่นได้ — เป็นที่ที่เหมาะที่สุดสำหรับ authentication guard return path ใหม่เพื่อ redirect หรือ null เพื่อไปต่อ
GoRouter( redirect: (context, state) { final loggedIn = auth.isLoggedIn; final goingToLogin = state.matchedLocation == '/login'; if (!loggedIn && !goingToLogin) return '/login'; // guard if (loggedIn && goingToLogin) return '/'; // already in return null; // no redirect }, routes: [ /* ... */ ],);