*
Render-примитивы (оптимизированный слой)
Семейство элементов с именем на
*
— это
высокооптимизированные render-компоненты
: в отличие от обычных элементов (каждый = отдельный Flutter-виджет/RenderObject), все
*
-элементы одной цепочки
схлопываются в ОДИН Flutter RenderObject
(
ReeelSingleChildRenderBox
) и рисуются напрямую в его
paint
через
Canvas
. Это даёт минимум оверхеда — идеально для
дашбордов и графиков
, где важна скорость перерисовки.
Когда использовать:
карточки/тайлы дашбордов, фоны с градиентом, векторная графика и графики. Для обычной раскладки экранов используйте виджет-элементы (
Column
/
Row
/
Grid
/…).
Как рисовать графику. Общий способ нарисовать что угодно — низкоуровневые примитивы рисования (*Line/*Rect/*Circle/*Arc/*Polygon/*Points/*Path) в компоновщике*Stack, с эффектами (*Clip/*Transform/*Opacity) и итератором*Repeat. Готовые чарты ($Bars/$Sparkline/$Donut/$Ring) — это макросы над этими примитивами (см. «Примитивы рисования» и «Чарты как макросы» ).
Модель:
линейная цепочка
*
-элементов схлопывается в один
ReeelSingleChildRenderBox
; как только дерево
ветвится
(
*Stack
/
*Repeat
) или
оборачивает
поддерево эффектом (
*Clip
/
*Transform
/
*Opacity
), оно рендерится в
ReeelMultiChildRenderBox
(рекурсивный обход, тот же один render-pass). Каждый слой либо задаёт отступ/размер (
layout
), либо рисует (
paint
) в общий бокс. Размер берётся из самого «внутреннего» элемента с размером (
*Text
, примитивы) — остальные (
*Colored
,
*Gradient
,
*Padding
) оборачивают его. Примитивы
адаптивны
: рисуют в выданный размер; можно закрепить
width
/
height
.
Базовые компоненты
| Элемент | Тип | Описание |
|---|---|---|
*Colored
|
single-child |
Заливка сплошным цветом (+ скругление
radius
).
|
*Gradient
|
single-child |
Двухстоповый линейный градиент (
from
/
to
/
angle
/
radius
).
|
*Padding
|
single-child | Отступы вокруг вложенного содержимого. |
*Text
|
leaf |
Текст напрямую через
TextPainter
.
|
*Captions
|
leaf |
Субтитры из записей по часам фильма: окна по нормам, появление и уход в полях, прокрутка, заливка по словам; стиль —
#style
(TextStyle), блок
:# speaker:"A"
на спикера. Только внутри
*Scene
.
|
*Sprite
|
leaf |
Картинка проекта в координатах сцены (
x
/
y
/
w
/
h
, как у
*Rect
): неподвижная, лист спрайтов (
grid:[столбцы, ряды]
+
fps
) или список кадров
frames:
; кадр — по часам слоя,
flipX
/
flipY
,
filterQuality:none
для пиксель-арта.
|
*Stack
|
multi-child | Компоновщик: накладывает детей в один бокс (снизу вверх). |
*State
|
single-child |
Скоуп реактивных
@
-переменных для вложенных
*
-элементов.
|
*Gradient
*Gradient from:0xFF6D5EF6 to:0xFF9B8AFB angle:120 radius:16
*Padding all:20
*Text "Balance" color:0xFFFFFFFF
| Параметр | Тип | Default | Описание |
|---|---|---|---|
from
|
color | прозрачный | Начальный цвет. |
to
|
color | прозрачный | Конечный цвет. |
angle
|
double | 0 | Угол в градусах: 0 = слева→направо, 90 = сверху→вниз. |
radius
|
double | 0 | Скругление углов. |
Чарты (
$Bars
/
$Sparkline
/
$Donut
/
$Ring
) — это
макросы
над примитивами рисования и
*Repeat
, см. разделы ниже.
Примитивы рисования
Низкоуровневые
*
-примитивы рисуют геометрию прямо в
Canvas
. Из них (плюс
*Repeat
) собирается
любой
график — чарты
$Bars
/
$Donut
/… — это макросы над той же механикой.
Координатное пространство.
По умолчанию координаты —
доли
0..1
от бокса (независимо от размера):
cx:0.5 cy:0.5
— центр,
r:0.42
— 42% меньшей стороны. Переключается:
-
units:px— абсолютные пиксели; -
viewBox:[w,h]— виртуальная SVG-сеткаw×h, растягиваемая на бокс.
Радиус (
r
у
*Circle
/
*Arc
) — доля меньшей стороны;
w
/
h
масштабируются по ширине/высоте.
Общие атрибуты заливки
Принимают все примитивы (
*Line
/
*Rect
/
*Circle
/
*Arc
/
*Polygon
/
*Points
/
*Path
):
| Атрибут | Описание |
|---|---|
fill
|
Цвет заливки. |
color
|
Дружелюбный алиас: у заливаемых фигур (
*Rect
/
*Circle
/
*Polygon
/
*Arc
-сектор) — заливка; у линейных (
*Line
/
*Points
/
*Path
/
*Arc
-кольцо) — обводка.
|
stroke
|
Цвет обводки (всегда). |
strokeWidth
|
Толщина обводки;
>0
добавляет проход обводки.
|
cap
|
butt
|
round
|
square
.
|
join
|
miter
|
round
|
bevel
.
|
gradientFrom
/
gradientTo
/
gradientAngle
|
Линейный градиент заливки (угол в градусах). |
draw
|
Доля обводки, которая нарисована (
0..1
от начала контура; по умолчанию
1
— вся). Заливки не касается.
|
blur
|
Мягкий край / свечение (
MaskFilter.blur
).
|
blendMode
|
Любое имя Dart
BlendMode
.
|
units
/
viewBox
|
Координатное пространство (см. выше). |
width
/
height
|
Закрепить размер бокса примитива (иначе — заполняет бокс). |
Фигура рисует
заливку
, если задан
fill
/градиент, и
обводку
, если
strokeWidth>0
(или задан
stroke
). Могут применяться оба прохода сразу.
*Line
/
*Rect
/
*Circle
/
*Arc
/
*Polygon
/
*Points
/
*Path
*Line x1:0.1 y1:0.85 x2:0.9 y2:0.2 color:0xFFF9C74F strokeWidth:4 cap:round
*Rect x:0.15 y:0.15 w:0.7 h:0.7 radius:10 fill:0xFF6C5CE7
*Circle cx:0.5 cy:0.5 r:0.38 fill:0x2200B894 color:0xFF00B894 strokeWidth:3
*Arc cx:0.5 cy:0.5 r:0.4 startAngle:-90 value:0.68 strokeWidth:10 cap:round color:0xFF6C5CE7
*Polygon points:[[0.5,0.12],[0.9,0.88],[0.1,0.88]] fill:0xFFE17055
*Points values:[30,42,38,55,48,63,58,70,60] mode:line color:0xFF22C55E strokeWidth:2 cap:round join:round
*Path d:"M0.05,0.9 L0.3,0.4 L0.5,0.65 L0.72,0.2 L0.95,0.5" color:0xFF9B8AFB strokeWidth:3
| Примитив | Ключевые параметры |
|---|---|
*Line
|
x1
/
y1
(0,0) →
x2
/
y2
(1,1). Только обводка.
|
*Rect
|
x
/
y
(0,0),
w
/
h
(1,1),
radius
(0). Заливка и/или обводка.
|
*Circle
|
cx
/
cy
(0.5),
r
(0.5 — доля меньшей стороны).
|
*Arc
|
cx
/
cy
,
r
,
startAngle
(-90 = 12 часов),
value
(0..1 — сектор
value·360°
)
или
sweep
(градусы),
useCenter
.
strokeWidth>0
— кольцо, иначе залитый сектор.
|
*Polygon
|
points:[[x,y],…]
,
closed
(true).
|
*Points
|
values:[…]
(x равномерно, y нормируется по min..max)
или
points:[[x,y],…]
;
mode
=
line
|
area
|
scatter
;
dotRadius
.
|
*Path
|
d
— SVG-путь
M/L/H/V/C/Q/A/Z
(заглавные — абсолютные доли, строчные — относительные);
fillRule
=
nonZero
|
evenOdd
.
|
Линия, которая рисует себя сама (
draw
)
draw:
— доля
обводки
, а не коробки: она меряется по длине контура, поэтому на изломанной линии половина приходится на середину пути, а не на середину ширины. Заливка при этом остаётся целой — ровно как
stroke-dashoffset
в CSS не трогает
fill
.
InView once:true seen:@seen
Animated of:{seen ? 1 : 0} duration:900 curve:easeOut value:@t
*Path d:"M0.02,0.7 C0.25,0.95 0.75,0.15 0.98,0.45" color:0xFF6C5CE7 strokeWidth:3 draw:{t}
Две вещи, которые стоит знать:
- На сайте долю отмеряет браузер после раскладки (длина обводки у растянутой системы координат известна только по коробке). Со снятым JS страница рисует обводку целиком — тот же безопасный откат, что и у установки, не знающей слова.
-
У замкнутой фигуры
обводка начинается там же, где начинается её контур: у круга это три часа, по часовой стрелке. Сдвигается
*Transform rotate:.
*Stack
/ эффекты
Компоновщики оборачивают одно
*
-поддерево операцией
Canvas
:
*Stack // рисует детей снизу вверх в одном боксе (z-слои)
*Colored color:0xFF12101F radius:12
*Circle cx:0.5 cy:0.5 r:0.4 fill:0xFF6C5CE7
*Clip shape:circle // rect | rrect (radius:) | circle | path (d:)
*Gradient from:0xFF6D5EF6 to:0xFFF9C74F angle:120
*Transform rotate:20 origin:[0.5,0.5] // rotate / scale / translate вокруг origin
*Rect x:0.2 y:0.3 w:0.6 h:0.4 radius:6 fill:0xFF4B9BFF
*Opacity value:0.4 // групповая прозрачность (0 — не рисует, 1 — без слоя)
*Circle cx:0.5 cy:0.5 r:0.42 fill:0xFFE84393
*Transform
прозрачен для раскладки (ребёнок сохраняет бокс) — оборачивай в
*Clip
, чтобы обрезать выступающее.
*Repeat
Штампует одного ребёнка-шаблон N раз, подставляя в скоуп каждой копии
{...}
-биндинги текущей итерации. Управляется
values:[…]
(числа или составные map'ы) либо
count:N
.
| Биндинг | Значение |
|---|---|
index
|
Индекс итерации (с 0). |
item
|
values[index]
(или
index
, если задан только
count
).
|
count
|
Число итераций. |
first
/
last
|
Булевы флаги. |
prev
/
next
|
Соседние значения. |
min
/
max
/
sum
/
mean
|
Агрегаты по числовым значениям. |
norm
|
item
, нормированный в
0..1
по
min..max
.
|
frac
|
item / sum
.
|
accBefore
/
accAfter
|
Префиксные суммы (для стекинга и дуг). |
accFrac
|
accBefore / sum
— префиксная доля
0..1
(стартовый угол доната:
{accFrac*360 - 90}
).
|
// бар-чарт: по одному *Rect на значение
*Repeat values:[12, 28, 20, 44, 32, 38, 26]
*Rect x:{index/count + 0.08/count} w:{0.84/count} y:{1 - item/max} h:{item/max} radius:3 fill:0xFF6D5EF6
Грабли. Ведущий унарный минус в{...}роняет парсер — пиши{x - 90}, а не{-90 + x}.
Приоритет. В{...}цепочкаa/b*cсчитается какa/(b*c)— для(a/b)*cподставляется готовыйaccFrac, либо ставь скобки:{(a/b)*c}. Для высоты столбца используй{item/max}, а не{norm}(normотправляет минимальное значение в 0, и его столбец исчезает).
Чарты как макросы
Поскольку весь чарт — это один
*Repeat
+ один примитив с
{...}
-математикой, он аккуратно сворачивается в
макрос
. Библиотека
assets/editor/reeel/charts.reeel
поставляет четыре готовых (
import charts
):
import charts
$Ring value:0.72 color:0xFF6C5CE7 strokeWidth:12
$Bars values:[12, 28, 20, 44, 32, 38, 26] color:0xFF6D5EF6
$Donut values:[35, 25, 22, 18] strokeWidth:22
$Sparkline values:[30, 42, 38, 55, 48, 63, 58, 70, 60] color:0xFF22C55E fill:0x2222C55E
Рецепты (тело макросов):
| Макрос | Собран из |
|---|---|
$Ring $value
|
*Stack
из двух
*Arc
— полный трек (
value:1
) + дуга прогресса (
value:$value
, скруглённый конец).
|
$Bars $values
|
*Repeat
по
*Rect
, высота
{item/max}
, ширина
{0.84/count}
.
|
$Donut $values
|
*Repeat
по
*Arc
:
startAngle:{accFrac*360 - 90}
,
sweep:{frac*360 - 4}
, цвета
{pick(index, …)}
.
|
$Sparkline $values
|
*Stack
из двух
*Points
— заливка (
mode:area
) + линия (
mode:line
).
|
Передай реактивный список — и чарт станет живым:
$Bars {revenue} color:0xFF9B8AFB
, где
@revenue
объявлена в
*State
или приходит от генератора данных.
Паттерн: карточка дашборда
*Gradient
(фон) может обёртывать Flutter-содержимое (
Padding
/
Column
/
Text
) как свой child; график кладём
*
-блоком (макрос
$Bars
из
charts
или примитивы напрямую).
import charts
*Gradient from:0xFF1E1B34 to:0xFF2A2550 angle:120 radius:20
Padding all:20
Column crossAxisAlignment:start spacing:12
Text "Доход за неделю"
#style color:0xFFEDEBFF fontSize:16 fontWeight:w600
SizedBox height:96
$Bars values:[12, 28, 20, 44, 32, 38, 26] color:0xFF9B8AFB
Реактивные значения работают внутри
*
-детей: объявите переменную в
*State
(или получите её от генератора —
JsonData
/
HttpData
) и привяжите выражением
{...}
к параметру примитива — списком (
values:{revenue}
) или скаляром (
value:{progress}
,
height:{t*120}
). При изменении переменной
*
-слой перерисовывается.
*State @revenue:[12, 28, 20, 44, 32, 38, 26]
*Repeat values:{revenue}
*Rect x:{index/count} w:{0.8/count} y:{1 - item/max} h:{item/max} radius:4 fill:0xFF9B8AFB
Анимация:
*Animated
/
Animated
Драйвер анимации, управляемый
внешней переменной
. Гонит выходную
@
-переменную из
from
в
to
(по умолчанию 0→1), а дети читают её выражением
{...}
. Два имени — один движок; поведение зависит от ребёнка:
-
*Animated— когда ребёнок — это*-render-поддерево: значение идёт через render-конвейер, элементы перерисовываются (repaint), виджеты НЕ пересобираются. -
Animated— когда анимируешь Flutter-виджеты (они пересобираются).
@mode:"fromStartToEnd"
*Animated value:@t controller:$mode duration:900 curve:easeOutCubic
*Arc value:{t} r:0.45 color:0xFF6C5CE7 strokeWidth:12 cap:round width:120 height:120
| Параметр | Тип | Default | Описание |
|---|---|---|---|
value
|
@var | — |
Выходная переменная; сюда пишется текущее eased-значение (
{value}
у детей).
|
controller
|
$var
(dynamic)
|
— | Управление: число = значение, к которому анимировать; строка = команда. |
from
/
to
|
double | 0 / 1 | Диапазон выхода. |
duration
|
int (мс) | 600 | Длительность. |
curve
|
string | easeOut | linear/easeIn/Out/InOut/easeOutCubic/easeOutBack/elasticOut/bounceOut/decelerate. |
Команды
controller
(строкой):
forward
(к
to
),
reverse
(к
from
),
stop
,
reset
(в начало),
fromStartToEnd
,
fromEndToStart
. Число —
animateTo
к этому значению в диапазоне
from..to
. Меняй значение переменной-контроллера (кнопкой, логикой) — анимация реагирует.
Дальнейшее развитие слоя (стек/z, удобная раскладка*Row/*Column/*Grid, обработка нажатий,*Arc/*Candles) ведётся инкрементально — примеры каждого нового примитива проверяютсяreeel_validateи доступны агенту черезreeel_schema.