Знакомство с системой навигации Flutter
Во Flutter экраны или страницы s, представленные вам в мобильном приложении, называются маршрутами. За кулисами, пока вы перемещаетесь по приложению от экрана к экрану, «стек маршрутов» управляется StatefulWidget, Navigator. Во всем этом участвует настоящий класс Navigator. В этой статье мы познакомимся с этим классом, а также предварительно рассмотрим систему навигации Flutter. Следующие статьи по этой теме будут опубликованы позже.
Мне нравятся скриншоты. Нажмите «Подписи к спискам».
Как всегда, я предпочитаю использовать в своих статьях скриншоты, а не суть, чтобы показать концепции, а не просто показать код. Откровенно говоря, я считаю, что с ними легче работать. Однако вы можете щелкнуть или коснуться их подписей, чтобы увидеть код в сущности или в Github. Коснитесь или щелкните сами снимки экрана, чтобы увеличить их.
Никаких движущихся изображений, никаких социальных сетей
В этой статье могут быть файлы gif, демонстрирующие аспекты рассматриваемой темы. Однако при чтении этой статьи на таких платформах, как Instagram, Facebook и т. Д., Просмотр таких файлов gif невозможен. Они могут выглядеть как статические изображения или просто пустые поля-заполнители. Помните об этом и, возможно, прочтите эту статью на medium.com.
Давай начнем.
Между прочим, это не совсем «стопка маршрутов». Объект класса Navigator работает напрямую со стеком StatefulWidgets, называемым Overlay. Что ж, это не совсем так. Чаще всего это стек виджетов без сохранения состояния, называемых ModalBarrier, каждый из которых «завернут» (или передан в) виджет Overlay.
Каждый оверлей также принимает дочерний виджет (экран приложения), и этот дочерний виджет затем «визуально плавает» поверх наложения. Опять же, эти виджеты Overlay буквально собираются один за другим в том порядке, в котором они были созданы по мере продвижения по вашему приложению с помощью функции Navigator.push()
- формируя то, что описывается как «стек экранов». И снова виджет ModalBarrier используется каждый оверлей, чтобы предотвратить любое взаимодействие с наложенными слоями, расположенными ниже, или, более конкретно, с дочерним виджетом, окруженным предыдущим виджетом наложения. Понял?
На самом деле это тоже не совсем так. В функции build () StatelessWidget, ModalBarrier, вы обнаружите, что другие виджеты участвуют в запрете взаимодействия с этими виджетами, расположенными под текущим оверлеем (см. Снимок экрана ниже ). Эти дополнительные виджеты скрывают, в частности, «семантическую информацию» предыдущих виджетов, которая традиционно предоставляется всем без исключения инструментам доступности. В любом случае, вы получили сообщение. Это набор маршрутов / оверлеев / модальных барьеров, управляемый виджетом Navigator.
Между прочим, обычно виджет ModalBarrier можно увидеть в действии, когда на экране отображается типичное диалоговое окно Flutter. Тогда вы заметите, что предыдущий экран / страница / маршрут за диалоговым окном стал темнее. Так работает объект ModalBarrier, находящийся в самом верхнем виджете Overlay. ModalBarrier так же очевиден, когда виджет Drawer открыт в вашем приложении - снова фон затемнен и труднодоступен.
Какой маршрут зависит от платформы
`` Тип маршрутов '' (экранов), используемых вашим приложением Flutter, зависит от того, используете ли вы свое приложение, используя `` Материальный дизайн '' или `` Дизайн Купертино '' (или любой другой вариант, если ваше приложение выбирает один, в зависимости от платформы, на которой оно работает. на). Другими словами, то, как маршруты появляются на экране, а затем выходят из него (переход ваших страниц), зависит от того, использует ли ваше приложение виджет MaterialApp (обычно встречается в примерах) или использует виджет CupertinoApp, который имитирует внешний вид 'традиционного приложения для iOS.
Например, когда вы пишете приложение Flutter и создаете свой домашний экран, знайте, что если ваше приложение запускается с виджетом MaterialApp, ваш домашний экран будет «обернут вокруг» объекта, MaterialPageRoute . Вы можете легко увидеть это на скриншоте ниже слева. Используя виджет MaterialApp, анонимная функция, назначенная его именованному параметру, pageRouteBuilder, создает виджет MaterialPageRoute. С правой стороны, если ваше приложение использует виджет CupertinoApp, используемый виджет маршрута будет иметь вид CupertinoPageRoute. Довольно прямолинейно, не так ли?
Также обратите внимание, как вы видите на каждом скриншоте выше, оба «дизайна интерфейса» во Flutter используют виджет WidgetsApp. Он служит основой вашего приложения. Это тот виджет, который по сути является основой дерева виджетов. В документации по собственному классу говорится: «C создает виджет, который обертывает ряд виджетов, которые обычно требуются для приложения».
Простой маршрут
Чтобы продемонстрировать тему этой статьи, я использую пример приложения, использованный в упражнении Поваренная книга Переход на новый экран и обратно. Хорошо, это не совсем так, я использую комбинацию из двух примеров приложений. Второй пример приложения из упражнения Cookbook под названием Возврат данных с экрана объединен с первым, чтобы более полно изучить подход Flutter к навигации между экранами - я имею в виду маршруты. Пример приложения доступен в виде содержания и называется nativgate_two_routes.dart.
Дорога домой
Теперь давайте посмотрим на функцию main () в нашем примере приложения. Ниже приведен снимок экрана с функцией, сопровождаемый гифкой, демонстрирующей работающее приложение. Сразу после перехода мы видим, что виджет MaterialApp () используется, и поэтому мы знаем, что все используемые маршруты (экраны) будут соответствовать спецификациям дизайна материалов по внешнему виду и поведению.
Ниже выделен параметр home, которому назначен StatelessWidget, FirstRoute. Поскольку используется виджет MaterialApp, мы знаем, что тогда виджет MaterialPageRoute будет «обернут вокруг» (т. Е. Принят в качестве параметра) виджета FirstRoute. Давайте пройдемся по коду и покажем это.
Ниже приведен снимок экрана объекта State, _WidgetsAppState. Это снимок экрана его функции build (), и именно там объект класса, Navigator , создается первый экземпляр. Кроме того, что касается нашего конкретного примера приложения, частная функция _onGenerateRoute () передается объекту Navigator. Это еще одна функция, участвующая в создании объекта маршрута для «главного экрана».
Навигация по маршруту
Итак, по мере продолжения мы видим, что объект State, NavigatorState, определяет в своей функции initState (), что должно быть «именем начального маршрута» для приложение Flutter. Он делает это, вызывая функцию onGenerateInitialRoutes (), найденную в его StatefulWidget, Navigator, и передавая, по умолчанию, если не указано явно, обратную косую черту, '/ ' в качестве "имени" начального маршрута (в данном случае это будет "Главный экран"). Кстати, именно тот объект List, _history на скриншоте ниже, будет содержать так называемый «стек маршрутов», пока вы переходите от экрана к экрану: List _history = <_RouteEntry>[];
Затем в большинстве случаев функция onGenerateInitialRoutes () вызывает частную функцию _routeNamed. Именно в функции _routeNamed () (см. Ниже) мы впервые видим ссылку на объект Route. Обратите внимание, что здесь создается экземпляр объекта RouteSettings, который передается еще одной функции, onGenerateRoute. Это объект RouteSetting, который является единственным параметром, передаваемым конструктору класса Route при его создании. Мы приближаемся.
Для этого конкретного приложения вызов функции onGenerateRoute возвращает нас к объекту WidgetsApp State и его функции _onGenerateRoute (). Эта функция отображается ниже, и там вы видите, что именованный параметр home передается в локальную переменную pageContentBuilder. Наконец, анонимная функция, с которой мы столкнулись впервые. назначенный именованному параметру, pageRouteBuilder, принимает эту локальную переменную, создающую объект Route, MaterialPageRoute. Ух! В этом фреймворке Flutter много всего, не так ли? Мы еще даже не посмотрели на класс Route. Это дальше.
Дорога к маршруту
1. NavigatorState.initState ();
2. Navigator.defaultGenerateInitialRoutes ();
3. NavigatorState._routeNamed ();
4. _WidgetsAppState._onGenerateRoute ();
5. pageRouteBuilder: ‹анонимная функция›
MaterialPageRoute ‹T› (настройки: настройки, строитель: построитель);
У вас много маршрутов
Опять же, каждый «экран» в приложении Flutter включает в себя объект класса Route. Конечно, как вы теперь знаете, он включает в себя ряд других классов, но теперь мы рассмотрим класс Route. Это абстрактный класс. Как вы видите ниже, это просто базовый класс длинной иерархии абстрактных классов, которые расширяются вплоть до «типа маршрута», предлагаемого виджетами MaterialApp и CupertinoApp. Каждое из их объявлений классов перечислено ниже, и вы можете видеть, что каждый «промежуточный» абстрактный класс играет определенную роль в поддержании этого «стека экранов».
Мы рассмотрим каждый из этих промежуточных классов и их роли в следующих статьях. Позвольте мне закончить эту статью, еще немного изучив пример приложения и вкратце увидев, что нужно для «возврата» к предыдущему экрану (маршруту). В этом приложении возврат возвращает определенное значение, которое отображается в SnackBar приложения - мы рассмотрим это сейчас.
Путь в никуда
При включении второго примера Вернуть данные с экрана из упражнений Flutter’s Cookbook я заметил, что чего-то не хватает. Видите ли вы разницу между его кодом и приведенным ниже кодом примера приложения?
Да, отсутствует «тип данных» для возвращаемого значения. Я имею в виду, что, будучи таким простым примером, это не сложно, но это хорошая практика, чтобы указать его, особенно если вы на самом деле ждете, когда будет возвращено значение Future. В нашем примере приложения возвращаемое значение имеет тип данных String. Многие статические функции в классе Navigator позволяют использовать аргумент типа данных.
Назад к нулю
Проблема с примером приложения. Как видите, я смешал два примера приложений. Я смешал один, который не дождался возвращенного значения, с тем, который ожидал, и поэтому в его выполнении будет обнаружено небольшое несоответствие. Нажатие кнопки «назад» приведет к отображению в виджете SnackBar слова null. Это потому, что, как вы видите в классе AppBar ниже, «кнопка назад» или «стрелка назад» вообще не вмещает возвращаемое значение.
Нет резервной копии
StatelessWidget, BackButton, при нажатии просто вызывает статическую функцию mightPop без указания возвращаемого результата.
На скриншоте ниже этой функции MaybePop () можно увидеть, что она позволяет использовать общий тип T, но это просто не выполняется при нажатии кнопки возврата. , и поэтому мы получаем нулевое значение для возвращаемого значения.
Заглянув внутрь этой статической функции, показанной выше, вы увидите другую статическую функцию Navigator.of().
It, обычно используемую для получения ссылки на аналог объекта State Navigator, NavigatorState,, передавая необязательное возвращаемое значение универсального типа Т. Кстати, когда он действительно возвращает экземпляр объекта State, это из виджета Navigator, который мы видели ранее в функции build () WidgetsAppState. Помнить?
Ниже приведен снимок экрана с функцией MaybePop () этого объекта State. Большая часть фреймворка Flutter, когда это уместно, использует функцию MaybePop (), а не функцию pop (), которая просто слепо "выталкивает" самую верхнюю запись маршрута. Как вы видите ниже, функция MaybePop () сначала проверяет, что самая верхняя запись маршрута (представленная переменной lastEntry) является правильной записью для 'pop' и не только выполняет операция возвращает значение Future, если доступно, но сама функция возвращает логическое значение True в случае успеха. Наконец, обратите внимание, что переменная lastEntry происходит от объекта List _history, который снова представляет собой «стек маршрутов».
Время от времени проверяйте на нуль
Между прочим, что касается этого конкретного примера приложения, обходной путь, который я бы выбрал, состоял бы в том, чтобы `` проверить на null '' и игнорировать возвращаемое значение null, если пользователь нажимает кнопку `` назад '' или `` кнопку закрытия '' (с параметром fullscreenDialog: true). См. ниже.
Смешайте или сопоставьте
Кстати, можно отключить второй MaterialPageRoute, используемый в этом простом примере приложения, с помощью CupertinoPageRoute. Обратите внимание, что переход между экранами теперь включает в себя перемещение второго экрана с правой стороны, а не снизу.
Я знаю, что эта статья была немного беспорядочной, но я решил представить навигационную систему Flutter таким образом в надежде, что вы оцените знак сложности, который присутствует в, казалось бы, фундаментальной операции - переходе от окна к окну. То есть от экрана к экрану. Я имею ввиду маршрут. Если вы понимаете, о чем я!! Мы продолжим наш путь в следующих статьях.
Ваше здоровье.