Те, кто хоть раз пробовал что-то сделать на Flutter, обычно начинали с того, что создавали новый Dart-файл и писали stfl, что сразу создавало шаблонный StatefulWidget с указанным именем для виджета и его состояния. В среде Flutter это называется Live Templates, но по сути — это сниппеты.

Помимо стандартных сниппетов stfl и stls, существуют и другие базовые шаблоны, которые уже установлены по умолчанию. А именно:

1. stanim (Statefull Widget с уже подлюченным SingleTickerProviderStateMixin и созданным контроллером)

class $NAME$ extends StatefulWidget {
  const $NAME$({super.key});

  @override
  State<$NAME$> createState() => _$NAME$State();
}

class _$NAME$State extends State<$NAME$> with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this);
  }
2. inh (Inherited widget)
  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return const Placeholder($END$);
  }
}

2. inh (Inherited widget)

class $NAME$ extends InheritedWidget {
  const $NAME$({
    super.key,
    required Widget child,
  }) : super(child: child);

  static $NAME$ of(BuildContext context) {
    final $NAME$? result = context.dependOnInheritedWidgetOfExactType<$NAME$>();
    assert(result != null, 'No $NAME$ found in context');
    return result!;
  }

  @override
  bool updateShouldNotify($NAME$ old) {
    return $SHOULD_NOTIFY$;
  }
}

3. И другие мелкие шорткаты

// thof (Экземпляр темы)
Theme.of(context)

// mdof (Экземпляр MediaQuery)
MediaQuery.of(context)

// ihof (В большинстве случаев ссылка на стейт)
$NAME$.of(context)

Редактируем снипеты

Создавать собственные сниппеты довольно просто, но первое, что я сделал при работе с ними, — это отредактировал стандартные сниппеты stls и stfl. Если вы никогда не редактировали сниппет stls, то после ввода названия виджета он оставит каретку ввода текста в центре Placeholder, который будет создан внутри метода build и который, разумеется, впоследствии будет стёрт и заменён другими виджетами.

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

// Было
class $NAME$ extends StatelessWidget {
  const $NAME$({super.key});

  @override
  Widget build(BuildContext context) {
    // Ключевое здесь
    return Placeholder($END$);
  }
}

// Стало
class $NAME$ extends StatelessWidget {
  const $NAME$({super.key});

  @override
  Widget build(BuildContext context) {
    // Автоматически из буфера обмена, если не пуст. Иначе сами пишем
    return $MYWIDGET$;
  }
}

И добавил новый вариант stfl виджета с ссылкой на State через популярный способ of(context).

class $NAME$ extends StatefulWidget {
  const $NAME$({super.key});
  
  static $SNAME$ of(BuildContext context) {
    final state = context.findAncestorStateOfType<$SNAME$>();
    assert(state != null,'State is null');
    return state!;
  }

  @override
  State<$NAME$> createState() => $SNAME$();
}

class $SNAME$ extends State<$NAME$> {
  @override
  Widget build(BuildContext context) {
    return $END$;
  }
}

В коде выше, чтобы одновременно с вводом имени виджета автоматически создавалось и имя состояния с префиксом State, необходимо добавить в настройку Expression следующий код: concat(NAME, "State").

О том где находится Expression и какие опции настроек там доступны мы поговорим ниже.

Создаем свои снипеты

Создание сниппетов я буду показывать на примере Android Studio. Чтобы найти их, перейдите в Settings / Editor / Live Templates / Flutter. Далее по нажатию иконки плюса можно приступить к созданию.

Пишем саму аббревиатуру (abbrevation) и описание (description) по желанию.

Далее вводим код, в котором места для заполнения шаблона помечаются переменными, заключёнными между символами $ с двух сторон. Ниже приведён простой пример BlocBuilder.

Кстати, в виде исключения: для переменной END нельзя задавать настройки. Не запутайтесь, где они находятся, — этот ключ используется в стандартных сниппетах исключительно как место для установки каретки.

BlocBuilder<$T$,$S$>(
  builder: (context,state) {
    return $W$;
  }
),

Каретка будет по очереди переходить между этими тремя переменными после нажатия Enter. Далее внизу страницы, в разделе Applicable in Dart, можно выбрать, в каких случаях будет использоваться сниппет.

  1. Top level — можно писать вне структур, просто в пустом месте файла например.

  2. Statement — в фигурных скобках {}

  3. Other — в любых остальных вариантах, внутри списка, например.

Далее в разделе Edit Variables для каждой переменной доступны четыре дополнительные настройки, а именно: Name, Default value, Expression и Skip if defined. Первые два пункта, думаю, не требуют пояснений. Параметр Skip if defined будет пропускать переменную, если она уже определена через Expression.

Настраиваем Expression

Expression, это по большей части готовые шаблоны, но некоторые из них дают возможность писать свою логику. Такие как regularExpression, например.

Базовые

camelCase()

Приводит ввод к camelCase

capitalize()

Делает первую букву заглавной

decapitalize()

Делает первую букву строчной

underscoresToCamelCase()

my_widget → myWidget

snakeCase()

myWidget → my_widget

upper()

В ВЕРХНИЙ РЕГИСТР

lower()

в нижний регистр

Работа со строками

concat(A, B)

My + State → MyState

substring(NAME, 0, 3)

Widget → Wid

replace(NAME, "Widget", "")

HomeWidget → Home

trim()

Убирает пробелы

escapeString()

Экранирует спецсимволы

regularExpression(VARIABLE, "PATTERN", "REPLACEMENT"),

Добавить суффикс

regularExpression(NAME, "(.*)", "$1State")

Убрать Widget

regularExpression(NAME, "(.*)Widget", "$1")

Префикс _

regularExpression(NAME, "(.*)", "_$1")

Snake → Pascal

underscoresToCamelCase() + capitalize()

Контекст IDE (очень полезно)

fileName()

Имя файла без расширения

fileNameWithoutExtension()

То же самое

className()

Имя текущего класса

packageName()

Имя пакета

methodName()

Имя метода

qualifiedClassName()

Полное имя класса

Дата и время

date()

2026-01-07

time()

14:32

timestamp()

Unix timestamp

Пользовательский ввод

complete()

Включает автодополнение

suggestVariableName()

Предлагает имя

suggestIndexName()

i, j, k

enum()

Выбор из списка

Реальный пример

В самом базовом шаблоне StatefullWidget используется конструкция для написания названия стейта используется такой expression

regularExpression(concat("", NAME, "State"), "^__", "")

Он добавляет слово State к названию виджета, и делает двойное подчеркивание, если вдруг название виджета приватное, чтобы избежать такого вида.

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


  1. umarr-devv
    08.01.2026 17:38

    Столько лет работаю Flutter разработчиком, но ни разу не думал до этого момента свои сниппеты написать, а использовал стандартные

    Огромное спасибо автору за статью - лично для меня она стала полезной


    1. markowskii Автор
      08.01.2026 17:38

      Благодарю, это мои первые статьи, очень приятное получить первый позитивный отклик от сообщества!