Ask the assistant
0/400
Your question is processed by an AI service and kept to improve answers. Do not enter personal data.

    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, ...)

    Параметры могут быть записаны:

    1. На той же строке, что и элемент :
    Element value0 key1:value1 key2:10 "Long string!"
    1. На следующих строках (именованные — с двоеточием в формате 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 , :# ) для передачи объектов как параметров
    • Макросов для переиспользования структур
    • Импортов для разбиения на несколько файлов

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

    Дополнительные материалы

    DOCS
    dark_mode_baseline
    RU/EN
    menu_baseline