Всем привет! На связи Глеб Гусаров, flutter-разработчик в команде aim digital. Мы занимаемся разработкой цифровых продуктов и сервисов и автоматизацией процессов для бизнеса. В статье рассказываю, как создать файл конфигурации сниппетов и делюсь своей подборкой полезных сниппетов.

Snippet — это шаблон (подсказка в коде), по вызову которого можно быстро вставить готовый код.
В файле конфигурации сниппет представляет собой сущность в JSON имеющую ключ состоящую из двух полей:
поле prefix — тип данных String — ключевое слово, при написании которого будет предложено использовать snippet
поле body — массив String — код, который будет вставлен после применения snippet'a
Пример snippet'a :
"localizationImport": { "prefix": "localizationImport", "body": [ "import 'package:perfluence_app/generated/l10n.dart';", ], }
Специальные возможности и регулярные выражения в snippet'ах
"\t" — табуляция.
"$1" — место куда будет поставлен курсор после применения snippet'a, можно использовать $2, $3 и т.д. Цифра — это очередность позиции, куда встанет курсор после нажатия на Tab.
"$TM_FILENAME_BASE" — обращение к названию файла без расширения.
"${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/g}" — вернет название файла в CamelCase.
Как создать файл конфигурации Snippet'ов
В Visual Studio code нажать комбинацию клавиш cmnd(ctrl) + shift + p
В появившемся поле ввести — Snippets
Выбрать в выпадающем меню пункт Preferences: Configure User Snippets и нажать Enter
Выбрать расширение файла, для которого будут использованы Snippet'ы (В нашем случае файлы с расширением .dart). После чего создастся json-файл конфигурации Snippet'ов.

Примеры полезных сниппетов
Шаблон для дата классов (десериализация json_annotation + equatable)
"dataClassFrom": { "prefix": "dataClassFrom", "body": [ "import 'package:equatable/equatable.dart';", "import 'package:json_annotation/json_annotation.dart';", "", "part '$TM_FILENAME_BASE.g.dart';", "", "@JsonSerializable(", "\tfieldRename: FieldRename.snake,", "\tcreateToJson: false,", ")", "", "class ${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/g} extends Equatable{", "\tconst ${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/g}();", "", "\tfactory ${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/g}.fromJson(Map<String, dynamic> json) => _$${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/g}FromJson(json);", "", "\t$1", "", "\t@override", "\tList<Object?> get props => [];", "}", ] },
Шаблон для дата классов (десериализация, сериализация json_annotation + equatable)
"dataClassToFrom": { "prefix": "dataClassToFrom", "body": [ "import 'package:equatable/equatable.dart';", "import 'package:json_annotation/json_annotation.dart';", "", "part '$TM_FILENAME_BASE.g.dart';", "", "@JsonSerializable(", "\tfieldRename: FieldRename.snake,", ")", "", "class ${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/g} extends Equatable{", "\tconst ${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/g}();", "", "\tfactory ${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/g}.fromJson(Map<String, dynamic> json) => _$${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/g}FromJson(json);", "", "\tMap<String, dynamic> toJson() => _$${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/g}ToJson(this);", "", "\t$1", "", "\t@override", "\tList<Object?> get props => [];", "}", ] },
Шаблон Stateless widget
"stateless": { "prefix": "stless", "body": [ "import 'package:flutter/material.dart';", "", "class ${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/} extends StatelessWidget {", "\t@override", "\tWidget build(BuildContext context) {", "\t\treturn $1;", "\t}", "}", ] },
Шаблон Statefull widget
"statefull": { "prefix": "stfull", "body": [ "import 'package:flutter/material.dart';", "", "class ${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/} extends StatefulWidget {", "\t@override", "\tState<${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/}> createState() => _${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/}State();", "}", "", "class _${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/}State extends State<${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/}> {", "\t@override", "\tWidget build(BuildContext context) {", "\t\treturn $2;", "\t}", "}", ] },
Шаблон для быстрого создания цвета из HEX
"color": { "prefix": "color", "body": "const Color(0xFF$1)", },
Шаблон для быстрого создания BoxShadow
"boxShadow": { "prefix": "bsh", "body": [ "BoxShadow(", "\tcolor: Colors.black.withOpacity(.5),", "\toffset: const Offset(0, 0),", "\tblurRadius: 0,", ")," ] },
Шаблон для написания тестов arrange, act, assert
"aaaTest": { "prefix": "aaaTest", "body": [ "test(", "\t'should $1',", "\t() async {", "\t//arrange", "\t$2", "\t//act", "\t", "\t//assert", "\t},", ");", ], },
Шаблон для создания интерфейса репозитория
"repo": { "prefix": "repo", "body": [ "import '/core/error/failures.dart';", "import 'package:dartz/dartz.dart';", "", "abstract class ${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/g} {", "\t$2", "}", ], },
Шаблон для написания имплементации репозитория
"repoImpl": { "prefix": "repoImpl", "body": [ "import 'package:dartz/dartz.dart';", "import '/core/error/failures.dart';", "class ${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/g} implements $1 {", "\t@override", "\tFuture<Either<Failure, $2>> getConcreteNumberTrivia() async {", "\t\treturn const Left(DefaultFailure());", "\t}", "}", ], },
Шаблон для создания дата-класса параметров usecase
"usecaseParams": { "prefix": "usecaseParams", "body": [ "import 'package:dartz/dartz.dart';", "import 'package:equatable/equatable.dart';", "import '/core/error/failures.dart';", "import '/core/usecases/usecase.dart';", "class ${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/g}UC implements UseCase<$1, ${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/g}Params> {", "\tconst ${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/g}UC(this.repo);", "", "\tfinal $2 repo;", "", "\t@override", "\tFuture<Either<Failure, $1>> call(${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/g}Params params) async {", "\t\treturn await repo.$3();", "\t}", "}", "", "class ${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/g}Params extends Equatable {", "\tconst ${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/g}Params();", "\t@override", "\tList<Object?> get props => [];", "}", ] },
