REEEL Формат
This page has not been translated into English yet. The Russian original is below.
REEEL — это текстовый формат для описания древовидных структур элементов. Формат использует отступы для представления иерархии и поддерживает переменные, макросы и различные типы данных.
Назначение
Формат REEEL предназначен для построения дерева элементов из текстовых строк с отступами. Формат универсален и может использоваться для различных целей:
- Описание дерева UI-виджетов
- Моделирование бизнес-логики
- Описание моделей данных
- Любые другие древовидные структуры
Расширение файла
Файлы формата REEEL имеют расширение
.reeel
.
Базовый синтаксис
Элементы
Каждая строка может содержать элемент. Элементы начинаются с названия, которое может быть любым:
// Single child
MyElement
parametr1:string
parametr2:"string 2"
InnerElement1
// Single child
InnerElement2
Отступы
Вложенность элементов определяется отступами в начале строки.
Один уровень вложенности = два пробела
(
" "
).
ParentElement
ChildElement
GrandChildElement
Комментарии
Строки, начинающиеся с
//
, являются комментариями и игнорируются парсером:
// Это комментарий
Element
// Комментарий внутри элемента
ChildElement
Пустые строки
Пустые строки игнорируются при парсинге.
Стиль именования
Рекомендация
: используйте
UpperCamelCase
для названий элементов и
lowerCamelCase
для названий параметров. Это не обязательное правило, но улучшает читаемость:
UserProfile
firstName:"Иван"
lastName:"Иванов"
Параметры элементов
Синтаксис параметров
Параметры бывают именованными и позиционными :
-
Именованные
:
key:valueилиkey: value— доступны по имени - Позиционные : без двоеточия — доступны по индексу (0, 1, 2, ...)
Параметры могут быть записаны:
- На той же строке, что и элемент :
Element value0 key1:value1 key2:10 "Long string!"
-
На следующих строках
(именованные — с двоеточием в формате
name:value, позиционные — с двоеточием без имени, в формате:value):
MyElement
parametr1:string
parametr2:"string 2"
Разделители
- Пробел используется для разделения параметров между собой
-
Двоеточие
(
:) отмечает имя именованного параметра - Параметры без двоеточия являются позиционными
Позиционные параметры на новой строке
Позиционные параметры можно записывать с новой строки, начиная строку с
:
(двоеточие без имени перед ним):
ElementWithNewLineArguments
:"position0" // позиционный параметр (индекс 0)
named1:value1 // именованный параметр
:100 // позиционный параметр (индекс 1)
named2:value2 // именованный параметр
Типы значений параметров
1. Строки
Строковые значения задаются двумя способами:
stringParam:string
stringParam:"String long"
-
Если значение начинается с
"— это строка - Если не определился другой тип и значение состоит из слова без пробела и двоеточия — тоже строка
Перенос строки и экранирование
\n
внутри строки в кавычках — это
перенос строки
, и единственный способ его записать: значение кончается на переносе строки файла, поэтому набрать перенос напрямую нельзя.
Text "Tall\nText\nHere"
Text $"${name},\nс возвращением"
Экранируемых последовательностей ровно четыре:
\"
,
\'
,
\\
и
\n
.
Любой другой обратный слэш сохраняется как написан
— благодаря этому регулярное выражение и путь читаются как есть (
"\d+"
остаётся
\d+
, а не
d+
). Чтобы получить два символа
\
и
n
, пишите
\\n
.
Противоположная кавычка экранирования не требует: внутри
'…'
свободно стоит
"
, внутри
"…"
—
'
.
2. Boolean
boolParam:true
boolParam:false
3. Числовые значения
intParam:10
doubleParam:.5
doubleParam:1.222
4. Duration (длительность)
Если значение начинается с числа и заканчивается на
s
или
ms
:
durationParam:10s
durationParam:100ms
5. Значение переменной и подписка (
$
)
Для получения значения переменной используется префикс
$
. Если переменная объявлена с префиксом
@
, подписка создаётся
автоматически
— при изменении переменной элемент перестраивается:
// @text — реактивная переменная; $text создаёт подписку автоматически
State @text:"Initial"
Text text:$text
// text — обычная переменная; $text просто читает значение без подписки
State text:"Initial"
Text text:$text
Поиск переменной происходит путём обхода родительских элементов вверх по дереву.
Доступ к членам по точке (
$obj.member
)
Если переменная содержит
Map
/объект, к его членам можно обращаться через точку — это сахар над subscript (
$obj.member
≡
$obj['member']
), работает и в позиции значения, и в выражениях, цепочками (
$a.b.c
). Отсутствующий член даёт
null
(прикрывайте
??
):
// item — Map; читаем поля по точке
Text $item.title
Text {item.price ?? 0}
Если голова — реактивная переменная, чтение тоже реактивно (перестраивается при изменении).
На этом механизме построены
ambient-объекты
$theme
и
$strings
— тема/цвета и переводы, доступные в любом файле без объявления:
color:$theme.primary
,
Text $strings.welcome
. См.
Тема и локализация
.
6. Шаблонные строки (
$"
)
Шаблонные строки начинаются с
$"
(или
$'
) и позволяют подставлять значения выражений через
${expression}
. Если переменная объявлена с
@
, подписка также создаётся автоматически.
Одиночные
{
и
}
внутри шаблонной строки — это обычные символы, экранировать их не нужно. Экранирование кавычек тоже не требуется: просто используйте альтернативную кавычку для обрамления строки (
$"…'…"
или
$'…"…'
).
// Без реактивности
State userName:"Tester"
Text text:$"Hello ${userName}"
// С реактивностью (@ на переменной)
State @remainingSeconds:30
Text text:$"Send after ${remainingSeconds} seconds"
// Литеральные фигурные скобки и вложенные выражения
State @count:5 firstName:"John"
Text value:$'{"to":"${firstName}","count":${count}}'
7. Вычисляемые выражения (
{expression}
)
Фигурные скобки
{...}
вычисляют выражение и возвращают результат в качестве значения параметра. Поддерживается почти полный набор математических и логических операций в синтаксисе Dart:
-
Тернарный оператор
:
{focused ? primaryColor : secondaryColor} -
Арифметика
:
{10 * 2 - 10 / 5},{2 * (3 + 4) / 5},{10 % 3},{10 ~/ 3} -
Сравнение
:
{4 >= 3},{a == b},{a != b},{a > b},{a < b},{a <= b} -
Логика
:
{a && b},{a || b},{!isEmpty(name)},{focused && isCurrent ? 3 : 1} -
Скобки
для группировки:
{(true && false) == false} -
Доступ по индексу / ключу
(subscript):
{data[0]['color']},{obj[i]} -
Вызов функций
:
{upperCase(userName)},{plural(count, one:"файл", few:"файла", many:"файлов", other:"файлов")}
Внутри
${...}
в шаблонных строках (
$"…${expr}…"
) допускается
любое
выражение, а не только имя переменной — например,
$"${upperCase(userName)}: ${count + 1}"
.
Полный список встроенных функций (строковые функции,
print
,
plural
), синтаксис callback-выражений с аргументами
(args){body}
и описание регистрации пользовательских функций — см.
expression_functions.md
.
APincodeField length:6
@ItemBuilder $focused:false $isCurrent:false $value:""
DecoratedBox
#decoration
#border fromBorderSide
#side
color:{focused ? primaryFocusedColor : primaryUnfocusedColor}
width:{focused && isCurrent ? 3 : 1}
Оператор
[...]
обращается к параметрам вложенной структуры (
#name
/
:#
):
-
целочисленный индекс → позиционный
:#элемент -
строковый ключ → именованный параметр (
key:value) -
работает как для позиционных (
:#), так и для именованных (#name) структур - индекс может быть переменной, операторы можно цеплять
// Доступ к элементам вложенной структуры (#data содержит :# элементы)
DataWidget
#data
:# title:"Title text" color:0xfff
:# title:"Title 2" color:0x000
Column
ColoredBox color:{data[0]['color']} // data[0] — первый :# элемент; ['color'] — именованный параметр
Text {data[0]['title']}
ColoredBox color:{data[1]['color']}
Text {data[1]['title']}
8. Изменение значений переменных (
{key=val;key=val}
)
Для изменения значений нескольких переменных используется синтаксис с
=
и
;
как разделителем (пробелы необязательны):
State @scale:1.0 @opacity:1.0
Animation scale:$scale opacity:$opacity
Button onHover:{scale=1.5;opacity=0.8} onExit:{scale=1.0;opacity=1.0}
Text "Interactive"
Для числовых переменных есть короткая форма
x++
/
x--
— эквивалент
x = x + 1
/
x = x - 1
, возвращает новое значение:
State @counter:0
Column
Button onTap:{counter++}
Text "Inc"
Button onTap:{counter--}
Text "Dec"
Text text:$"${counter}"
Ограничения: только голые переменные (не
arr[0]++
и не
obj.field++
), только числовые значения, только постфиксная форма (без
++x
/
--x
).
9. Callback с аргументами (
(args){body}
)
Если вызывающий Dart-код передаёт в callback значения (например,
index
от билдера списка,
value
от
onChanged
), их можно принять, объявив список имён в скобках
перед
телом
{...}
:
ListBuilder itemBuilder:(index){$"Item #${index}"}
TextField onChanged:(value){text=value}
Button onTap:(a, b){a + b}
Объявленные аргументы перекрывают одноимённые переменные родителей. Подробности и примеры — см. expression_functions.md .
Переменные и состояние
Объявление переменных
Переменные объявляются с префиксом
@
. Такие переменные являются
реактивными
: при изменении их значения все элементы, использующие
$variable
, перестраиваются автоматически.
State @variableName:"Initial value"
Можно объявить несколько переменных на одной строке:
State @scale:1.0 @opacity:1.0
Переменные без
@
являются обычными константами и не создают подписок:
baseUrl:"http://localhost:8500/api"
Область видимости переменных
Переменные доступны всем дочерним элементам в дереве:
State @scale:1.0
Animation scale:$scale
Button onHover:{scale=1.5}
Padding all:10
Text "Nested"
Мульти-элементы
Мульти-элементы — это элементы, у которых может быть несколько прямых дочерних элементов . Элемент автоматически становится мульти-элементом, если у него несколько дочерних элементов с одинаковым отступом.
Базовый синтаксис
Column
Text
SizedBox
Text
Паттерн для однотипных дочерних элементов
Если все дочерние элементы одного типа, можно использовать синтаксис
ParentElement{ChildType}
:
Column{Text}
text:"Hello"
text:" "
text:"World!"
Важно
: при использовании паттерна все строки с параметрами (
param:value
) считаются дочерними элементами указанного типа, а не параметрами родительского элемента.
Chain
Chain
— специальный элемент, который принимает плоский список дочерних элементов и автоматически вкладывает их друг в друга: первый элемент оборачивает второй, второй — третий, и так далее. Последний элемент в списке становится листовым содержимым.
Это позволяет избежать глубокой вложенности при описании цепочки элементов-обёрток и упрощает добавление, удаление или перестановку элементов без изменения отступов.
Без
Chain
— глубокая вложенность:
Storage
@accessToken:""
@isAuthenticated:false
HttpHeaders
Authorization:$"Bearer ${accessToken}"
Actions "auth:logout":{isAuthenticated=false;accessToken=null}
Column
$Header "Title"
С
Chain
— плоский список:
Chain
Storage
@accessToken:""
@isAuthenticated:false
HttpHeaders
Authorization:$"Bearer ${accessToken}"
Actions "auth:logout":{isAuthenticated=false;accessToken=null}
Column
$Header "Title"
Оба примера эквивалентны:
Storage
оборачивает
HttpHeaders
,
HttpHeaders
оборачивает
Actions
,
Actions
оборачивает
Column
.
Переменные, объявленные в любом элементе цепочки (например,
@accessToken
в
Storage
), доступны всем последующим элементам цепочки.
Вложенные структуры
Вложенные структуры позволяют передавать объекты как параметры элементов. Они начинаются с
#
на новой строке.
Именованная вложенная структура (
#name
)
#name
начинает именованную вложенную структуру. Её параметры могут быть:
-
Инлайн
— на той же строке, что и
#name(позиционные и именованные параметры) -
Вложенными
— на следующих строках с дополнительным отступом (в том числе вложенные
#child)
ElevatedButton onPressed:$onPressed
#style
backgroundColor:$primaryFocusedColor
disabledBackgroundColor:$disableColor
elevation:0
shadowColor:0x0
#padding symmetric horizontal:48 vertical:24 // "symmetric" — позиционный параметр инлайн
#shape rounded // "rounded" — позиционный параметр инлайн
#borderRadius circular 24 // позиционные параметры инлайн
$Text.button $text
Вложенные структуры могут быть многоуровневыми:
TextFormField
#style color:$textPrimaryColor fontSize:22
#decoration
hintText:$placeholder
#hintStyle color:$textSecondaryColor fontSize:22
#border outline
#borderRadius circular 24
#focusedBorder outline
#borderSide color:$primaryFocusedColor width:3
#borderRadius circular 24
fillColor:$backgroundSecondaryColor
filled:true
Позиционная вложенная структура (
:#
)
:#
начинает позиционную вложенную структуру (передаётся как следующий позиционный аргумент родительского элемента). Параметры структуры могут быть инлайн на той же строке или вложенными:
Structure
:# position0 color:0xff0000ff // позиционный аргумент "position0" + именованный параметр инлайн
:# position1 value:true
:# // позиционная структура с вложенными дочерними структурами
#shape rounded
#borderRadius circular 24
backgroundColor:#00ff00
Инлайн-параметры
после
:#
или
#name
— это краткая запись, полностью эквивалентная вложенной:
// Инлайн (компактно)
:# position0 color:0xff0000ff
// Эквивалентная вложенная запись
:#
:position0
color:0xff0000ff
Макросы
Макросы позволяют определить переиспользуемые структуры элементов с параметрами.
Объявление макросов
Макросы объявляются с префиксом
@
:
@PrimaryButton
AButton.primary onPressed:$onPressed
AText.button text:$text
Использование макросов
$PrimaryButton onPressed:"navigate:enter" text:"Вход"
Параметры макросов
Передача параметров
Параметры передаются при использовании макроса. В определении используются через
$
:
@PrimaryButton
AButton.primary onPressed:$onPressed
AText.button text:$text
Параметры по умолчанию
@SecondaryButton text:"Register text"
AButton.secondary onPressed:$onPressed
AText.button text:$text
// Использует значение по умолчанию для text
$SecondaryButton onPressed:"navigate:register"
// Переопределяет значение по умолчанию
$SecondaryButton onPressed:"navigate:register" text:"Регистрация"
Позиционные параметры макросов
Макрос может принимать позиционные параметры. Позиционная переменная объявляется с
$
в заголовке макроса:
@Text.h1 $text color:$textPrimaryColor align:center
Text $text textAlign:$align
#style fontSize:56 color:$color
Использование:
$Text.h1 "Заголовок"
$Text.h1 "Заголовок" color:#FF0000
Макросы с дочерними элементами
Для сложных макросов с дочерними элементами используется параметр
child
без значения
— он указывает, куда вставлять дочерние элементы.
Важно
: если
child
не указан, дочерние элементы добавляются к последнему элементу в макросе.
@ColumnMacros
Column spacing:25
Expanded
SizedBox
SizedBox height:200 child
Padding
$ColumnMacros
Text text:"Target text"
Предел раскрытия макросов
Каждый вызов
$Макроса
при разборе файла копирует тело макроса в дерево. Макрос, который несколько раз зовёт другой, умножается на каждом уровне вложенности, поэтому разбор считает скопированные элементы:
-
больше
50 000
— файл собирается целиком, анализатор предупреждает (
macro_expansion_wide); -
100 000
— предел: вызовы за ним остаются пустыми, анализатор сообщает об ошибке (
macro_expansion_limit). Счёт один на файл вместе с его импортами.
Для сравнения: самая большая страница настоящего сайта копирует около 20 000 элементов, полутораминутная сцена — около 3 000. То, что повторяется сотнями, выводят списком (
ListView.builder
, в сцене —
*Repeat
), а не копиями макроса.
Полный пример с макросами
@PrimaryButton onPressed:null text:""
ElevatedButton onPressed:$onPressed
#style
backgroundColor:$primaryFocusedColor
elevation:0
#padding symmetric horizontal:48 vertical:24
#shape rounded
#borderRadius circular 24
$Text.button $text
@SecondaryButton onPressed:null text:""
ElevatedButton onPressed:$onPressed
#style
backgroundColor:$backgroundSecondaryColor
elevation:0
#padding symmetric horizontal:48 vertical:24
#shape rounded
#borderRadius circular 24
$Text.button $text
Column
$PrimaryButton onPressed:"navigate:login" text:"Войти"
$SecondaryButton onPressed:"navigate:register" text:"Регистрация"
Импорты
Документ может импортировать другие
.reeel
-файлы для повторного использования макросов, переменных и констант. Строка импорта размещается в самом начале файла:
import components/theme components/buttons
Несколько путей разделяются пробелами. Пути указываются относительно корня проекта, без расширения
.reeel
.
Что импортируется
Из импортируемого файла берутся только
объявления
(
@
-макросы и переменные/константы). Блоки примеров использования (дерево элементов без
@
) в финальное дерево не попадают — они служат только для разработки и предпросмотра в редакторе.
Пример
components/buttons.reeel
:
import text theme
@PrimaryButton onPressed:null text:""
ElevatedButton onPressed:$onPressed
#style
backgroundColor:$primaryFocusedColor
#padding symmetric horizontal:48 vertical:24
#shape rounded
#borderRadius circular 24
$Text.button $text
// Примеры использования — в финальное дерево не попадают
Column
$PrimaryButton onPressed:"navigate:login" text:"Войти"
Документ, который использует этот файл:
import components/theme components/buttons
Column
$PrimaryButton onPressed:"navigate:login" text:"Войти"
$PrimaryButton onPressed:"navigate:register" text:"Регистрация"
Цепочки импортов
Импортируемые файлы сами могут содержать
import
. Всё дерево зависимостей разрешается автоматически:
// components/text.reeel
import theme
@Text.h1 $text color:$textPrimaryColor align:center
Text $text textAlign:$align
#style fontSize:56 color:$color
// components/buttons.reeel импортирует text
import text theme
@PrimaryButton onPressed:null text:""
ElevatedButton onPressed:$onPressed
$Text.button $text
Полные примеры
Пример 1: Базовая структура
Container
padding:20
Column
Text text:"Заголовок"
SizedBox height:10
Text text:"Описание"
Пример 2: Работа с переменными и реактивность
State @counter:0
Column
Text text:$"Счётчик: ${counter}"
Button onPressed:{counter=counter+1}
Text "Увеличить"
Пример 3: Анимация с состоянием
State @scale:1.0 @opacity:1.0
Animation scale:$scale opacity:$opacity duration:300ms
Button
onHover:{scale=1.5;opacity=0.8}
onExit:{scale=1.0;opacity=1.0}
Text "Наведи на меня"
Пример 4: Мульти-элементы с паттерном
Row{Button}
text:"Кнопка 1"
text:"Кнопка 2"
text:"Кнопка 3"
Пример 5: Вложенные структуры
ElevatedButton onPressed:$onPressed
#style
backgroundColor:$primaryFocusedColor
elevation:0
shadowColor:0x0
#padding symmetric horizontal:48 vertical:24
#shape rounded
#borderRadius circular 24
Text "Нажми"
Пример 6: Вычисляемые выражения
APincodeField length:6
@ItemBuilder $focused:false $isCurrent:false $value:""
DecoratedBox
#decoration
#border fromBorderSide
#side
color:{focused ? primaryFocusedColor : primaryUnfocusedColor}
width:{focused && isCurrent ? 3 : 1}
SizedBox width:100 height:120
Center
Text $value
Пример 7: Импорты и макросы
import components/theme components/buttons components/text
Column
$Text.h1 "Добро пожаловать"
SizedBox height:24
$PrimaryButton onPressed:"navigate:login" text:"Войти"
$SecondaryButton onPressed:"navigate:register" text:"Регистрация"
Пример 8: Комплексный сценарий
import components/theme components/text
@status:initial
@error:""
@email:""
State @status @error @email
Container
padding:20
Column spacing:16
$Text.h1 "Авторизация"
Input value:$email placeholder:"Введите email"
Switcher $status
SizedBox id:initial
$Text.button id:error $error
Button onPressed:{status="loading"}
Text "Войти"
Сжатие
REEEL поддерживает опциональный формат сжатия, который уменьшает размер файлов путём замены имён элементов и параметров на короткие алиасы. Сжатые файлы начинаются с заголовка
#packed
.
Пример сжатого формата:
#packed B:Button P:Padding T:Text t:text a:all
B^1P a:10^2T t:"Hello"
Подробнее — см. COMPRESSION.md.
Компоненты
Документация всех UI-компонентов REEEL — параметры, вложенные структуры, примеры использования:
Заключение
Формат REEEL предоставляет простой и выразительный способ описания древовидных структур с поддержкой:
- Иерархии через отступы
- Различных типов данных (строки, числа, boolean, duration)
-
Реактивных переменных
(
@) с автоматической подпиской через$ - Шаблонных строк для динамического формирования текста
-
Вычисляемых выражений
в синтаксисе Dart (
{...}) -
Изменения переменных
через
{key=val;key2=val2} - Мульти-элементов для компактного описания списков
- Chain для плоского описания цепочки вложенных элементов-обёрток
-
Вложенных структур
(
#name,:#) для передачи объектов как параметров - Макросов для переиспользования структур
- Импортов для разбиения на несколько файлов
Формат универсален и может применяться для различных задач, где требуется описание древовидных структур в текстовом виде.
Дополнительные материалы
- Best Practices — практические правила вёрстки в REEEL-формате
- Функции выражений — встроенные функции и callback-аргументы
-
Тема и локализация
— ambient
$theme/$strings, файлыdata/themeиdata/strings - Компоненты — документация UI-компонентов