Пишем и публикуем идеальный пакет для Flutter | OTUS

Пишем и публикуем идеальный пакет для Flutter

Думаю, многие разработчики хотя бы раз в жизни хотели поделиться своими наработками с сообществом. Уж точно все пользовались тем, чем делятся другие. Мое мнение на этот счет примерно такое: если ты делаешь что-то для себя и можешь это сделать таким, чтобы этим могли пользоваться другие с, относительно небольшим количеством трудозатрат -- делай это. К тому же, выставляя "напоказ" свои велосипеды -- так или иначе, придется их хотя бы покрасить. Смазать цепь. А значит, и в твоем проекте данное решение будет уже более качественным. Не буду углубляться в философию опен-сорса (простите меня, нелюбители английских слов, написанных по-русски), поэтому перейдем сразу к делу.

С чего начинается путь велосипедных дел мастера во Flutter?

Краткий ответ -- с pub.dev. Более длинный -- с ознакомления с документацией. Кстати -- вот она: https://flutter.dev/docs/development/packages-and-plugins/developing-packages. Начнем с самого начала -- во Flutter/Dart пакеты разделяются на два типа:

  1. Собственно, пакет (Dart package).
  2. Плагин (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, можно сделать следующее:

1.Создаем новый проект.

55b4462d8635170cba61ce2e9d218861_1-1801-8f3475.png

2.Выбираем Flutter в качестве основы (предварительно -- вы должны установить Flutter + Dart плагины.

7d8273a101b0f6141b4a4acb7d6b5454_1-1801-da2d6f.png

3.Выбираем тип проекта -- Plugin / Package (остальные свойства выбираем исходя из своих задач).

1-1801-491f84.png

Отлично. Проект создан, что дальше?

Я написал пакет -- как его опубликовать?

Не торопись, ковбой! Прежде чем публиковать пакет, стоит помнить о следующих вещах:

  • твой опубликованный пакет будет таковым навсегда (пока существует 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).

d166d4946ee8290a43ea53a52ca6e13e_1-1801-0080f7.png

Желательно использовать 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 на полное автоформатирование кода и перестать беспокоиться, что где-то случайно поставил два пробела вместо одного или фигурная скобка висит не на той строке. Для этого нужно всего-то сделать это:

1aef02b8b222413d81295d4b785c79f0-1801-994e13.png

Работать после таких манипуляций намного приятнее.

Полный список параметров, из которых формируется оценка твоего пакета, показан ниже:

1.Сопроводительные файлы:

83deb4bf937ef5d959db0b4bcfe89510_1-1801-1dd7f8.png

2.Документирование кода. Важный момент в этом пункте связан с проектом-примером, который следует располагать в папке example твоего пакета и отражать в этом примере то, как именно следует пользоваться твоим пакетом:

2597db53ec25fff21ca15f4c78b559a5_1-1801-89c790.png

3.Поддержка всех платформ. С этим тоже могут быть проблемы, например, некоторые части стандартной библиотеки не могут быть использованы в Web -- поэтому получить все возможные баллы для некоторых пакетов просто невозможно. Также, из интересного -- после релиза Flutter 2 появилась поддержка десктопных платформ, и для таких пакетов теперь есть пометка об их поддержке. А также такие пакеты стоит писать сразу с null-safety (при Dart >= 2.12).

0e064c781ed7ce2b399b651f2dc3b281_1-1801-048e0a.png

4.Прохождение форматтера.

d8e20e82358bece7254aee8555fdff1b_1-1801-a6ad4d.png

5.Ну и последнее -- свежие зависимости -- как мотиватор хотя бы поддерживать твой пакет.

eb15ebdb9737343949c1134509c7a2d3_1-1801-9bea51.png

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), да и просто развитие себя, как технического специалиста.

Не пропустите новые полезные статьи!

Спасибо за подписку!

Мы отправили вам письмо для подтверждения вашего email.
С уважением, OTUS!

Автор
0 комментариев
Для комментирования необходимо авторизоваться
Популярное
Сегодня тут пусто