← Назад к вопросам

Какие знаешь способы реализации навигации?

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 SafetyDeep 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

Выбор навигации влияет на архитектуру приложения, поэтому выбирай с учетом размера и требований проекта.

Какие знаешь способы реализации навигации? | PrepBro