Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как работает go_router?
go_router — это современный решение для маршрутизации в Flutter, созданное командой Google. Это декларативный роутер, заменивший устаревший Navigator.
Основные концепции
1. Декларативная маршрутизация vs Императивная
// ❌ Старый способ (Navigator 1.0 - императивный)
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => DetailsPage(id: 123)),
);
// ✅ Новый способ (go_router - декларативный)
context.go('/details/123');
go_router основана на деклараторе маршрутов, как в веб-приложениях.
Базовая настройка
import 'package:go_router/go_router.dart';
// Определяем маршруты
final router = GoRouter(
initialLocation: '/',
routes: [
GoRoute(
path: '/',
builder: (context, state) => HomePage(),
),
GoRoute(
path: '/details/:id',
builder: (context, state) {
final id = state.pathParameters['id']!;
return DetailsPage(id: id);
},
),
GoRoute(
path: '/profile',
builder: (context, state) => ProfilePage(),
),
],
);
// Используем в MaterialApp
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp.router(
routerConfig: router, // Передаём конфиг
);
}
}
Навигация
Простая навигация:
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
ElevatedButton(
onPressed: () => context.go('/details/123'),
child: Text('Go to Details'),
),
ElevatedButton(
onPressed: () => context.go('/profile'),
child: Text('Go to Profile'),
),
],
),
);
}
}
Навигация с параметрами:
// В определении маршрута
GoRoute(
path: '/details/:id', // :id — параметр пути
builder: (context, state) {
final id = state.pathParameters['id']!;
return DetailsPage(id: id);
},
)
// Использование
context.go('/details/42');
context.go('/details/user-abc-123');
Query параметры:
// В определении маршрута
GoRoute(
path: '/search',
builder: (context, state) {
final query = state.uri.queryParameters['q'];
final page = state.uri.queryParameters['page'] ?? '1';
return SearchPage(query: query, page: int.parse(page));
},
)
// Использование
context.go('/search?q=flutter&page=2');
Вложенные маршруты (Sub-routes)
GoRoute(
path: '/home',
builder: (context, state) => ShellPage(child: state.navigationContainer),
routes: [ // Вложенные маршруты
GoRoute(
path: 'feed',
builder: (context, state) => FeedPage(),
),
GoRoute(
path: 'messages',
builder: (context, state) => MessagesPage(),
),
GoRoute(
path: 'settings',
builder: (context, state) => SettingsPage(),
),
],
)
// Использование
context.go('/home/feed'); // FeedPage
context.go('/home/messages'); // MessagesPage
context.go('/home/settings'); // SettingsPage
Shell Routes (Bottom Navigation, Drawer)
final router = GoRouter(
initialLocation: '/home/feed',
routes: [
ShellRoute(
builder: (context, state, child) {
return ScaffoldWithBottomNav(
child: child,
onTabChanged: (index) {
// Навигация при клике на табы
switch (index) {
case 0:
context.go('/home/feed');
case 1:
context.go('/home/search');
case 2:
context.go('/home/profile');
}
},
);
},
routes: [
GoRoute(
path: '/home/feed',
builder: (context, state) => FeedPage(),
),
GoRoute(
path: '/home/search',
builder: (context, state) => SearchPage(),
),
GoRoute(
path: '/home/profile',
builder: (context, state) => ProfilePage(),
),
],
),
],
);
class ScaffoldWithBottomNav extends StatefulWidget {
final Widget child;
final Function(int) onTabChanged;
const ScaffoldWithBottomNav({
required this.child,
required this.onTabChanged,
});
@override
State<ScaffoldWithBottomNav> createState() => _ScaffoldWithBottomNavState();
}
class _ScaffoldWithBottomNavState extends State<ScaffoldWithBottomNav> {
int _currentIndex = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: widget.child,
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (index) {
setState(() => _currentIndex = index);
widget.onTabChanged(index);
},
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Feed'),
BottomNavigationBarItem(icon: Icon(Icons.search), label: 'Search'),
BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'),
],
),
);
}
}
Redirect для авторизации
final router = GoRouter(
initialLocation: '/',
redirect: (context, state) async {
// Получаем статус авторизации
final isLoggedIn = await getAuthStatus();
final isLoginPage = state.matchedLocation == '/login';
// Если не авторизован и не на странице логина
if (!isLoggedIn && !isLoginPage) {
return '/login';
}
// Если авторизован и на странице логина
if (isLoggedIn && isLoginPage) {
return '/';
}
return null; // Нет редиректа, продолжить как есть
},
routes: [
GoRoute(
path: '/',
builder: (context, state) => HomePage(),
),
GoRoute(
path: '/login',
builder: (context, state) => LoginPage(),
),
],
);
Error handling
final router = GoRouter(
initialLocation: '/',
routes: [...],
errorBuilder: (context, state) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('404 - Page not found'),
Text('Location: ${state.uri}'),
ElevatedButton(
onPressed: () => context.go('/'),
child: Text('Go Home'),
),
],
),
),
);
},
);
Передача данных между маршрутами
Способ 1: Through path parameters
context.go('/details/123');
GoRoute(
path: '/details/:id',
builder: (context, state) {
final id = state.pathParameters['id']!;
return DetailsPage(id: id);
},
)
Способ 2: Through query parameters
context.go('/search?q=flutter&sort=recent');
GoRoute(
path: '/search',
builder: (context, state) {
final query = state.uri.queryParameters['q'];
final sort = state.uri.queryParameters['sort'];
return SearchPage(query: query, sort: sort);
},
)
Способ 3: Through extra (не в URL)
context.go(
'/details',
extra: User(id: '123', name: 'John'),
);
GoRoute(
path: '/details',
builder: (context, state) {
final user = state.extra as User?;
return DetailsPage(user: user);
},
)
Навигационный стек и pop
class DetailsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Details'),
leading: BackButton(
onPressed: () => context.pop(), // Вернуться назад
),
),
body: Column(
children: [
Text('Details page'),
ElevatedButton(
onPressed: () => context.pop('result_value'), // Pop с результатом
child: Text('Return'),
),
],
),
);
}
}
// В HomePage
ElevatedButton(
onPressed: () async {
final result = await context.push<String>('/details');
print('Result: $result'); // result_value
},
child: Text('Open Details'),
)
Лучшие практики
// ✅ Используйте именованные маршруты для типобезопасности
final router = GoRouter(
routes: [
GoRoute(
path: '/details/:id',
name: 'details', // Дайте имя
builder: (context, state) => DetailsPage(
id: state.pathParameters['id']!,
),
),
],
);
// Навигация по имени
context.goNamed('details', pathParameters: {'id': '123'});
// ✅ Кэшируйте router в верхнем уровне приложения
final router = GoRouter(...);
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp.router(
routerConfig: router,
);
}
}
// ❌ Не создавайте новый router каждый раз
class BadExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
final router = GoRouter(...); // ❌ Пересоздаётся каждый раз!
return MaterialApp.router(routerConfig: router);
}
}
go_router — это modern, type-safe решение для навигации в Flutter приложениях, обеспечивающее deep linking и лучший контроль над навигационным стеком.