Как применить БЭМ методологию во Flutter проекте

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Меня зовут Семен Плевако, Flutter разработчик в Центре развития финансовых технологий (ЦРФТ) Россельхозбанка. Сегодня поговорим про применение БЭМ методологии в проектах на Flutter.

Я, как и многие Flutter разработчики, мигрировал из веб-разработки. По инерции хотелось использовать те же подходы к вёрстке и управлению состояниями. Если во втором случае можно было взять MobX или BLoC и получить что-то очень близкое к популярным веб фреймворкам, то с вёрсткой было не все так однозначно.

Надо заметить, что в вебе не использовался БЭМ «на всю катушку». В моих проектах не было ни сборщиков, ни BEMJSON, использовалась только малая часть инструментов: подход к названию компонентов и, так называемое, "мышление по БЭМ".

Подробнее почитать о методологии можно здесь.

Что мы хотим добиться?

  • Улучшить читаемость кода за счет нейминга;

  • Избавиться от очень объемных по коду виджетов;

  • Повторного использования кода;

  • Упростить адаптивную верстку;

  • Плавного переноса проекта на БЭМ;

  • Более быстрого создания компонентов и элементов за счет сниппетов.

Нейминг

Внутри команды самое большое сопротивление встретила необходимость отхода от общепринятых правил нейминга. Но со временем мы убедились, что добавить исключение в правила линтера было не такой и плохой идеей. Теперь компоненты всегда будут в CamelCase с нижними подчеркиваниями, также как файлы и директории. (Здесь есть некоторое отхождение и от БЭМ. Элементы у нас отделяются двумя нижними подчеркиваниями, а модификаторы одним). Например, компонент «домашняя страница» будет иметь свою директорию HomePage – это и название компонента, в этой директории будут лежать следующие файлы:


HomePage
– это компонент;

HomePage__appBar – это виджет элемент, который не может существовать отдельно;

HomePage_tablet - это модификатор, виджет, который будет отображаться для планшетной версии.

Чтобы мы могли создавать файлы и классы такого вида, нам нужно отключить правила линтера в файле analysis_options.yaml

include: package:flutter_lints/flutter.yaml

linter:
  rules:
    lowercase_with_underscores: 0
    camel_case_types: 0

Плавный переход на БЭМ

Чтобы начать создавать компоненты, вам не нужно переписывать весь проект. Достаточно ввести договоренности в команде. Отличать БЭМ виджет от других очень просто: БЭМ-компонент всегда имеет название файла типа Name__Element_modificator и название директории совпадает с названием компонента так же, как и название классов.

Остальные файлы проекта (стейты, модели, репозитории, и т.д.) оставляем без изменения, согласно рекомендациям линтера. В формате discount_page_state.dart

Когда вы будете заниматься рефакторингом старых виджетов, вы можете на своё усмотрение перемещать виджеты на БЭМ. При этом все итерации не будут создавать больших проблем, и со временем весь проект станет единообразным.

Как быстро создавать компоненты и элементы

Когда начнете создавать виджеты по БЭМ вы заметите, что приходится довольно много создавать небольших виджетов. Для этого были сделаны сниппеты под Android Studio.

  1. Чтобы добавить свой сниппет, перейдите в Android Studio -> Preferences -> Live Templates; 

  2. Создайте группу под названием «custom»;

  3. Добавьте сниппет izBem;

    import 'package:flutter/material.dart';
    
    class $NAME$ extends StatelessWidget {
      const $NAME$({
        Key? key,
      }) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Container($END$);
      }
    }
  4. Также выберите контекст Dart;

  5. Измените значение переменной Name, установите значение fileNameWithoutExtension() как показано на скриншоте;

Теперь всё готово, чтобы быстро создавать компоненты или элементы.

Для VSCode создание сниппетов практически ничем не отличается, однако синтаксис будет немного другой.

Code -> Preferences -> UserSnippets;

"BEM easy component": {
		"scope": "dart",
		"prefix": "izBem",
		"body": [
			"import 'package:flutter/material.dart';",
			"",
			"/// Компонент ${1:}",
			"class $TM_FILENAME_BASE extends StatelessWidget {",
			"\tconst $TM_FILENAME_BASE({Key? key}) : super(key: key);",
			"",
			"\t@override",
			"\tWidget build(BuildContext context) {",
			"\t\treturn Container();",
			"\t}",
			"}"
		]
	},

Порядок создания компонента такой

  1. Придумываете название этого компонента, например LoginPage;

  2. Создаете в папке LoginPage файл LoginPage.dart;

  3. После создания в файле сразу вводите izBem и нажимаете Enter;

  4. Сниппет возьмет название файла и создаст виджет с таким же названием;

  5. Чтобы создать элемент, скопируйте название компонента и создайте рядом новый файл с двумя подчеркиваниями и названием элемента LoginPage__element, либо модификатора LoginPage__element_tablet и также используйте izBem.

У нас есть и другие сниппеты, например позволяющие быстро создать шаблон стейта для МobX. Вообще сниппеты крайне удобный инструмент, который можно гибко настроить под нужды вашего проекта. Что же касается БЭМ подхода, то вы можете самостоятельно дописать в сниппет свои конструкции, которые будут нужны исключительно в вашем случае. Главное здесь придерживаться правил нейминга.

Адаптивная вёрстка

Чтобы быстро и эффективно разрабатывать виджеты для различных размеров экранов, можно использовать утилиту responsive которую можно также найти в этом репозитории в lib/utils/responsive.dart скопируйте его себе в проект

С помощью данной утилиты можно писать вот такой код:

@override
Widget build(BuildContext context) {  
  return responsive(
    context,
    phone: const LoginPage_phone(),
    tablet: const LoginPage_tablet(),
  );
}

При изменении размеров экрана будет изменяться дерево виджетов в зависимости от модификаторов.



Запустите пример на десктопной версии и изменяйте размер экрана.
С таким подходом можно создавать очень большие проекты и легко их поддерживать.

Также как прописывать модификаторы для компонента можно прописывать модификаторы для элементов: LoginPage__hello_phone LoginPage__hello_tablet

 Размеры экранов зафиксированы, вы можете изменить их сами, если посчитаете нужным:

const double ResponsiveMinPhoneSize = 480.0;
const double ResponsivePhoneSize = 640.0;
const double ResponsiveTabletSize = 768.0;

Также в этой утилите есть дополнительные функции, которые могут вам пригодиться.

Избавляемся от антипаттерна

Когда мы пишем большой виджет с огромной вложенностью, первое, что приходит на ум – вынести часть вёрстки в отдельную функцию. Со временем файл разрастается такими функциями и становится сложно читаемым, иногда такой подход увеличивает файл до тысячи строк.

Так как форма логина может существовать отдельно от страницы логина, то мы её вынесем в отдельный компонент. Для примера я вынес в отдельный элемент label, и посмотрите, насколько легче воспринимать такую структуру. Если вы ранее занимались вёрсткой под веб-приложения по БЭМ – вы должны оценить.

Мы сразу улучшаем читаемость и уменьшаем основной файл в 2 раза, также виджет LoginForm__field стал константой, что положительно влияет на производительность. Так как константы лишний раз не перерисовываются в отличие от функции.

После внедрения подобного подхода к неймингу на нашем проекте можно выделить следующие плюсы:

  • Читаемость кода сильно увеличилась, что положительно сказывается на погружении новых разработчиков в проект;

  • Разбиваем виджеты на более мелкие вместо использования build-методов. При использовании MobX оборачивание самого конечного легковесного виджета даёт приличную оптимизацию, поскольку рендериться при изменении будет только он;

  • Также есть в планах десктопные версии и адаптивная вёрстка (сейчас опробовали на уровне PoC), а БЭМ методология отлично, если не идеально подходит для такого рода задач.

Код можно посмотреть здесь.

На этом пока все! Спасибо что дочитали. 

В следующей статье расскажу, как использовать данный подход в связке с MobX и Provider, как создавать отзывчивые компоненты без единого (почти) StatefulWidget. Подпишись, чтобы не пропустить.

Источник: https://habr.com/ru/company/rshb/blog/670368/


Интересные статьи

Интересные статьи

Приветствую, коллеги! Меня зовут Женя и я менеджер по развитию продукта в Digital Design. По долгу службы я часто посещаю зарубежные онлайн-конференции, посвященные корпоративному управлению и актуаль...
Как мы ускорили автоматизированное тестирование в большом проектеМы делаем масштабные приложения на высококонкурентном рынке. Чем выше скорость обновлений и внедрений новых фич, тем больше зарабатывае...
На работе я занимаюсь поддержкой пользователей и обслуживанием коробочной версии CRM Битрикс24, в том числе и написанием бизнес-процессов. Нужно отметить, что на самом деле я не «чист...
Слева — нейрофизиолог Елена Белова, справа — робот-хирург Neuralink Летом 2019 прошла презентация стартапа Neuralink, цель которого — создать интерфейс типа «мозг—машина». Илон Маск рассказа...
Довольно часто владельцы сайтов просят поставить на свои проекты индикаторы курсов валют и их динамику. Можно воспользоваться готовыми информерами, но они не всегда позволяют должным образом настроить...