Если вы только думаете начать писать свой первый Flutter-проект, скорее всего, вы даже не догадываетесь обо всех граблях, которые скрыты под листвой показательно низкого порога вхождения в технологию. 

Наверняка вопросов больше, чем ответов. Стоит ли задумываться о локализации на старте и какой подход выбрать? А ничего страшного, что я Android совершенно не знаю? Если тестировщик просит несколько конфигураций сборки для него настроить, мне сразу резюме обновлять или не всё так страшно? Ещё и Dart этот… вроде обычный язык, но всё какие-то флешбеки из динамически типизированного прошлого простреливают.

Эта статья будет фонариком в тёмной и неисследованной перещере ужасов под названием «первый проект на Flutter».

Меня зовут Евгений Сатуров, и я руководитель отдела Flutter-разработки в Surf. Мы три года занимаемся Flutter-разработкой и за это время успели набить столько шишек, что даже страшно вспомнить. А ещё больший страх меня обуревает, когда я думаю о том, какое большое количество людей сейчас находятся в той же самой позиции, что и мы три года назад: только готовятся использовать Flutter в своем следующем большом, среднем или маленьком проекте. 

Я постарался собрать все самые ценные советы в статье. Это — текстовая версия моего доклада. Если вам удобнее смотреть, чем читать, посмотрите видео.

Видео доклада «Стелем мягкую соломку на жёсткий Flutter» >>

Первые шаги

Получить базовые знания об Andriod и iOS

Flutter поддерживает шесть платформ, но в первую очередь пока остаётся фреймворком для разработки мобильных приложений под Android и iOS. 

Для начинающих разработчиков это частая проблема: когда они берутся изучать Flutter и начинают с очевидного — с Flutter. Я советую начинать не с Flutter, а с основ Android и iOS. Звучит контринтуитивно, но их всё равно нужно будет изучать. Только в процессе разработки реального продукта это будет более стрессово и болезненно.

Минимум знаний, которые я рекомендую иметь о нативных платформах:

  • Представлять структуру проектов Android и iOS: из чего состоят, какие есть слои.

  • Хорошо знать синтаксис языков Kotlin и Swift. С Objective-C приходится сталкиваться гораздо реже, с Java — и того реже.

  • Понимать принцип работы ключевых фич платформы: например, permission — его нужно запрашивать очень часто. Существуют готовые плагины, которые берут много на себя. Но специфику и различия между платформами нужно понимать всё равно: они влияют на поведение приложения.

  • Понимать аспект работы приложения в фоне. Когда приложение работает в фоне, оно потребляет батарейку и вычислительную мощность устройства. Всё это влияет на негативный user experience пользователя от устройства.

    Вендоры с каждым обновлением операционной системы ограничивают возможности приложений по работе в фоне. Запретить приложениям работать в фоне нельзя, поэтому разработчики ОС и вендоры придумали другие ограничения. Нужно понимать, как это отражается на нас: некоторые задачи, которые может потребовать бизнес, могут и вовсе быть нереализуемы. Это влияет на подходы к разработке проекта. На разных платформах — свои нюансы.

  • Помнить про push-уведомления. Как правило, все пользуются Firebasе для доставки push-уведомлений. Он берёт основную сложность этой задачи на себя. Тем не менее, я встречал ситуации — и не один раз — когда push-уведомления на Android приходят успешно, а на iOS — нет. Самая банальная причина этого — забыли запросить permission на получение push-уведомлений в приложении. Для iOS он необходим, а для Android вообще не нужен: там такого permission просто не существует. 

Изучить все коробочные виджеты 

Не факт, что этот совет для вас актуален. Возможно, вы прекрасно ориентируетесь в семействе виджетов. Но если нет, настоятельно рекомендую целенаправленно посмотреть, как они устроены, поделать с ними сэмплы.

Сомневаюсь, что кто-то всерьёз садился, открывал каталог виджетов и по одному их перебирал: изучал возможности, пробовал в действии, изучал API. Тем не менее, знать виджеты полезно. Иногда мы пытаемся решить задачу с помощью монстра из виджетов. А потом оказывается, что существует виджет из коробки, который решает задачу гораздо проще.

Классификации виджетов:

  • Простые и часто используемые виджеты: например, Expanded, Flex, Wrap. Они простые и чем-то могут напоминать друг друга. Поэтому люди иногда путают их между собой. Приходится уточнять, лезть в документацию. 

  • Простые виджеты узкого применения. Яркий представитель — виджет Divider. Если использовать обычный контейнер высотой в 1–2 пикселя, покрашенный в серый или другой цвет, получается тот же самый Divider. И вроде бы для проекта это не фатальная разница: Divider или контейнер. 

    На самом деле с помощью Divider вёрстка становится более декларативной: он отделяет один блок от другого не только визуально в интерфейсе, но и описание одного блока от другого в коде. Получается, использовать Divider реально важно.

  • Сложные виджеты разметки, например, CustomMultiChildLayout. Мы привыкаем собирать UI из простых виджетов настолько, что не особо горим желанием разбираться в сложных. А это неправильно, так как порой порождает неоптимальный код. 

  • Редко используемые виджеты разметки: IntrinsicHeight, OverflowBox, FittedBox. Если не знаешь про их существование, естественно, в голову не придёт их использовать.  

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

    Подробнее про Sliver-виджеты писал мой коллега Михаил Зотьев >> 

Понимать, как работают constraint

Constraint — определяющая вещь в вопросах верстки пользовательского интерфейса. По мнению Flutter-разработчиков, эта вещь наиболее неинтуитивно реализована во Flutter: много различий относительно принципов, которыми мы привыкли руководствоваться в императивных UI-фреймворках. 

Ситуация: есть контейнер, которому задаются фиксированная высота, ширина и цвет. Из всех свойств применяется только цвет. Высота и ширина — нет, потому что контейнер растягивается на весь экран или на всё доступное место в родительском виджете.

Почему такое может быть? Это не баг — это нормальное поведение. Ему посвящена огромная страница в документации Flutter, которая называется Understanding constraints. В ней есть более 30 примеров различных комбинаций виджетов, каждый из которых раскрывает какой-то отдельный нюанс работы constraint при верстке. Их важно изучить: это сильно поможет верстать макеты.

Старт проекта

Не всё можно отложить на потом: некоторые вещи нужно делать сразу. Либо потом их нельзя будет сделать вовсе, либо это будет стоить несоизмеримых затрат времени, денег, ресурсов, нервов и в целом не очень хорошо скажется на качестве продукта. В этом блоке я постарался собрать советы, которые помогут сделать всё важное на старте сразу.

Настроить релизную сборку

Не откладывайте это до момента, когда релиз на носу: нужно будет фиксить кучу багов, а тут ещё и релизные сборки придётся настраивать.

Android-сборку настроить несложно: может появиться ложное ощущение что всё так же просто будет и для iOS. И вот тут начинается самое веселое: чтобы понять, как это всё сделать в iOS, нужно гораздо больше времени. Provisioning профайлы, сертификаты, куда прописать девайсы и нужно ли их прописывать, как всё это связать с консолью, как в итоге задеплоить артефакты, как их собрать правильно. 

Без Xcode, конечно же, не обойтись. Придётся и в его замечательном интерфейсе разобраться и посмотреть, как всё настраивать. Поэтому первое, что нужно делать при старте проекта, – это заранее всё это настроить.

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

Статья Debugging how you think about code signing >>

Настроить CI/CD

В том, что касается CI/CD, люди делятся на два противоборствующих лагеря. Первый — те, кто работает в больших компаниях. Там наверняка есть DevOps-отделы, которые занимаются настройкой CI/CD. Разработчик просто говорит: «Я хочу CI/CD», — и через пару дней у него есть CI/CD со всеми необходимыми настройками и степами, которые нужны для разработки.

Другой лагерь – это люди, которые работают в маленьком стартапе: два разработчика, никаких DevOps. В общем, что сам сделаешь, то и будет. Естественно, никто отдельного времени на настройку CI/CD не выделит.

Хорошая новость: простенький CI/CD можно развернуть буквально на коленке. Для этого потребуется Github Actions. Удобная и полезная штука, которая сильно снижает порог вхождения в тему.

Можно развернуть её, написать простенький пайплайн, ничего не зная про настройку CI/CD, и реализовать необходимые шаги. Особенно рекомендую сконцентрироваться на сборке: она даст понимание, работает ли ваш код или нет. Если сборка упала, значит, скорее всего, в коде что-то сломано.

Также не помешают шаги с прогоном тестов и форматированием. Но это опционально: например, если в вашем проекте нет тестов, шаг с прогоном становится совершенно бессмысленным. Если вы считаете, что перфекционизм при форматировании – это лишнее, шаг можно исключить. Но я крайне рекомендую его всё-таки оставить.

Если хотите освоить Github Actions, рекомендую начать со статьи «Используем бесплатные возможности Github Actions для CI/CD на Flutter-проекте». Ключевой момент: даже если проект находится в приватном репозитории и Github Actions платный, затраты можно минимизировать. Нужно развернуть собственный self-hosted runner и подключить его через специальную софтину к Github Actions: появится возможность собирать сборки сколько угодно до тех пор, пока машина находится в онлайне.

Предусмотреть локализацию

Даже если приложение моноязычное и никаких предпосылок для других языков нет, рекомендую всё равно заложить механизм локализации. На старте это не займёт много времени. А вот переписать приложение, в котором локализацию не предусмотрели, практически невозможно.

Если закладываете механизм локализации на старте проекта, у вас появится дополнительный ограничитель, который помешает реализовывать работу со строками на том архитектурном уровне, на котором это не должно происходить. Работать со строками нужно строго: на слое UI либо на презентационном слое. Независимо от выбранной архитектуры, слой бизнес-логики — явно не то место, где нужно работать со строковыми ресурсами. 

Для локализации я рекомендую intl. Если intl пугает своей монструозностью, обилием бойлерплейт-кода, который необходимо писать, вам помогут плагины для Android Studio и VS Code. Они будут генерировать весь этот ужас сами, и работать с локализацией станет немного приятнее.

Сконфигурировать несколько типов сборок

Настройка различных конфигураций сборок — мощный инструмент, которым редко пользуются. А между тем, он сильно ускоряет процесс: можно избавить себя от лишних пересборок, выделив изменяемые настройки на другой уровень реализации. Некоторые настройки конфигурации мы даже можем изменять «на лету» — без пересборки приложения. Для решения этой задачи у нас есть богатый арсенал. 

Flavors и схемы в Android и iOS. Это то, что имеет отношение непосредственно к нативной платформе, на которой собираем приложение. Bundle ID, название приложения, иконка — всё это конфигурируется через flavors и схемы, ведь на эти настройки самой сборки больше повлиять никак нельзя. Мы заранее конфигурируем несколько типов сборок с разными настройками.

Точка входа — замечательная особенность, которая есть у Dart-приложений. Её можно подменять при сборке: создать несколько main-файлов, и собирать сборки с использованием разных main-файлов. Это даёт богатые возможности для настроек деталей реализации: в main-файлах можно держать глобальную конфигурация, которая определяет, например, к серверу с каким URL обращается сборка. 

Тестировщики любят, когда у них на выбор есть несколько сборок, которые обращаются к разным серверам: так не придётся собирать каждый раз вручную сборку с нужным сервером. Наличие таких сборок, сконфигурированных при помощи разных main-файлов, сильно выручает. Кроме того, если в проекте есть feature toggle, здесь их тоже можно настраивать.

--dart-define — команда, через которую можно передавать переменные окружения. Мы в Surf её не используем — так исторически сложилось. Я знаю, что есть приверженцы, которые --dart-define любят, но мы решаем всё подменой main-файлов и не испытываем никаких проблем.

Тема и стилизация 

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

Представьте: практически каждый виджет имеет настройки — цвета, закругления углов, тени, текстовые стили и так далее. Если нужно будет сделать глобальный редизайн, то из простой задачи смены одного свойства в одном месте в теме приложения она превращается в задачу на недели: придётся ходить и искать свойства по всему приложению.

Но вместе с тем есть несколько ложек дёгтя. Во-первых, работать с темой нужно аккуратно. Некоторые свойства темы могут отвечать за неожиданные вещи: например, canvasColor. Мы столкнулись в своё время с багом: контекстное меню, которое вызывается при выделении текста в текстовом поле, нечитабельно, потому что черный текст отображается на тёмно-сером фоне. 

Долго бились с этой проблемой. В итоге я залез в исходники контекстного меню и обратил внимание, что бэкграунд пробрасывается туда через много уровней из свойства canvasColor-темы. А тёмный canvasColor был прописан в теме, которая применялась глобально для всего приложения: мы так реализовали тёмную тему. Это решение оказалось неправильным.

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

Но если честно, здесь всё зависит не только от вас. Если дизайнеры не работают в рамках дизайн-системы и не делают UI kit, они могут «сломать» в макетах консистентность компонентов, которую сами же и создали. И все красиво настроенные темы пойдут прахом: придётся хардкодить стилевые настройки прямо на местах. Поэтому советую сделать всё, что от вас зависит, чтобы в проекте такой UI kit появился. 

И если UI Kit всё-таки появился, рекомендую обратить внимание на опенсорсный сервис Widgetbook.io. Он нужен, чтобы получить наглядное и красивое представление всех виджетов экранов, которые есть в приложении. Я думаю, что для большого проекта, в котором переиспользованных компонентов великое множество, это будет очень хорошим подспорьем для онбординга новых членов команды.

ScreenUtil рекомендую использовать, если ориентируетесь на девайсы с разным форм-фактором и не хотите тратить особенно много времени на реализацию адаптивности.

ScreenUtil позволяет получать адаптивность практически «бесплатно». Указываете нефиксированное значение ширины и высоты через специальный extension. Он масштабирует указанные значения и делает их относительными к размеру девайса, на котором приложение запущено. ScreenUtil классно себя показывает как быстрое решение для получения достаточно хорошего результата.

Продуктивность

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

Кроме того, на этом экране можно настраивать прокси. Это нужно, чтобы:

  • Дать возможность подключаться к инструментам для подмены ответов и имитации поведения сервера — например, Charles. Тестировщики будут благодарны за такую возможность. 

  • Использовать экран для демонстрации UI kit либо для демонстрации нотификаций, для включения и отключения feature toggle. Тут вы ограничены только вашей фантазией и фантазией тестировщиков.

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

В нашем случае debug-экран выглядит вот таким образом.

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

Навигация

Во Flutter навигация неплохо реализована из коробки: можно взять самый обычный  императивный навигатор и написать на нём большой проект.

Мы долгое время так и делали. Но за три года работы с Flutter мы познали такую мудрость: если долго писать приложение, когда-нибудь обязательно прилетит задача на реализацию дип-линков. И вот здесь вы вспомните, что есть же ещё и другие способы реализовать навигацию, на которых можно было бы дип-линки сейчас буквально в две строчки поддержать.

Если такая мысль посетила и вас, возможно, вам нужно посмотреть либо в сторону Navigator 2, хотя о нём разные отзывы, либо в сторону таких решений, как go_router, который сейчас активно продвигает Крис Селлс из Google (UPD: к сожалению, недавно Крис покинул компанию)

Есть несколько плагинов из числа популярных, которые я могу посоветовать, — в частности, для VS Code. 

Better comments — выделяет цветом важные комментарии в коде: есть предопределённые стили для стандартных комментариев, но можно настроить собственные.

Color Highlight — в исходном коде находит строки, содержащие закодированные цвета, и выделяет эти строки указанным цветом.

Rainbow Brackets — подсвечивает пары скобок: у каждой пары свой цвет. Незакрытые скобки подсвечивает красным.

Pubspec assist — позволяет, не выходя из редактора, добавлять зависимости в pubspec.yaml в проектах на Dart или Flutter.

Если плагина, который вам нужен, не существует, – напишите его. А если у вас нет идеи сейчас для своих собственных плагинов, то я могу поделиться самой банальной идеей из всех, которая возникает у каждого. Это плагины для генерации шаблонного кода, который вы пишете очень часто.

Если вы выбрали какую-то определённую архитектуру для своего проекта, скорее всего, каждый раз, когда вы создаете новый экран, вы создаете целую иерархию классов для этого экрана. Как правило, это похоже на копипаст. И только потом вы начинаете вносить туда изменения. Генерацию этих скелеты для экранов вполне можно автоматизировать через собственные плагины, чем мы сейчас в нашей команде и занимаемся.

Интересная особенность: с приходом Flutter for Web отладку теперь можно делать не только на реальном девайсе или эмуляторе, когда пишем под Android и под iOS, но и в вебе. Можно собрать приложение, запустить его в браузере, подогнать его под размеры девайса и пользоваться им для отладки.

Здесь, конечно, масса допущений: в проекте не должно быть библиотек, которые не поддерживаются вебом, и вообще не должно быть фич, которые ломают поведение приложения в вебе. В сложных проектах, скорее всего, это применить не получится. Но если у вас есть что-то супер простое, то вполне можете попробовать.

UPD: А ещё лучше вместо эмулятора подходит для отладки ваших приложений Flutter for Desktop. Собирайте ваше приложение так, словно оно предназначается для работы на десктопе и отлаживайте без задержек и проблем. Не забывайте о том, что платформенные интеграции с мобильными платформами таким образом, конечно, не протестировать.

Генерация сетевого слоя. Swagger и OpenAPI — открытые протоколы, которые имеют достаточно большую свободу реализации. Это можно заметить, попробовав в действии валидаторы OpenAPI, которые в большом количестве есть в маркетплейсе, например, VS Code. Можно поставить 10 валидаторов, и все они найдут ошибки, которые другие не найдут.

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

И здесь я хотел бы прорекламировать наше решение SurfGen, которое мы сейчас активно развиваем: оно может стать тем рабочим генератором, который помогает в 100% случаев из 100. Но пока ещё не могу похвастаться его готовностью: он проходит у нас внутреннюю обкатку. Мы активно движемся в этом направлении.

Осторожно, Dart!

Миксины. Dart — замечательный язык, который за последние годы стал ещё лучше и имеет массу уникальных фич и возможностей. С другой стороны, у него, как и в любом другом языке, есть то, к чему лучше относиться с осторожностью: например, миксины. Когда с ними сталкиваешься в первый раз, они вызывают вау-эффект: «Ничего себе! Можно теперь ещё чуть ли не множественное наследование делать».

Хотя, конечно же, нужно понимать, что миксины – это не множественное наследование. Это просто небольшая фича, которая помогает увеличить процент переиспользуемого кода в проекте. С ней нужно обращаться аккуратно: если вы знаете, что будет напечатано в консоли после того, как будет исполнен этот код, я могу вас поздравить. Вы, как минимум, хорошо понимаете принцип действия миксинов.

Но сам факт, что IDE, анализаторы и компиляторы не помешают вам написать этот код, довольно опасен: можно получить странный и трудно отлавливаемый баг с поведением, если вы дублируете название переменных, методов в разных миксинах и применяете их к одному и тому же классу. В своё время я сам столкнулся с такой неприятной особенностью, после чего к миксинам отношусь с осторожностью. 

Восклицательный знак и null safety. С переходом на null safety Dart стал другим языком. Но вместе с тем появился и способ выстрелить себе по ногам, причем достаточно соблазнительный: я говорю про восклицательный знак, который можно использовать для обращения к nullable переменной, игнорируя безопасность, которую даёт null safety. 

Это достаточно неочевидная особенность Dart: если обращаться к глобальной переменной, которая имеет nullable-тип, даже проверка на null в предыдущей строке не даёт гарантии, что эта переменная в дальнейшем будет non null.

Обратиться к хвосту в строке tail.cut просто не получится. Компилятор не даст это сделать: вместо tail и переменной у нас может быть getter, который может неожиданно вернуть null даже при двух последовательных вызовах.

В этом случае возникает всегда такой соблазн. Ты говоришь: «Слушай, я же проверку сделал. Он точно не null. Это же не getter». Ставишь восклицательный знак, обращаешься к переменной и всё хорошо — до тех пор, пока кто-нибудь не придёт и эту переменную на getter не поменяет. Но когда это случится? Да и случится ли?

Такое отношение начинает распространяться на другие кейсы использования восклицательного знака, и ты начинаешь его расставлять повсеместно. И рано или поздно что-нибудь упадёт в runtime, а ещё хуже — на проде. Поэтому восклицательным знаком лучше не пользоваться.

Типизация

Отключайте implicit-casts и implicit-dynamic: это спасёт немало нервов при отладке. А с dynamic вообще лучше дела не иметь: сейчас уже почти не осталось ни одного кейса, когда dynamic действительно имеет смысл применять в работе.

Настраивайте статический анализатор максимально строго. Чем строже настройки, тем меньше случайных ошибок останутся незамеченными в кодовой базе. Нет действительно ни одной причины не сделать этого. Вместо того, чтобы мучиться с настройкой самому, вы можете воспользоваться нашими наработками. Пакет называется surf_lint_rules, доступен на pub.dev. Он очень строго настроен и даст вам настройки, которые мы применяем в работе каждый день.

Не закрывайте глаза на warning — предупреждения: даже если у вас будут самые строгие настройки, но все они будут предупреждениями, необязательными к исправлению, и вы на них внимания обращать на будете, то смысла никакого абсолютно от этих настроек не будет. Выработайте в команде warning-нетерпимость. И по-максимуму старайтесь warning пресекать.

RxDart 

RxDart нужен только для сложных операций с потоками данных и синхронизации потоков данных. RxDart мы некоторое время по привычке затаскивали в проекты, потому что многие из нас – выходцы из Android. В Android RxJava — очень популярная штука. Раньше в Android не было нормального способа взаимодействовать с асинхронными операциями: много избыточного, запутанного бойлерплейт-кода. Хорошую реализацию написать было довольно трудно. RxJava вывел работу с асинхронными процессами на новый уровень.

Как оказалось, смысла в RxDart осталось примерно 25% от исходного. Dart из коробки обладает всеми необходимыми классами для работы с асинхронщиной: например, получением отложенного результата операций с помощью Future, стримами для работы с потоками данных. Если нужна взрослая и настоящая работа в фоне, то — Isolate. 

И на RxDart у нас остается, пожалуй, только небольшой кусочек сложных операций с потоками данных, которые встречаются далеко не в каждом проекте: синхронизацией, сложной фильтрацией, маппингом и другими. 

Качество кода

Несколько рекомендаций по организации проекта. 

Структурируйте файлы проекта по фичам. Это самый удобный способ ориентироваться в проекте, который не является микроскопическим по масштабам.

Нейминг. Имя файла должно всегда соответствовать имени класса. Иначе некоторые классы вы будете искать в вашей кодовой базе очень и очень долго. Особенно если забудете точное написание их названия.

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

Если мы имеем дело с виджетами, я рекомендую этим правилом немного пренебречь, потому что виджет — это и виджет, и state. Это неразрывные вещи. Их можно декларировать в одном файле.

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

Не имеет ни малейшего смысла делать вручную простые задачи. Кодогенерация для моделей данных, например, через json-serializable или через какие-то другие библиотеки – это то, что должно быть в вашем проекте по умолчанию. 

Качество продукта

Оба совета касаются производительности, потому она влияет на user experience больше всего.

Первый совет: прогревать анимации. Больше года Flutter-сообщество страдало от проблемы с junk кадров при анимациях на iOS — и не только на iOS. Хорошо, что прошли те времена, когда не было никакого способа эту проблему пофиксить. Существовали разные костыльные варианты, но это не тянуло на фундаментальное решение.

Теперь такая возможность у нас есть, поэтому забывать о прогреве анимаций совершенно не стоит. Можно сделать её даже вручную, если приложение не покрыто интеграционными тестами. Хотя и интеграционные тесты для этой цели написать тоже не так страшно, как казалось бы. Поэтому не забывайте про прогрев анимаций: эффект действительно будет.

Второй совет: если анимации лагают, обратитесь к rebuild stats. Совет работает не всегда, но в моей практике он работал поразительно часто. Если у вас есть проблема с performance на каком-то экране, вы видите, что анимации лагают и не очень плавно работают, первый инструмент, к которому вам нужно обратиться, – это Rebuild stats. Посмотрите, какие виджеты перерисовываются сотни и тысячи раз — это явно больше, чем надо. 

Если визуально всё нормально, рекомендую поскроллить экран и понажимать кнопки. Иногда перерисовки начинаются после какого-то действия: например, в коде есть неправильное зацикливание процессов, которые всякий раз триггерят перерисовку.

Если все виджеты работают адекватно и не перерисовываются чаще, чем надо, стоит идти в devtools и смотреть на всякие CPU-профайлеры.


Мы часто видим проекты, в которых не соблюдались советы, с которыми вы только что ознакомились. Часто такие проекты становятся разочарованием для команды. Команда винит технологию, то есть Flutter, в том, что он недостаточно хорош, недостаточно развит и не готов к использованию в крупном и сложном проекте.

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

Комментарии (4)


  1. xuxumba
    21.04.2022 22:46
    +2

    Отлична статья! На объемные темы я текст лучше воспринимаю, спасибо.


  1. cVoronin
    22.04.2022 00:14

    Спасибо, очень полезно.


  1. Mitai
    22.04.2022 13:08

    интересно что же выведет код с миксинами...


    1. Mitai
      22.04.2022 14:11

      I played with your hearth