Флаттер Беспорядок трудно читать! Сейчас уберу!

Знаете, что мне не нравится в Flutter? Очень мало. Однако мне пришлось привыкнуть к чтению длинных вертикальных списков параметров. В большинстве случаев это были не просто параметры, это был список анонимных функций, передаваемых как параметры, что значительно усложняло чтение. И человек! Иногда бывает много беспорядка! Много беспорядка Flutter!

Мне нравятся скриншоты. Щелкните для Gists.

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

Давай начнем.

Конечно, можно адаптироваться. У него есть свои достоинства, но я решил немного его почистить. Ниже представлены фотографии «до и после». Что вы думаете?

На примере

В качестве примера я использую репозиторий contacts_service. Я произвольно выбрал это. Я не придираюсь к этим конкретным разработчикам или чему-то еще. Так получилось, что он сам по себе демонстрирует использование подключаемого модуля contacts_service, но моя версия не столько о подключаемом модуле, сколько о внешнем виде. Больше об организации. Подробнее о шаблоне проектирования MVC.

Итак, тебе это нравится? Что ты видишь? Я вижу чисто. Я легко могу видеть, что «данные» поступают из статических ссылок в классе под названием «Контакты». Он исходит из "ссылки редактирования" класса Contact. В конце концов, это экран редактирования приложения, и поэтому мы видим, что класс, Контакт, находится в режиме редактирования! Ниже представлен более подробный обзор.

Вы можете видеть, например, что свойство AppBar.title ниже получает данные из справочника контакта displayName. Вы знаете, что свойство заголовка AppBar принимает виджет, и поэтому вы правильно догадались, что элемент «Contacts.edit.displayName.text», следовательно, возвращает текстовый виджет. Чистый.

MVC в игре

Из всех моих статей самой популярной до сих пор была Наконец-то Flutter + MVC!. В нем рассказывается о реализации шаблона проектирования MVC во Flutter, и вы быстро увидите, что MVC здесь также используется. Давайте посмотрим на класс "Контакты".

Если вы знакомы с моей реализацией MVC во Flutter, вы поймете, что класс Contacts является контроллером в дизайне MVC, поскольку он расширяет класс ControllerMVC. Вы также можете увидеть, как он реализует статическую фабрику: пункт 1 в теперь известной оригинальной публикации Джошуа Блоха 2001 года Эффективная Java. Вы видите, что он импортирует класс ContactsService - часть модели этого приложения.

Наконец, вы, возможно, догадались, что, возможно, все, что упоминается в этом классе, является статическим (в соответствии с правилом 22 Джошуа Блоха Эффективная Java 2-е изд.), И опять же, вы были правы.

MVC внутри и снаружи

Даже структура каталогов приложения отражает подход MVC, использованный при разработке этого приложения. Следование шаблону проектирования (любому шаблону проектирования) обеспечивает руководство, структуру, как вы организуете свою логику, как вы организуете свой код - даже как вы организуете свои файлы и папки.

Сохранение последовательности и усердия в этой структуре, например, позволяет «перетекать» разработчиков при переходе от одного проекта к другому. Например, они могут сразу увидеть ниже, где и что составляет «Представление», «Контроллер» и «Модель» для этого конкретного проекта.

Я взял исходное репо. и организовал это выбранным образом. Я подозреваю, что разработчик, даже не знакомый с MVC, может понять, где все живет за достаточно короткое время. Хорошие шаблоны проектирования позволяют это.

Следующая статья с подробным описанием этой структуры каталогов находится в моем списке дел.

Возьми? Понятно. Хороший!

Теперь вернитесь наверх и посмотрите на последний получатель в классе Контакты. Он вызывается edit и ссылается на переменную private library, которая, в свою очередь, ссылается на созданный экземпляр класса ContactEdit. Помните этот получатель? Вы видели это раньше. Контакт в режиме редактирования! Есть еще два класса: ContactList и ContactAdd. Видишь, к чему я клоню?

Вы, наверное, догадались, что здесь есть три вещи; три «режима» в этом приложении. Если вы догадались, в этом приложении есть три экрана: один для добавления контактов, один для редактирования контактов и один для списка контактов. Опять же, вы были бы правы.

Один от другого

Начнем с конца. Давайте посмотрим на класс ContactAdd. Однако обратите внимание, что вы обнаружите, что он является наследником класса ContactEdit. Затем мы рассмотрим этот класс, который, как оказалось, расширяет класс ContactList. Затем мы посмотрим на то, что расширяет еще один класс под названием ContactFields. Мы скоро доберемся до них всех, но сначала о классе, который помогает добавить контакт в приложение.

Он складывается.

Глядя на класс выше, вы видите, что действительно этот класс ContactAdd расширяет класс ContactEdit. Как видите, это не очень большой класс, но он делает много интересного. Во-первых, он необязательно принимает объект Контакт в своем первом методе, называемом init (). Затем он присваивает значения двум свойствам, называемым телефоном и электронной почтой. Поскольку они не определены здесь в этом классе, мы можем предположить, что они определены в его родительском классе или в другом унаследованном классе выше по иерархии. Вы можете видеть, что метод init () вызывает метод своего родителя init (), передавая этот объект Contact… если он есть.

Переменной «частной библиотеки» назначен класс с именем PostalAddress. GlobalKey предоставляется получателем под названием formKey, а есть метод onPressed.

Теперь есть некоторые детали, которые я упустил, но я действительно хочу, чтобы вы сейчас посмотрели на то, как этот класс затем используется на экране «Добавить контакт» для этого приложения.

Найдите получатель Contacts.add во всем приведенном ниже коде. Я подожду здесь. (Внизу есть маленькие красные стрелки, которые помогут вам. В конце концов, это не тест.) Посмотрите, как свойства и методы, найденные в классе ContactAdd, теперь будут иметь смысл, когда вы увидите, где они применяются, ниже .

Это складывается!

Для экрана Добавить контакт необходим ключ формы. А откуда у него ключ формы? Еще раз взгляните на Класс ContactAdd, и вы легко увидите, откуда берется этот ключ формы. Чистый.

Когда вызывается метод onPressed виджета FlatButton (когда пользователь нажимает кнопку сохранить на экране), какой метод вы вызываете? Опять же, оглядываясь на класс ContactAdd, я бы сказал, что вы должны вызвать его собственный метод onPressed. У этого подхода есть большая цель. Вы можете видеть, что функция build () фактически диктует API, который будет использоваться Контроллером, не так ли?

Другая статья, подробно описывающая это, также находится в моем списке дел. Этот список становится все длиннее.

Посмотрите, какие данные будут отображаться при добавлении нового контакта. Сможете ли вы догадаться, какие данные нужно ввести? Спорим, ты можешь! (Подсказка: givenName, middleName, familyName и т. Д.) Вы также можете легко увидеть, что данные также должны быть введены в список виджетов TextFormField. Давайте посмотрим на исходный экран ниже. Видите, как данные и интерфейс не так сильно разделены?

Теперь прокрутите вниз и снова посмотрите мою версию.

Что вы думаете?

Редактировать контакт

Что у нас будет дальше? Затем мы приступили к редактированию контакта. Легко видеть, что класс ContactEdit расширяет класс ContactList. Что обычно делают с контактом в списке контактов? Обычно мы добавляем, редактируем и удаляем контакт, не так ли. Мы можем даже "восстановить" Контакт, если передумаем. Смотри ниже. Что ты видишь?

Вы можете видеть, что класс ContactEdit вызывает статические методы из класса ContactService (модальный аспект приложения) для добавления контакта, удаления контакта и восстановления контакта. Вы можете видеть, что объект контакта, переданный этим методам, сначала превращается в объект карта. Вы также видите, что параметр контактного объекта является необязательным, и, если он равен нулю, назначает переменную private library, _contact. Обратите внимание, что эта переменная принадлежит родительскому классу ContactList.

Контакт!

У класса Contact есть получатель, toMap. Как вы видите ниже, у него также есть именованный конструктор Contact.fromMap. Вы можете догадаться, что это значит. Вы не видите их здесь, но этот конструктор назначает записи карты списку установщиков, названных по именам полей контакта. Однако вы видите список «частных переменных библиотеки», назначаемых этими установщиками. Наконец, вы видите получатель, toMap, который преобразует свойства контакта обратно в карту. Вы правильно догадались, что «бэкэнд» имеет дело непосредственно с объектами карты. Чистый.

Перечислите их Контакты

Следующий класс является родительским классом двух предыдущих. У него есть единственный метод, называемый init (). Вы можете увидеть множество частных переменных библиотеки, которым присвоены различные классы поля. Все эти переменные, очевидно, определены из родительского класса ContactFields. Новый объект Contact создается, если в метод init () не передается объект Contact. Обратите внимание, что объект присваивается этой переменной _contact, которую мы видели в предыдущем классе ContactEdit.

Какая у вас область?

Давайте взглянем на один из тех «полевых» классов. Первый из перечисленных выше - это класс DisplayName. Это очень похоже на другие, но, как и другие, оно должно быть конкретным и предоставлять свое конкретное имя поля, displayName. Обратите внимание, что переменная value принадлежит родительскому классу Field.

Обратите внимание: onSave () вызывается, когда вы сохраняете контакт, например, в AddContactPage в методе _formKey.currentState.save () . В то время как circleAvatar, конечно же, находится на главном экране ContactListPage.

Поле контактов

Первый класс в этой иерархии - это последний класс, который мы рассмотрим, ContactFields. Вы видели, как каждый класс в той или иной степени опирается на предыдущий. Итак, что предоставляет этот класс? Он предоставляет переменные field типа Field вместе с соответствующими им геттерами и сеттерами. См. ниже.

Составьте список ваших контактов

Ниже представлена ​​функция сборки для первого или главного экрана этого приложения. Те, кто знаком с моей реализацией MVC во Flutter, знают, что я рассматриваю функцию build () виджета для главного экрана приложения (build () функция любого виджета на самом деле) быть представлением в реализации MVC. Помнить?

Для этого приложения любые данные, отображаемые в Представлении, доступны через Контроллер. Итак, в этом случае Контакты - это контроллер для этого приложения. Везде ниже, где вы видите слово контакты, вы знаете, что его значение (значения) выводятся из Контроллера. Например, вы не можете увидеть его здесь, но частная переменная _contacts получает свои значения из метода, найденного в контроллере: Contacts.getContacts ()

Я бы выложил его оригинальную версию, но вы потеряетесь в коде. Взгляните на его оригинальный Github, если хотите провести сравнение. Обратите внимание: я добавил в свою версию виджет Dissmiss (красная стрелка), чтобы легко удалять контакты. В оригинале нет средств для удаления контактов, а в моей версии вы можете сразу увидеть, какое поле отображается и удаляется одним движением пальца!

Где это Неразличимое Распознавание?

Возможно, я немного забегаю вперед, но эта красная стрелка знакомит вас с очень важным подходом, применяемым здесь: Контроллер не только определяет, что отображается, но и что вы можете с этим делать. В какой-то степени этот подход заставляет Контроллер выполнять то, что будет задачей View, и представляет данные определенным образом (то есть в TextFormField, в тексте или в Dismissible).

Как вы видели в классе ContactFields, определяется множество классов Field. Именно в этом классе Field вы найдете метод onDissmiss ().

Получатель получает это

Вышеупомянутый метод фактически вызывает getter, отключенный. Это getter, который возвращает желаемый виджет (в данном случае Dismissible) переменной newWidget, указанной выше.

Поговорим о флаттерном беспорядке

Как вы видите выше, происходит много всего. Библиотеки предназначены для того, чтобы значительно упростить жизнь разработчикам, но, как следствие, они сами могут запутаться (много чего происходит в фоновом режиме). В этом случае все параметры, связанные с виджетом Dissimissible, являются предоставляется пользователю для использования… или нет. В противном случае библиотека предоставляет другие значения или функции. Это дает возможность выбора, и разработчикам нравятся варианты.

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

Я имею в виду, что все это упражнение было направлено на сокращение практики передачи анонимных функций в качестве параметров, помните? Ну ... давай сделаем это. Бум!

Куда все пошли ??

На рисунке выше показана та же функция build (), впервые отображаемая с методом onDismissible. Он использовался с его именованными параметрами: дочерний и отклоненный, но теперь их больше нет ?! Заменено на элемент Contacts.list.displayName.dissmiss. Куда они делись? Предположить. Если вы догадались, что они теперь находятся в классе DisplayName, к которому обращается получатель, displayName, вы были правы.

Впервые представленный вам класс DisplayName теперь имеет два новых реализованных метода. Каждый переопределяющий методы из родительского класса, Field: onDismissed () и onChild (). Эта конкретная логика теперь встроена в сторону контроллера.

MVC означает управляемость

Это приводит к разделению работы. Представьте, что над этим приложением работала команда разработчиков, и одному разработчику был назначен аспект приложения Контактные данные. Ему даже не дали доступа к функциям build ()! Он выполняет свою работу в Контроллере, так как Контроллер несет ответственность за предоставление данных в Представление (т.е. Представление может« разговаривать с Контроллером»). Таким образом, он должен только предоставить API (общедоступные свойства и функции) команде UI, необходимой для доступа к Контактным данным. Нет проблем. В данном конкретном случае, например, он дает им следующее: Contacts.list.displayName.dissmiss.

Выполнено.

Потребовалось немного больше кода, но вот и все. Как бы то ни было, я предпочитаю первую реализацию этой. Это всего лишь маленькое приложение, и я считаю, что разделение логики не должно быть настолько явным. В любом случае, у вас есть варианты, и мы, разработчики, любим их. Верно?

Полевой класс

Итак, давайте подробнее рассмотрим этот класс, Филд. Как вы уже догадались. Это библиотека, которую я создал, чтобы сделать мою жизнь разработчика намного проще. Это делает мой код (код в моих функциях build ()) намного чище. Когда Контроллер должен предоставить определенное поле данных, он может сделать это с помощью виджета, наиболее подходящего для данной ситуации. Это может быть TextFormField, Text, ListTile, CheckBox и некоторые другие. Список виджетов ограничен, но продолжает расти. Как и любой хороший файл библиотеки, он делает за вас большую часть работы.

Ирония

Смотри на меня. Я начал эту статью с того, что, возможно, немного пожаловался на список параметров, найденных во Flutter, и в итоге создал библиотеку классов, принимающую около 77 параметров. И, вероятно, будет больше! Веселый!

Смысл всего этого

В любом случае, вы понимаете, почему я это сделал? В вашем следующем проекте у вас будет поле данных из базы данных, и вы должны отобразить это поле данных в TextFormField, ListTile, CircleAvatar и, возможно, даже в CheckBox. Вдобавок ко всему, должен быть реализован Dismissible, чтобы поле данных можно было удалить одним движением пальца по другому экрану вниз по строке.

Дело в том, что определите один объект Field, передающий все необходимые параметры для всех этих виджетов, и все готово. Возьми? Теперь у вас есть объект Field, который можно применить к нескольким экранам во всем приложении. Что вы думаете?

Виджеты? Сколько виджетов?

На момент написания этой статьи класс Field включает следующие виджеты. Опять же, вероятно, будет добавлено больше.

TextFormField
Текст
RichText
ListTile
CheckBoxListTile
CircleAvatar
Отклоняемый
CheckBox

Так в чем же реальный смысл всего этого?

Итак, вы можете сделать вывод, что работа над классом Field все еще продолжается. Он находится в файле в каталоге Utils, но вы можете видеть, что он не один. Видите ли, класс Field находится просто в служебном файле Fields.dart в еще более крупном репозитории. Тот, который содержит мой следующий выпуск пакета, mvc_application.

Большая картина

В процессе разработки я медленно, но верно создавал свою собственную небольшую платформу приложения, чтобы облегчить себе жизнь с каждым последующим проектом. Разработчики, конечно, так и поступают - наращивают свой инструментарий.

С MVC_Pattern идет MVC_Application

Я был приятно удивлен выпуском пакета mvc_pattern. Этот пакет применил шаблон проектирования MVC 40-летней давности к вашему приложению Flutter, и он получил одобрение ответ при освобождении. Я увидел потребность, и, поскольку это мой выбранный мной шаблон дизайна для большинства моих приложений, было приятно видеть, что многие до сих пор ценят дедушку шаблонов дизайна.

Время шло, я работал, мой инструментарий рос. Теперь я намерен выпустить мою текущую структуру, mvc_application, в виде пакета. Это не завтра, но скоро. Как бы то ни было, он просто работает поверх пакета mvc_pattern.

Какая у меня финальная игра?

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

Ваше здоровье.

→ Другие рассказы Грега Перри