Здравствуйте! Меня зовут Андрей. Больше известен, как #кодеротбога (это самоирония, если что). Осваиваю Flutter в режиме «live-code», уже 567 трансляций. Без купюр – «from zero to hero», начиная с учебника по Dart и до полноценного «open-source» проекта в продакшене. А ещё, я скоро заканчиваю собственный онлайн-курс на 100 часов занятий – учитель учится у своих учеников. Благодаря интенсивной практике и предыдущему богатому опыту на ReactJS, сформировал набор соглашений, который хочу представить для получения фидбека: «Ваш звонок очень важен для нас, оставайтесь на линии».
Основные ограничения
Не тяните в проект всё, что блестит, как сорока. Например: dartz – про Haskell, kt_dart – про Kotlin. Познайте дзен языка Dart. Он неслучайно такой простой и выразительный – это улучшает процессы разработки и сопровождения. Используйте KISS.
Откажитесь от вашего предыдущего опыта применения архитектуры стейт-менеджмента. По исходникам проектов можно угадать, кто откуда: Аndroid разработчик – MVP, если ReactJS – Redux/MobX.
Обходите стороной «монолиты». Некоторые пакеты берутся делать всё и сразу. Помните, что отряд бежит со скоростью последнего отстающего. Не буду показывать пальцем, чтобы не оскорблять ваши чувства. Правильный выбор – «unix-way». Пакет должен делать что-то одно, и делать это максимально хорошо, иначе ему не выжить.
Как известно, в программировании всего две проблемы: инвалидация кеша и именование сущностей. 1) Не переименовывайте переменные на разных стадиях движения данных. По возможности, применяйте одинаковое название: в таблице, в модели, внутри виджета и т.п. 2) Все имена функций начинаются глаголом (а не префиксом вашего модуля, например). 3) Подружитесь с пакетом lint – он более гибкий, чем pedantic. Приходит на помощь в любой непонятной ситуации и вырабатывает привычку на код-стайл.
Структура файлов
Да-да-да, разделение по фичам удобнее в ряде случаев. Например, когда проекты на потоке, и нужно копипастить функционал. Или хочется обособить мини-команды в одном большом проекте. Но, пожалуйста, не плодите папки внутри проекта. (Это очень больно для сопровождения – я знаю, о чём говорю). Используйте разделение на пакеты – каждая фича в своём собственном пакете. Подобная практика популярна в экосистеме JavaScript, посмотрите на инструмент lerna и его нарождающегося потомка для Dart – melos. Следует оговориться, что монорепозитории полезны не всегда – когда какому-то пакету становится тесно, его нужно выделить в самостоятельный пакет.
Применяйте инстументы по назначению. Помимо файловой системы, есть другие способы навигации по вашему коду. Для VSCode, включите "workbench.editor.labelFormat": "short". Обратите внимание на картинку. Теперь отображаются папки в табах и можно избавить названия файлов от суффиксов: todos_screen.dart – screens/todos.dart, todos_cubits.dart – cubits/todos.dart. Дальше мы видим хлебные крошки и выпадающее окно с классами внутри нашего модуля. Классы, которые видны наружу, сохраняют префикс по названию файла. А спрятанные внутри (с подчёркиванием), не требуют префикса. Видимость TodosBody требуется для тестирования.
Вы знаете, что члены класса с подчёркиванием имеют область видимости «protected», т.е. доступны другим классам внутри модуля (файла)? Это ещё один довод за подобную организацию вложенных виджетов – если виджеты используются в поддереве только на одном экране, то они живут в файле этого экрана. И только когда виджеты переиспользуются, они получают право на собственный файл. Вас могут напугать размеры файлов – это вопрос привычки. Навык по навигации приобретается достаточно быстро.
Переиспользуемые виджеты в папке widgets не имеют суффикса в названии класса, остальные получают суффикс (в единственном числе) по названию папки, в которой они находятся: screens/todos.dart – TodosScreen, cubits/todos.dart – TodosCubit.
Папки не допускают вложенности подпапок, за редким исключением. Итого, что мы имеем внутри lib:
common – свалка, то что не вошло в другие папки
cubits – стейт-менеджмент на BLoC
import.dart – общий импорт исключает дублирование
main.dart – точка входа в проект
models – типизированные данные
repositories – источники данных (на выходе модели)
screens – экраны приложения
widgets – переиспользуемые виджеты
Функционал
В проекте реализован и покрыт тестами Firebase Authentication, как базовый функционал для почти любого проекта. Помимо юнит-тестов и виджет-тестов, есть пример интеграционных тестов для BDD (Behavior Driven Development).
В качестве примера, реализован CRUD на BLoC & GraphQL. Если вы сомневаетесь, какой архитектурный шаблон использовать для стейт-менеджмента, то изучите исходники проекта. BLoC был представлен Google ещё в 2018-ом году, но очень сложно сделать просто. Феликс Ангелов смог, я влюбился в cubit и научился его готовить.
И в завершение, вишенка на торте – навигация без context с типизацией аргументов:
class ZoomScreen extends StatelessWidget {
Route<T> getRoute<T>({bool isInitialRoute}) {
return buildRoute<T>(
'/zoom?unit_id=${unit.id}', // для аналитики
builder: (_) => this,
fullscreenDialog: true,
isInitialRoute: isInitialRoute,
);
}
ZoomScreen(this.unit);
final UnitModel unit;
...
navigator.push<void>(ZoomScreen(unit).getRoute());
Исходники, и спасибо за внимание.