Работа с исходным кодом. Изменяем "под себя"
В реальной жизни даже профессиональным разработчикам приходится не столько писать код самостоятельно, сколько адаптировать уже существующее под нужды своей задачи.
Отчасти, именно поэтому сейчас в вакансиях большой акцент делается на владении тем или иным архитектурным подходом, это даже важнее умения непосредственно программировать.
Наше первое домашнее задание моделирует ситуацию, когда Вам удалось найти где-то на просторах интернета программу, которая на какой-то приемлемый для вас процент удовлетворяет представлениям о вашем собственном будущем приложении.
Вы скачали исходный код, проверили, что он запускается без неразрешимых ошибок и настала пора сделать так, чтобы оно стало "вашим".
Сразу оговорка: если вдруг вас мучает совесть, что вы нагло пользуетесь плодами чужих трудов - помните две вещи:
Во-первых, вам не стоит говорить, что все написано именно вами. Упомяните настоящего автора как источника вашей программы. Такие замечания помогут ему сделать карьеру в будущем. Иногда вместо этого достаточно поставить ему звезду на GitHub - она одна стоит дороже тысячи лайков в инстаграме. Главное, если есть возможность - задонатьте ему! Часто такие авторы, выставляющие свой код напоказ пишут способы, которыми можно его поблагодарить. В общем, карму можно не испортить и даже улучшить. Скромно, но публично похвалив его за качественный код (даже если это немного не так) вы увеличите его шансы на дальнейшую счастливую жизнь.
Во-вторых, подумайте о том, что если бы вы заказали разработку этого приложения в студии, то нет никаких гарантий, что они также не воспользуются чужим трудом, но вы переплатите раз в десять больше и при этом нет уверенности, что настоящий гений-разработчик из какой-нибудь голодной провинции в Индии получит свою награду.
Итак, вернемся к нашей программе. Сначала опишем словами то, что видим. А мы видим при запуске коротенький сплэш (стартовый экран) с картинкой внизу и названием верху, потом появляется главный экран с четырьмя рецептами. Наверху тоже написан заголовок, а судя по медленной подгрузке картинок - они берутся из интернета. При нажатии на рецепт мы видим в заголовке название этого рецепта, а под ним три блока информации - пищевая ценность в виде анимированных кружочков, горизонтальный список состава в белых загрукленных элементах и шаги приготовления, которые мы пристально рассматривали ранее.
Понятное дело, что мы можем заменить текстовку, картинки, добавить кнопку "заказать" (например), и у нас получится вполне себе пригодное приложение для какого-нибудь маленького бизнеса.
Важный момент для понимания: все в исходном приложении рецепты у нас из одной категории, если бы они делились, например, на "десерты", "первые блюда", "вторые блюда" то изменить домашний экран будет сложнее, чем найти подходящий новый. Поэтому сделаем вид, что у нас все наши товары и услуги принадлежат одной категории.
Например, пусть это будет автосалон, у которого есть в продаже некие автомобили, но их ассортимент настолько мал, что нет смысла делить их на категории.
1. Навигация по файлам проекта
Начнем мы с того, что попытаемся понять путь, которым следует программа в момент своего запуска.
Ранее уже говорилось, что все начинается с файла main.dart, а первый виджет (экран) который будет открыть сразу после запуска - указан в параметре home
Если подвести к нему курсор мышки - он покажет, где лежит этот файл и мы сможем сами его найти слева в дереве приложении. А можем нажать левой кнопкой мыши на название виджета (SplashView) с зажатой на клавиатуре кнопкой Ctrl (Cmd для мак) и вам автоматически откроют нужный файл и поставят курсор в то место, где встречается упоминание этого виджета:
Это первый экран. Если приглядеться внимательно, то мы не видим, чтобы в нем что-то происходило интересное, он просто показывает информацию. А по факту мы видим, что он через три секунды пропадает и мы видим домашний экран с рецептами. Возвращаемся к файлу и main.dart и смотрим, какой еще код в нем вызывается.
В частности, у нас инициализируется глобальный контроллер программы, который, возможно и берет инициативу на себя
Точно также откроем его с помощью сочетания кнопки Ctrl и левой кнопки мыши, и вот мы внутри:
Я тут щедро написал комментариев, чтобы вы могли разобраться в коде, если вдруг будет интересно, но именно здесь мы видим команду перехода на другой экран:
На самом деле во Flutter попасть на другой экран можно примерно двадцатью разными способами и запоминать их, кроме самых популярных, смысла нет. По факту нам не важно, что происходит внутри если это происходит так, как нам надо, поэтому точно также открываем "главный" экран приложения в коде:
Надеюсь, вы уловили общий смысл "раскручивания" клубка виджетов найденного кода.
2. Меняем ключевые подписи в приложении
Если вы видите какой-то текст в приложении, то знайте: он там мог появиться из трех источников.
а) база данных - если текст меняется в зависимости от каких-то условий, то он, скорее всего, идет из какой-то внешней коллекции данных. Первый признак - этот текст в принципе невозможно найти в текстах исходного кода
б) внутренний источник данных - это может быть либо текстовый файл в формате JSON (обилие фигурных скобочек, похож на упрощенный код Dart), либо отдельный класс в коде (как у меня в исходнике), либо внутренняя база данных. В случае внутренней базы данных вам будет невозможно отредактировать ее содержимое без специальных инструментов, но и вариант такой встречается крайне редко
в) текстовая информация может быть записана в сам код либо в виде констант в отдельном файле, либо записана напрямую в тексте программы (т.н. "хардкод").
Как же найти то, что нам надо?
В Android Studio есть прекрасный встроенный поиск, который позволяет в том числе редактировать текст прямо на лету.
Чтобы им воспользоваться надо слева встать курсором на папку lib и нажать сочетание клавиш на клавиатуре Ctrl-Shift-F.
Либо нажать правой кнопкой на папку lib и в выпадающем меню выбрать пункт "Find in files"
откроется диалог поиска. Давайте напишем туда, например, слово "рецепты". На первый взгляд оно в программе встречается два раза - на экране-заставке и в качестве заголовка на главном экране. На самом деле вхождений чуть больше. Вот как это выглядит:
Первые два варианта местонахождения - это комментарии, третий вариант - это аппаратный, так сказать, заголовок программы (на смартфонах его не видно), а четвертый и пятый - это уже известные нам вхождения.
Мы можем не переходя к файлу прямо здесь поменять надписи, и перезапустить приложение, Когда будете менять, помните, что строка должна быть в кавычках, поэтому не удаляйте их. Вообще, меняя что-то - меняйте только то, что понятно. Если перестарались - Ctr-Z в помощь.
Я умышленно меняю строку на такую, чтобы в ней тоже были кавычки - тут секрет в том, что если строка была заключена в одинарные кавычки ('строка') то если внутри нее мы используем двойные, то они не воспринимаются системой как кавычки.
Итак, перезаписываем строки:
Обратите внимание, что по мере замены текста старые варианты в списке результатов поиска становятся INVALID - то есть их там больше нет, теперь там везде написано 'Автосалон "Егорка"'. Обращаем внимание на кавычки! Если все сделано правильно, то цвет текста в редакторе не поменяется.
Далее перезапускаем программу и смотрим, что получилось:
Как видим, текст поменялся.
Справочно: Перезапуск приложения возможен по двум сценариям: через ту же самую кнопку, которой мы его запускали ("горячий перезапуск") либо через значок молнии ("горячая перезагрузка"). Разница между ними в том, что перезапуск сбросит приложение к исходным данным и выполнит весь код заново, а перезагрузка покажет только мелкие изменения, в основном касающиеся показа виджетов. Если же вы добавляли в приложение новые файлы, например, закидывали их в папку assets, то тогда ни одна из этих "горячих" кнопок не поможет. Придется останавливать приложение полностью (красный квадратик там же) и запускать заново. Это дольше.
В общем, нам все понятно, теперь давайте заменим тексты на экране с детальной информацией. Таким же образом (уже самостоятельно) найдите слова "пищевая ценность", "состав" и "рецепт" чтобы оно выглядело примерно так:
Подсказка видна на скриншоте. Все три строки у нас в одном файле.
Следующий момент "причесывания" чужого кода чуть посложнее, поскольку уже потребует от вас либо уверенного пользования гуглом, либо определенного опыта. Видите, что между словом "особенности" и следующим блоком у нас сильно большой промежуток?
Давайте еще раз посмотрим на код:
Даже не особо понимая, что здесь написано, мы видим, что где цифра 1 - там первый блок, где характеристики и кружочки. Где цифра 2 - там про комплектацию и белые плашки. Цифра три у нас - особенности и перечисление пунктов. Никакой видимой разницы в настройке отображения здесь нет, а большой пробел у нас именно после текста "особенности" и содержимым виджета RecipeSteps. Это значит, что проблема где-то в коде виджета RecipeSteps. "Проваливаемся" в него, нажав на название левой кнопкой мыши, предварительно зажав Ctrl на клавиатуре:
Как видим, у нас здесь какой-то билдер клепает одинаковые контейнеры, у которых задан внутренний отступ в 8 единиц. А что если поменять эту цифру 8.0 на просто 0? Перезапускаем и видим, что текст некрасиво съежился, а отступ как был, так и остался.
По-правде говоря, поздравляю - мы вместе с вами нашли баг, которого быть не должно. Дело в том, что Listview.builder, занимающийся отрисовкой элементов, тоже имеет свойство padding - мы это можем увидеть, если наведем на него мышку:
Но его нет среди перечисленных, а в списке параметров он идет со знаком вопроса (?), то есть если он не задан явно - то его как бы и вообще нет.
А отступ визуально мы наблюдаем.
А отступ визуально мы наблюдаем.
Что же будет, если мы возьмем и добавим туда это свойство? Вот возьмем и скопируем из контейнера, что ниже в этом же месте. Да заодно поменяем на ноль и сделаем горячую перезагрузку:
Сработало!
Фактически мы не только адаптировали чужой код под свои нужды, но и исправили ошибку, да еще и из разряда "багов", то есть трудноуловимых. Главное - капелька аналитического мышления.
Впрочем, идем дальше:
3. Меняем исходные данные приложения
Я не буду повторять про поиск, сами вспомните. Скажу лишь, что все данные я положил в файл, да не просто текстовый, а в виде программного класса. В реальной жизни так никто не делает, у меня задача была показать общие принципы работы. Итак, содержимое файла data.dart
Мы здесь найдем все четыре отображаемых рецепта. Как нам сделать их не-рецептами? Если приглядеться, то увидим, что каждый рецепт состоит из трех строк и трех списков. Список, напомню, это такая структура, которая может внутри себя содержать неограниченное количество элементов нужного типа. А типы у нас там - два списка со строками, а один список типа Nutrients, каждый из которых тоже - две строки и дробное число (должно быть меньше 1).
Чтобы понять все по факту - попробуем просто подставить свои значения вместо имеющихся. Я возьму второй рецепт и сделаю из него автомобиль:
Как видите, на этом этапе все относительно неплохо. Кстати, именно здесь вы сможете увидеть разницу между "горячим перезапуском" и "горячей перезагрузкой": поскольку данные мы загружаем в самом начале работы этого экрана, то просто перерисовать его мало, надо перезапустить программу с самого начала.
Если мы нажмем на вновь добавленный автомобиль, то увидим новую страницу данных о нем:
Как видим, из недочетов у нас - текст в кружочках крупноват и не влазит (надо бы или кружочки сделать больше, или текст уменьшить) ну и просится сделать побольше отступы в заголовках блоков (это я про "характеристики", "комплектация" и "особенности").
Вернемся обратно к коду этого экрана:
Вот у нас текст, а вот у нас и какой-то NutritionWidget, лежащий в этом же проекте в отдельном файле. "Проваливаемся" в него и смотрим, что можем поменять:
Видим, что кружочками здесь и не пахнет, а каждый отдельный кружочек - это отдельный виджет CircleIndicator, которых рисуется ровно столько, сколько элементов Nutrients было получено. Что же, проваливаемся в него. Тут уже что-то похожее на правду - какая-то математика, какие-то таймеры. Пролистываем и находим два виджета с параметрами width и height, что обозначает "ширина" и "высота". Там было значение 70, попробуем поменять на 80 и сделаем горячий перезапуск:
Уже лучше.
Теперь давайте разберемся с отступами в заголовках
Теперь давайте разберемся с отступами в заголовках
Для этого возвращаемся в основной файл этого экрана. Если посмотреть на список параметров виджета Text, то мы не увидим особых намеков на отступы:
В параметре style, который типаTextStyle, знает много про варианты написания, но, опять же, ничего про отступы:
В этом нет ничего удивительного, ведь, как мы говорили раньше, параметрами отображения виджетов часто занимаются так называемые виджеты-обертки. Android Studio знает несколько основных, для этого надо нажать на виджет, который мы собираемся "обернуть" правой кнопкой мыши и выбрать самый верхний пункт меню - Show context actions
Тут есть несколько популярных виджетов, и даже что-то имеющее название Padding:
Давайте выберем его и посмотрим, что у нас появится:
Здесь мы видим, что IDE создала виджет Padding, с уже имеющимся параметром и запихала наш Text в параметр child. В принципе, не меняя ничего, сделайте горячую перезагрузку и посмотрите, что получилось. Отступы добавились.
Но если нам нужны отступы не со всех сторон, а только с определенной стороны?
Для наглядности работы давайте добавим такой же padding над текстом "комплектация":
Как видим, отступы появились. Но как нам сделать, например, чтобы отступ был только сверху, а снизу нет?
Тут на помощь придет еще одна особенность IDE Android Stuido: если мы приглядимся, то увидим, что методы и свойства классов указываются через точку. Например, параметр с отступами выглядит так:
padding: const EdgeInsets.all(8.0),
и в принципе, здесь все понятно. Padding - это название параметра, который должен быть типа EdgeInstets, а слово all после точки, наверное, значит, что значение в скобках имеет отношение ко всем четырем сторонам.
Давайте сделаем следующее: удалим слово all вместе с точкой и поставим снова точку, чтобы IDE выдала нам подсказку. И в подсказке мы увидим несколько интересных вариантов:
с .all мы уже знакомы - там в скобках надо передать число и все, а вот only или symmetric уже намекают на тонкую настройку. Симметричные поля нам не нужны, поэтому попробуем выбрать и нажать в этом списке на "only"
Нам показали, что текущее значение в скобках неприемлемо, а заодно в подсказке дали нужные варианты. Давайте сделаем так, чтобы у нас было написано top: 16.0 и обновим приложение:
Как видим, мы получили двойной отступ над словом "комплектация" и никакого отступа под ним (красные полоски нарисовал я, чтобы сравнить с отступами, которые мы задавали ранее).
На этом пока все, вам остается лишь переделать тренировочное приложение под свой набор данных.