Блог
Привет! Меня зовут Павел Шалимов, я flutter-разработчик в InstaDev. В этой статье подробно рассмотрим навигацию во Flutter, отличия и специфичные моменты разных компонентов, а также обсудим возможности Router.

Навигация во Flutter предоставляет разработчикам возможность создавать сложные и гибкие пользовательские интерфейсы, а также легко адаптировать приложения под разные платформы и устройства.
Основной идеей навигации во Flutter является стековая организация экранов, где каждый экран представлен своим виджетом. Переходы между экранами осуществляются путем добавления и удаления маршрутов из стека.

Основные компоненты навигации

  • Navigator: центральный компонент для управления навигацией в приложении. Он поддерживает стек маршрутов, где каждый маршрут представляет собой экран или виджет. Navigator позволяет добавлять, удалять и заменять маршруты, а также управлять анимациями перехода между ними.
  • Route: класс, который представляет собой один экран приложения. Реализация Route может быть стандартной (например, MaterialPageRoute) или пользовательской, в зависимости от требований приложения.
  • MaterialPageRoute: реализация Route, специфичная для мобильных приложений, основанная на материальном дизайне. Она обеспечивает стандартные анимации перехода между экранами и интегрируется с другими элементами Material Design.
  • PageRoute: базовый класс для пользовательских маршрутов. Разработчики могут создавать собственные реализации маршрутов для достижения специфического поведения навигации.
При написании приложения с навигацией, можно придерживаться одного из двух вариантов реализации навигации: императивный способ (Navigator Api) или декларативный (Pages Api, он же Navigator 2.0, он же Router). Далее мы будем называть его Router.

Обычно разработчики используют Navigator Api из-за его простоты и удобства, но если необходима бóльшая гибкость и глубокая поддержка веб приложений - то Pages Api будет лучшим выбором.

Navigator API базируется на стеке маршрутов, где каждый маршрут представляет собой экран приложения. Navigator предоставляет методы для добавления, удаления и замены маршрутов в стеке, а также управляет анимациями перехода между экранами. При использовании Navigator API разработчики определяют маршруты напрямую внутри MaterialApp или Navigator-а, и навигация осуществляется путем вызова методов Navigator.push(), Navigator.pop() и других.

Router - новый способ навигации, представленный командой Flutter относительно недавно, он предоставляет разработчикам свободу самим реализовывать навигацию. Router основан на классах RouteInformationParser, RouterDelegate и RouteInformationProvider, которые позволяют приложению работать с маршрутами на более абстрактном уровне и интегрироваться с механизмами маршрутизации URL в веб-приложениях.

Далее я приведу пример простого приложения с реализацией навигации с обоими API.

Пример приложения, использующего Navigator Api
Здесь мы в виджете MaterialApp, в поле routes, определяем роуты приложения, по которым впоследствии сможем переходить, используя конструкции Navigator.pushNamed(context, ‘/something’), Navigator.pop(context), Navigator.pushReplacementNamed(context, ‘/something’) и другие.

Пример приложения, использующего Router.
Здесь нам необходимо создать 3 класса, благодаря которым и будет осуществляться навигация:

  1. RouteInformationParser: класс, который преобразует информацию о маршруте, например, URL, в конфигурацию состояния навигации.
  2. RouterDelegate: класс, который управляет состоянием навигации приложения. Он отслеживает текущий маршрут и уведомляет Router о необходимости перестроения навигации. Когда возникает необходимость изменить роут приложения, информация о маршруте парсится в RouteInformationParser в тип конфигурации навигации, RouterDelegate получает её и строит новый виджет (экран). Здесь мы можем реализовывать настолько сложную и кастомную навигацию, какую только захотим.
  3. RouteInformationProvider: этот класс предоставляет информацию о текущем маршруте приложения. Он отвечает за передачу информации о маршруте и уведомляет слушателей (виджет Router), когда доступна новая информация о маршруте.
Как вы, наверное, заметили Router гораздо более многословен и труден в освоении, как и сложен в поддержке, когда в приложении количество экранов переваливает за 10-15 штук, тогда конфигурация навигации становится чересчур громоздкой и поддерживать декларативность становится затруднительно.

Поэтому разработчики используют либо проверенный и простой, но, к сожалению, слабо конфигурируемый Navigator, либо какую-нибудь библиотеку, основанную на Router.

Библиотеки и решения

Библиотеки обычно объединяют всё лучшее из обоих миров: простоту и доступность Navigator, и гибкость и, пусть и не полную, но очень ощутимую кастомизируемость Router. Здесь я расскажу про наиболее популярные и интересные библиотеки.

go_router одна из наиболее популярных библиотек для навигации, она интересна тем, что для всех переходов по страницам используется декларативная команда context.go('/user/765/post/1'), которая сама заменит текущий стек навигации на стек, ведущий к этому роуту. И для работы диплинков нет необходимости что-то конфигурировать отдельно, кроме базовой конфигурации, необходимой для любого приложения, использующего диплинки.

Начальная конфигурация go_router крайне простая: необходимо создать экземпляр объекта GoRouter, в котором указаны все роуты приложения:
И добавить в виджет App routerConfig:
auto_route - ещё одна популярная библиотека, которая предоставляет довольно много полезных “фишек” для разработчиков. Тут и диплинки из коробки, и кодогенерация для кучи бойлерплейт-кода, и guarded роуты.
Начальная конфигурация ничем не отличается от таковой в go_router:
А ‘защита’ роутов с помощью auto_route становится довольно тривиальной задачей:
beamer - библиотека, ключевой концепт которой заключается в упрощении работы с Router. Она предоставляет опыт, похожий на работу с Router, но вместо того, чтобы ответственность за генерацию всего стека роутов лежала на одном RouterDelegate, Beamer предлагает создать группу классов BeamLocation, которые будут ответственны за свою часть приложения.

Здесь для начальной конфигурации нужно будет вместо routerConfig указать routeInformationParser и routerDelegate, которые предоставляет библиотека:
Ещё лучше воспользоваться ключевым концептом всего пакета и создать BeamLocation для каждой части приложения:
Тогда RouterDelegate будет выглядеть так:
И это ещё далеко не все библиотеки для навигации.

Существует огромное множество совершенно разных плагинов, с различными дополнительными возможностями и способами взаимодействия с ними. Ещё рекомендую обратить внимание на Fluro, Octopus, Qlevar_router, мне они показались интересными, но, к сожалению, в статью уже не помещаются.

Резюме

Навигация в Flutter не самая тривиальная вещь и подступиться к ней можно по-разному.

Можно выбрать простой путь и использовать Navigator, это по-прежнему не самый плохой выбор, он просто выполняет свою работу и не требует большого количества кода, не ставит высокий порог для входа, но и не делает ничего дополнительного; а можно выбрать любую доступную библиотеку, они все предоставляют бóльшую функциональность и гибкость в обмен на изучение и дополнительную зависимость.
Я бы только не рекомендовал использовать Router в его текущей версии, так как библиотеки выполняют его работу лучше.

Хотите кроссплатформенное приложение на Flutter, которое будет решать проблемы бизнеса или увеличивать покупательский трафик?
Просто позвоните нам или напишите.
Также подписывайтесь на Telegram-канал InstaDev mobile. Там мы еще больше пишем о мобильной разработке, дизайне и делимся новостями агентства и рынка.
Оцени эту статью!
Поделиться

Если у вас возникли вопросы или хотите обсудить разработку мобильного приложения

Просто напишите нам или позвоните +7 495 128 0804