Думаю, многие разрабочики хотя бы раз в жизни хотели поделиться своими наработками с сообществом. Уж точно все - пользовались тем, чем делятся другие. Мое мнение на этот счет примерно такое - если ты делаешь что-то для себя и можешь это сделать таким, чтобы этим могли пользоваться другие с, относительно, небольшим количеством трудозатрат - то делай это. К тому же, выставляя "напоказ" свои велосипеды - так или иначе, придется их хотя бы покрасить. Смазать цепь. А значит и в твоем проекте данное решение будет уже более качественным. Не буду углубляться в философию опен-сорса (простите меня, нелюбители английских слов, написанных по русски), поэтому перейдем сразу к делу.
С чего начинается путь велосипедных дел мастера во Flutter?
Краткий ответ - с pub.dev. Более длинный - с ознакомления с документацией. Кстати - вот она. Начнем с самого начала - во Flutter / Dart пакеты разделяются на два типа:
Собственно - пакет (Dart package)
Плагин (Plugin package)
Отличия у них такие: простые пакеты содержат только Dart-код и могут содержать зависимости от Flutter. Плагины - это пакеты, имеющие связь с нативным кодом. Это может быть Java / Kotlin, Objective-C / Swift и с недавних пор, пожалуй, сюда можно отнести и C++ / С / etc, так как Flutter официально ступил на земли десктопов. Есть еще такие вариации, когда ты не используешь платформенный код как таковой, но при этом используешь те же плюсы через FFI. Это можно отнести, скорее всего - к плагинам. Но на самом деле в разрезе данной статьи это не играет большой роли. Далее - оба этих типа будут называться одним словом - пакет, без разделения на подтипы.
Небольшое интро провели - идем дальше. Как советует та же дока, чтобы начать писать пакет необходимо выполнить следующую команду:
flutter create --template=package my_package_name
# or
flutter create --template=plugin my_plugin_name
Я же воспользуюсь возможностями IDE, поэтому тем, кто сидит на Android Studio / IDEA, можно сделать следующее:
Создаем новый проект
Выбираем тип проекта - Plugin / Package (остальные свойства выбираем исходя из своих задач)
Отлично. Проект создан, что дальше?
Я написал пакет - как его опубликовать?
Не торопись, ковбой! Прежде чем публиковать пакет, стоит помнить о следующих вещах:
Твой опубликованный пакет будет таковым навсегда (пока существует pub.dev)
Твой пакет должен соответствовать хотя-бы каким-то минимальным требованиям к качеству кода
Для каждой новой версии пакета необходимо указывать изменения в файле
CHANGELOG.md
Прежде чем публиковаться - необходимо заботливо положить в корень твоего проекта файлик
LICENCE
с лицензией, согласно которой он будет доступенpubspec.yaml
в твоем проекте должен содержать обязательные поля, содержащие информацию о твоем проектеВсе зависимости твоего пакета должны быть опубликованы на pub.dev
Давай пройдемся по всем этим пунктам не по порядку.
Качество кода
Все опубликованные пакеты автоматически оцениваются по нескольким критериям качества кода. Рассмотрим их подробнее:
Необходимо документировать все публичные поля и методы, которые есть в твоем проекте
Тут все просто - используем ///
для всего, что будет доступно пользователям твоей прекрасной библиотеки. Например, вот так:
/// Describes a one cell of animated text:
/// We change "100" to "250"
/// Then, we have 3 animated tokens in not reversed flow:
/// 1th 2th 3th
/// | 2 | 5 | _ |
/// | 1 | 0 | 0 |
/// | _ | _ | _ |
class AnimatedToken {
AnimatedToken({
@required this.top,
@required this.center,
@required this.bottom,
@required this.direction,
@required this.topSize,
@required this.centerSize,
@required this.bottomSize,
this.axisY,
this.axisYOld,
this.axisX,
this.axisXTween,
this.opacity,
this.opacityOld,
});
/// | top |
/// | center |
/// | bottom |
final String top;
/// | top |
/// | center |
/// | bottom |
final String center;
/// | top |
/// | center |
/// | bottom |
final String bottom;
/// Describes in which direction this token will move
final Direction direction;
/// Size of top letter
final Size topSize;
/// Size of center letter
final Size centerSize;
/// Size of bottom letter
final Size bottomSize;
/// Animation in Y axis for new letter
Animation<double> axisY;
/// Animation in Y axis for old letter
Animation<double> axisYOld;
/// Animation in X axis for the same letter (old == new)
Animation<double> axisX;
Tween<double> axisXTween;
/// If token is Direction.bottom - opacity ween will be from
/// If Direction.top - 0 -> 1
Animation<double> opacity;
/// If token is Direction.bottom - opacity ween will be from
/// If Direction.top - 0 -> 1
Animation<double> opacityOld;
@override
String toString() => '''AnimatedToken {
top: $top -> $topSize
center: $center -> $centerSize
bottom: $bottom -> $bottomSize
direction: $direction
}''';
}
Не буду говорить, что такая практика позволяет и самому, спустя какое-то время, понимать что тут к чему, но она помогает и юзерам твоего пакета. Например, в той же IDEA / AS есть возможность отображения комментариев к коду по наведению курсора (прямо как в VSCode).
Желательно использовать dart fmt
- форматтер кода, настроенный в соответствии с рекомендуемыми параметрами
Тут все довольно просто. Используем зависимость pedantic или effective_dart (лично я предпочитаю pedantic, т.к. он более строгий из коробки). Затем создаем файл analysis_options.yaml
и используем в нем нашу зависимость:
include: package:pedantic/analysis_options.yaml
Если есть личные предпочтения в том, как должен выглядеть код, то можно дополнять / переопределять правила линтера. В этом помогут этот и этот ресурсы. К слову, кастомизировать можно не только правила линтера, но и общие правила языка (с некоторыми оговорками). Делается это через манипуляции в блоке:
include: package:pedantic/analysis_options.yaml
analyzer:
strong-mode:
implicit-dynamic: false
implicit-casts: false
errors:
todo: ignore
mixin_inherits_from_not_object: ignore
sdk_version_async_exported_from_core: ignore
missing_required_param: error
division_optimization: error
must_call_super: error
always_put_required_named_parameters_first: error
avoid_positional_boolean_parameters: error
unnecessary_await_in_return: error
invalid_use_of_protected_member: error
# ...
linter:
rules:
# ...
Вот тут есть весь перечень возможных ошибок / ситуаций, которыми можно управлять. Можно настроить все так, словно ты настоящий маньяк - мне нравится возможность сделать некоторые warning'и ошибками, и не позволять запускать проект в принципе, к примеру, при наличии в коде обращений к @protected
полям и методам.
После всего этого твой код, скорее всего, засияет - ты увидишь все проблемы, которые стоит исправить заблаговременно. Плюс - можно настроить IDE на полное автоформатирование кода и перестать беспокоиться, что где-то случайно поставил два пробела вместо одного или фигурная скобка висит не на той строке. Для этого нужно всего-то сделать это:
Работать после таких манипуляций намного приятнее.
Полный список параметров, из которых формируется оценка твоего пакета, показан ниже:
1. Сопроводительные файлы
2. Документирование кода. Важный момент в этом пункте связан с проектом-примером, который следует располагать в папке example твоего пакета и отражать в этом примере то, как именно следует пользоваться твоим пакетом
3. Поддержка всех платформ. С этим тоже могут быть проблемы, например - некоторые части стандартной библиотеки не могут быть использованы в Web - поэтому получить все возможные баллы для некоторых пакетов просто невозможно. Также, из интересного - после релиза Flutter 2 появилась поддержка десктопных платформ, и для таких пакетов теперь есть пометка об их поддержке. А также, такие пакеты стоит писать сразу с null-safety (при Dart >= 2.12)
4. Прохождение форматтера
5. Ну и последнее - свежие зависимости - как мотиватор хотя бы поддерживать твой пакет
Changelog
Каждое обновление пакета (и публикация первой версии) должно сопровождаться описанием того, что было изменено. Для этого в проекте должен быть специальный файл, в котором описываются изменения. Это довольно просто и выглядит следующим образом:
## [1.1.0] - Add opacity sub-animation for tokens and curves manipulation
## [1.0.1] - Add demo gif and update readme
## [1.0.0] - First release
Для каждой новой версии добавляем строку сверху согласно приведенному шаблону и все будет хорошо.
License
Все тоже весьма просто. Идем, к примеру - сюда. Ищем подходящую лицензию, берем ее текст, указываем себя как автора и готово.
Pubspec
Можно ознакомиться с документацией к данному конфигу или просто посмотреть на пример (в нем отражены необходимые для публикации поля):
name: anitex
description: Anitex is a implicitly animated text widget, which animates on passed text changes
version: 1.2.0
repository: https://github.com/alphamikle/anitex
homepage: https://github.com/alphamikle/anitex
environment:
sdk: ">=2.7.0 <3.0.0"
flutter: ">=1.17.0 <2.0.0"
dependencies:
flutter:
sdk: flutter
dev_dependencies:
flutter_test:
sdk: flutter
pedantic: ^1.9.2
flutter:
Процесс публикации
Сделать это можно таким образом:
pub publish
Результатом выполнения команды будет вывод краткого отчета, например такого:
...
...
Package validation found the following potential issue:
* ./CHANGELOG.md doesn't mention current version (2.0.0).
Consider updating it with notes on this version prior to publication.
Publishing is forever; packages cannot be unpublished.
Policy details are available at https://pub.dev/policy
Package has 1 warning.. Do you want to publish anitex 2.0.0 (y/N)?
Тут показан отчет, когда не все ок - есть проблемы с пакетом, а значит публиковать его в таком виде однозначно не стоит, поэтому, чтобы рука не сорвалась, нажав случайно y
- стоит пользоваться командой
pub publish --dry-run
Она покажет те же самые проблемы, либо их отсутствие, как тут:
...
...
Package has 0 warnings.
И когда ты увидишь заветные 0 warnings
- значит можно публиковать пакет. Какие еще есть нюансы? Нужно зарегистрироваться на том же pub.dev (с помощью аккаунта Google). А при выполнении команды публикации тебе будет предложено авторизоваться уже в консоли.
Это навечно
Даже если никто и никогда не воспользуется твоим пакетом (надеюсь, что все будет не так), гугл не позволит выполнить операцию, непосредственно, удаления твоего пакета из pub.dev (может только через поддержку, но я не пробовал). Однако, если ты понял, что совершил ошибку - то ты можешь пометить свой пакет как "Неподдерживаемый". У него появится яркая плашка, которая будет говорить всем твоим потенциальным фанатам, что этот продукт деятельности твоего ума больше не будет развиваться.
Можно зайти еще дальше и сделать пакет Unlisted - он выпадет из обычного поиска, но по прежнему будет доступен при расширенном поиске - это нечто среднее между приватным и публичным пакетом.
Также в админке управления твоими пакетами имеется возможность создать так называемого publisher - некое абстрактное лицо, от имени которого будут опубликованы пакеты. Это удобно для различных комьюнити / компаний, но каких-то особых профитов не дает (дает лычку). Еще для этого необходимо прикупить домен в .dev зоне (можно не только в ней), к которому publisher и будет привязан.
Что еще?
Пакет ты опубликовал - собрал 110 или 130 баллов, но его никто не использует... Тут начинается самое интересное - продвижение. Можно писать статьи, приводя расширенные примеры использования твоего пакета и рассказывая в деталях, почему он лучше другого очень похожего решения. На ресурсе на букву M можно встретить множество статей подобного плана. Можно начать, хотя бы, с коллег или, если есть уверенность в себе и своем решении - использовать его в рабочем проекте. После достижения хотя бы какой-то известности можно попытать удачу и податься, например - сюда. Это коллекция интересных open source решений для Flutter, и там может оказаться и твой прекрасный пакет!
Выводы
Их не особо много - процесс публикации библиотек в экосистеме Flutter выглядит довольно простым, а сама идея делиться своими наработками с сообществом очень благородна, и, как по мне - обязательна, просто потому что каждый разработчик пользовался результатом умственного труда других разработчиков и будет весьма справедливым - внести и свою лепту. К тому же, это полезно и тебе, дорогой друг - новые знакомства из open source комьюнити, новые возможности в поиске работы (многие HR'ы ищут разрабов уже и на GitHub), да и просто развитие себя, как технического специалиста.