← Назад к вопросам
Какие знаешь способы реализации навигации?
2.3 Middle🔥 171 комментариев
#State Management#Архитектура Flutter
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Способы реализации навигации в Flutter
Навигация — критический элемент мобильного приложения. Flutter предоставляет несколько подходов для управления переходами между экранами.
1. Navigator 1.0 (Imperative Navigation) - классический подход
Navigator 1.0 — управление навигацией через вызовы push/pop методов.
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Home')),
body: Column(
children: [
ElevatedButton(
onPressed: () {
// Переход на DetailScreen
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const DetailScreen(id: 123),
),
);
},
child: const Text('Go to Detail'),
),
ElevatedButton(
onPressed: () {
// Переход с заменой экрана (удаляет текущий из стека)
Navigator.of(context).pushReplacementNamed('/detail');
},
child: const Text('Replace'),
),
ElevatedButton(
onPressed: () {
// Переход и удаление всех экранов ниже
Navigator.of(context).pushNamedAndRemoveUntil(
'/home',
(route) => false, // Удалить все
);
},
child: const Text('Reset to Home'),
),
],
),
);
}
}
class DetailScreen extends StatelessWidget {
final int id;
const DetailScreen({required this.id});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Detail'),
leading: BackButton(
onPressed: () {
// Вернуться и передать значение
Navigator.of(context).pop('result_data');
},
),
),
body: Center(child: Text('Detail ID: $id')),
);
}
}
// Получение результата
void getResult() async {
final result = await Navigator.of(context).push<String>(
MaterialPageRoute(builder: (context) => const DetailScreen(id: 123)),
);
print('Got result: $result');
}
// Определение маршрутов в main
void main() {
runApp(
MaterialApp(
home: const HomeScreen(),
routes: {
'/detail': (context) => const DetailScreen(id: 0),
'/profile': (context) => const ProfileScreen(),
},
onGenerateRoute: (settings) {
if (settings.name?.startsWith('/detail/') ?? false) {
final id = int.parse(settings.name!.split('/')[2]);
return MaterialPageRoute(
builder: (context) => DetailScreen(id: id),
);
}
return null;
},
),
);
}
Плюсы:
- Простой и понятный
- Максимальный контроль
- Дешево в памяти
Минусы:
- Сложно масштабировать (спагетти-код)
- Трудно тестировать
- Нет type safety для параметров
- Сложно управлять back stack
2. Named Routes (Именованные маршруты)
Named Routes — упрощение Navigator 1.0 с именами маршрутов.
void main() {
runApp(
MaterialApp(
home: const HomeScreen(),
routes: {
'/': (context) => const HomeScreen(),
'/detail': (context) => const DetailScreen(id: 0),
'/profile': (context) => const ProfileScreen(),
'/settings': (context) => const SettingsScreen(),
},
),
);
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
ElevatedButton(
onPressed: () => Navigator.pushNamed(context, '/detail'),
child: const Text('Detail'),
),
ElevatedButton(
onPressed: () => Navigator.pushNamed(context, '/profile'),
child: const Text('Profile'),
),
],
),
);
}
}
Плюсы:
- Чище чем push(MaterialPageRoute)
- Все маршруты в одном месте
- Легко добавлять новые
Минусы:
- Все равно нет type safety
- Сложно с параметрами
- Нет поддержки deep linking из коробки
3. Navigator 2.0 (Declarative Navigation) - модерн
Navigator 2.0 — декларативное управление навигацией (как React Router).
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp();
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final _appRouterDelegate = AppRouterDelegate();
final _routeInformationParser = AppRouteInformationParser();
@override
Widget build(BuildContext context) {
return MaterialApp.router(
routerDelegate: _appRouterDelegate,
routeInformationParser: _routeInformationParser,
);
}
}
// Модель приложения
class AppRouteInformationParser extends RouteInformationParser<AppRoutePath> {
@override
Future<AppRoutePath> parseRouteInformation(
RouteInformation routeInformation,
) async {
final uri = Uri.parse(routeInformation.location ?? '/');
if (uri.pathSegments.isEmpty) {
return AppRoutePath.home();
} else if (uri.pathSegments[0] == 'detail') {
final id = int.tryParse(uri.pathSegments[1]);
return AppRoutePath.detail(id);
}
return AppRoutePath.home();
}
@override
RouteInformation restoreRouteInformation(AppRoutePath configuration) {
if (configuration.isHome) {
return RouteInformation(location: '/');
}
if (configuration.isDetail) {
return RouteInformation(location: '/detail/${configuration.id}');
}
return RouteInformation(location: '/');
}
}
class AppRoutePath {
final bool isHome;
final bool isDetail;
final int? id;
AppRoutePath.home() : this._(true, false, null);
AppRoutePath.detail(this.id) : this._(false, true, id);
AppRoutePath._(
this.isHome,
this.isDetail,
this.id,
);
}
class AppRouterDelegate extends RouterDelegate<AppRoutePath>
with ChangeNotifier, PopNavigatorRouterDelegateMixin<AppRoutePath> {
late int? _selectedId;
bool _showDetail = false;
@override
GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
AppRouterDelegate() {
_selectedId = null;
}
@override
AppRoutePath get currentConfiguration {
if (!_showDetail) {
return AppRoutePath.home();
}
return AppRoutePath.detail(_selectedId);
}
@override
Widget build(BuildContext context) {
return Navigator(
key: navigatorKey,
pages: [
const MaterialPage<void>(
key: ValueKey('home'),
child: HomeScreen(),
),
if (_showDetail)
MaterialPage<void>(
key: ValueKey('detail-$_selectedId'),
child: DetailScreen(id: _selectedId ?? 0),
),
],
onPopPage: (route, result) {
if (!route.didPop(result)) return false;
_showDetail = false;
notifyListeners();
return true;
},
);
}
void goDetail(int id) {
_selectedId = id;
_showDetail = true;
notifyListeners();
}
void goHome() {
_showDetail = false;
notifyListeners();
}
@override
Future<void> setNewRoutePath(AppRoutePath configuration) async {
if (configuration.isDetail) {
_selectedId = configuration.id;
_showDetail = true;
} else {
_showDetail = false;
}
}
}
Плюсы:
- Deep linking из коробки
- Синхронизация с URL
- Лучше для веб
- Type safe возможна
Минусы:
- Очень сложный setup
- Много boilerplate
- Трудно для простых приложений
4. GetX Navigation (простой и мощный)
GetX — альтернатива Navigator с простым API.
void main() {
runApp(
GetMaterialApp(
home: const HomeScreen(),
getPages: [
GetPage(name: '/', page: () => const HomeScreen()),
GetPage(name: '/detail/:id', page: () => DetailScreen()),
GetPage(name: '/profile', page: () => const ProfileScreen()),
],
),
);
}
class HomeScreen extends StatelessWidget {
const HomeScreen();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Home')),
body: Column(
children: [
ElevatedButton(
onPressed: () => Get.to(() => const DetailScreen()),
child: const Text('Go to Detail'),
),
ElevatedButton(
onPressed: () => Get.toNamed('/detail/123'),
child: const Text('Go to Detail (Named)'),
),
ElevatedButton(
onPressed: () => Get.offAll(() => const ProfileScreen()),
child: const Text('Replace All'),
),
],
),
);
}
}
class DetailScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final id = Get.parameters['id']; // Получить параметр
return Scaffold(
appBar: AppBar(
title: const Text('Detail'),
leading: const BackButton(),
),
body: Center(
child: Column(
children: [
Text('Detail ID: $id'),
ElevatedButton(
onPressed: () => Get.back(result: 'result'),
child: const Text('Back with Result'),
),
],
),
),
);
}
}
// Получить результат
void getResult() async {
final result = await Get.to(() => const DetailScreen());
print('Result: $result');
}
Плюсы:
- Минимальный boilerplate
- Type-safe параметры
- Контекст не нужен:
Get.to()вместоNavigator.of(context).push() - Поддержка named routes
- Состояние управляется GetX
Минусы:
- Зависимость от GetX
- Менее стандартный подход
5. Go Router (современная альтернатива)
Go Router — от Google, типобезопасный routing.
final router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => const HomeScreen(),
routes: [
GoRoute(
path: 'detail/:id',
builder: (context, state) => DetailScreen(
id: int.parse(state.pathParameters['id']!),
),
),
],
),
],
);
void main() {
runApp(
MaterialApp.router(
routerConfig: router,
),
);
}
class HomeScreen extends StatelessWidget {
const HomeScreen();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Home')),
body: ElevatedButton(
onPressed: () => context.go('/detail/123'),
child: const Text('Go to Detail'),
),
);
}
}
Плюсы:
- Type-safe
- Простой API
- От Google
- Современный подход
Сравнение подходов
| Подход | Сложность | Type Safety | Deep Link | Масштабируемость | Рекомендация |
|---|---|---|---|---|---|
| Navigator 1.0 | Низкая | Нет | Нет | Малые приложения | Простые apps |
| Named Routes | Средняя | Нет | Частично | Средние | Быстрые прототипы |
| Navigator 2.0 | Высокая | Да | Да | Отличная | Большие apps |
| GetX | Низкая | Да | Да | Хорошая | Быстрая разработка |
| Go Router | Средняя | Да | Да | Отличная | Современный выбор |
Рекомендации
- Маленькие приложения (< 10 экранов): Navigator 1.0
- Средние приложения (10-50 экранов): Named Routes или GetX
- Большие приложения (50+ экранов): Navigator 2.0 или Go Router
- Веб приложения: Navigator 2.0 или Go Router (нужна синхронизация URL)
- Быстрая разработка: GetX
- Лучший современный выбор: Go Router
Выбор навигации влияет на архитектуру приложения, поэтому выбирай с учетом размера и требований проекта.