Освоение навигации и маршрутизации в Flutter: полное руководство
FlutterPulseЭта статья переведена специально для канала FlutterPulse. В этом канале вы найдёте много интересных вещей, связанных с Flutter. Не забывайте подписываться! 🚀

Введение
Введение
Навигация и маршрутизация являются фундаментальными понятиями в Flutter, которые позволяют пользователям перемещаться между разными экранами (страницами) в приложении. Хотя они связаны между собой, они служат разным целям.
В этом руководстве мы рассмотрим:
- Навигация vs Маршрутизация
- Императивная Навигация
- Декларативная Навигация
- Когда использовать каждый подход

1. Навигация vs Маршрутизация: понимание разницы
Навигация относится к процессу перемещения между экранами (например, с экрана входа на домашний экран). Это связано с действием перехода из одного представления в другое.
Маршрутизация - это механизм, который определяет, как должна происходить навигация, включая сопоставление путей, переходы и правила, регулирующие соединения экранов.
Простой аналог:
- Навигация = "Перемещение с Экрана А на Экран Б"
- Маршрутизация = "Определение того, как Экран А соединяется с Экраном Б"
2. Императивная Навигация: прямой контроль
Императивная навигация - это традиционный подход, при котором вы явно указываете приложению, как перемещаться, используя прямые команды (например, push, pop).
Как это работает
Вы вручную управляете стеком навигации с помощью класса Navigator Flutter,推ив и выталкивая маршруты по мере необходимости.
Преимущества:
- Простой и понятный для базовой навигации
- Точный контроль над стеком навигации
- Нет дополнительных зависимостей
Недостатки:
- Труднее поддерживать в сложных приложениях
- Логика навигации распределена по виджетам
- Труднее обрабатывать глубокие ссылки
Необходимые команды навигации:
Базовая навигация
// Добавить новый экран в стек
Navigator.push(
контекст,
MaterialPageRoute(builder: (контекст) => СледующийЭкран()),
);
// Вернуться на предыдущий экран
Navigator.pop(контекст);
Именованные маршруты
// Перейти к предопределенному именованному маршруту
// Переходит к именованному маршруту
Navigator.pushNamed(контекст, '/подробности');
// Заменить текущий маршрут именованным маршрутом
Navigator.pushReplacementNamed(контекст, '/домой');
// Удалить текущий, затем вновь добавить именованный маршрут
Navigator.popAndPushNamed(контекст, '/профиль');
// Добавить именованный маршрут и очистить стек
Navigator.pushNamedAndRemoveUntil(
контекст,
'/панель_управления',
(маршрут) => ложь
);
Управление стеком (удаление маршрута)
// Очистить весь стек и перейти на HomeScreen
Navigator.pushAndRemoveUntil(
контекст,
MaterialPageRoute(builder: (контекст) => HomeScreen()),
(маршрут) => ложь, // Удаляет ВСЕ маршруты
);
// Сохранить первый маршрут (например, после входа в систему)
Navigator.pushAndRemoveUntil(
контекст,
MaterialPageRoute(builder: (контекст) => Dashboard()),
(маршрут) => маршрут.isFirst,
);
// Заменить текущий экран
Navigator.pushReplacement(
контекст,
MaterialPageRoute(builder: (контекст) => НовыйЭкран()),
);
// Удалить определенный маршрут
Navigator.removeRoute(контекст, ModalRoute.of(контекст)!);
// Удалить маршрут ниже текущего
Navigator.removeRouteBelow(контекст, ModalRoute.of(контекст)!);
// Удалить до условия
Navigator.popUntil(контекст, (маршрут) => маршрут.isFirst);
Проверки навигации
// Проверить, возможен ли возврат
если (Navigator.canPop(контекст)) {
Navigator.pop(контекст);
}
// Умный возврат, который выполняется только при возможности
окончательный didPop = ожидание Navigator.maybePop(контекст);
если (!didPop) {
// Не могу вернуться - возможно выйти из приложения или показать предупреждение
}
// Проверить, использует ли пользователь жест восходящего движения
если (Navigator.of(контекст).userGestureInProgress) {
// Обработать жест возврата
}
3. Декларативная навигация: современный подход
Декларативная навигация управляет состоянием навигации как частью состояния приложения, что делает ее более предсказуемой и легкой в обслуживании в сложных приложениях.
Как это работает
Вы определяете маршруты и их состояния декларативно, а навигация управляется изменениями состояний приложения (как изменения URL-адресов в веб-приложениях).
Преимущества:
- Лучше для сложных сценариев навигации
- Централизованное и предсказуемое состояние навигации
- Отличная глубокая связь поддержка
- Отлично работает с решениями управления состоянием
Недостатки:
- Требуется немного больше настроек
- Может быть слишком для простых приложений
Типы декларативной навигации:
1. Маршрутизация на основе URL (с помощью go_router)
Самый распространенный подход, идеальный для веба и глубокой связи:
MaterialApp.router(
routerConfig: GoRouter(
routes: [
GoRoute(
path: '/',
builder: (_, __) => HomeScreen(),
),
GoRoute(
path: '/профиль/:id',
builder: (_, состояние) => ProfileScreen(id: состояние.pathParameters['id']!),
),
],
),
)
Когда использовать:
- Веб-приложения
- Приложения, требующие глубокие ссылки
- Общие конфигурации маршрутов
2. Маршрутизация, управляемая состоянием
Маршруты меняются в зависимости от состояния приложения (например, изменений аутентификации):
MaterialApp(
home: BlocBuilder<AuthBloc>(
builder: (context, state) =>
state.isLoggedIn? HomeScreen() : LoginScreen(),
),
)
другой пример:
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'auth_cubit.dart';
import 'screens/login_screen.dart';
import 'screens/home_screen.dart';
import 'screens/splash_screen.dart';
GoRouter createRouter(AuthCubit authCubit) {
return GoRouter(
initialLocation: '/splash',
refreshListenable: GoRouterRefreshStream(authCubit.stream),
redirect: (context, state) {
final authState = authCubit.state;
if (authState is Authenticated && state.location == '/login') {
return '/home';
}
if (authState is Unauthenticated && state.location!= '/login') {
return '/login';
}
return null;
},
routes: [
GoRoute(path: '/splash', builder: (_, __) => const SplashScreen()),
GoRoute(path: '/login', builder: (_, __) => const LoginScreen()),
GoRoute(path: '/home', builder: (_, __) => const HomeScreen()),
],
);
}
Когда использовать:
- Потоки аутентификации
- Экраны настройки
- Флаги функций
3. Вложенная навигация
Вложенная навигация означает управление независимыми стеками навигации в разных разделах вашего приложения:
Router(
routerDelegate: BeamerDelegate(
locationBuilder: (_, state) {
if (state.uri.path.contains('/dashboard')) {
return DashboardLocation(state);
}
return HomeLocation(state);
},
),
)
Когда использовать:
- Приложения с нижней навигацией
- Макеты с разделенным видом
- Сложные приложения с несколькими потоками
Популярные пакеты декларативной навигации:
go_router
Лучше всего для: Большинства производственных приложений
Ключевые функции:
- Автоматическая синхронизация URL
- Встроенная поддержка глубоких ссылок
- Простые охранники маршрутов через перенаправление
beamer
Лучше всего для: Приложений со сложной вложенной навигацией
Ключевые функции:
- Группировка маршрутов с BeamLocation
- Независимые стэки навигации
- Встроенная настройка переходов
auto_route
Лучше всего подходит для: Большие приложения
Ключевые особенности:
- Сгенерированные кодом типизированные маршруты
- Сильная безопасность во время компиляции
- Уменьшение количества шаблонного кода для сложных маршрутов
Ключевые декларативные шаблоны:
Охранники маршрутов
GoRouter(
redirect: (_, state) {
final loggedIn = auth.isLoggedIn;
if (!loggedIn &&!state.matchedLocation.startsWith('/login')) {
return '/login';
}
return null;
},
)
Динамические пути
GoRoute(
path: '/user/:id/posts/:postId',
builder: (_, state) => PostDetail(
userId: state.pathParameters['id']!,
postId: state.pathParameters['postId']!,
),
)
Пользовательские переходы
CustomTransitionPage(
child: DetailsScreen(),
transitionsBuilder: (_, animation, __, child) =>
FadeTransition(opacity: animation, child: child),
)
4. Выбор правильного подхода
Используйте императивную навигацию, когда:
- Ваше приложение имеет простые потребности в навигации
- Вы строите быстрый прототип
- Вы предпочитаете прямой контроль над стэком навигации
- Вам не нужна поддержка веба или глубокая связь поддержка
Используйте декларативную навигацию, когда:
- Ваше приложение имеет сложные требования к навигации
- Вам нужна поддержка веба
- Вам нужны возможности глубокой связи
- Ваша навигация зависит от состояния приложения (авторизация, разрешения)
- Вы работаете над командным проектом, который пользуется строгими контрактами маршрутов
Вывод
Flutter предлагает мощные инструменты как для императивной, так и для декларативной навигации. Для простых приложений императивная навигация может быть все, что вам нужно. Но когда ваше приложение растет в сложности, декларативная навигация с пакетами, такими как go_router может сэкономить вам бесчисленные часы головных болей при обслуживании.
Помните:
- Начинайте с Navigator.push/pop для базовых потребностей
- Переходите к декларативному маршрутизированию по мере роста вашего приложения
- Рассмотрите ваши веб-потребности и глубокую связь требования рано
- Выбирайте шаблоны, соответствующие рабочему процессу вашей команды
Какой бы подход вы ни выбрали, понимание как императивной, так и декларативной навигации сделает вас более универсальным разработчиком Flutter. Счастливой навигации!