惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

K
Kaspersky official blog
Engineering at Meta
Engineering at Meta
D
DataBreaches.Net
Stack Overflow Blog
Stack Overflow Blog
Microsoft Security Blog
Microsoft Security Blog
Y
Y Combinator Blog
B
Blog RSS Feed
GbyAI
GbyAI
P
Proofpoint News Feed
aimingoo的专栏
aimingoo的专栏
MyScale Blog
MyScale Blog
D
Docker
阮一峰的网络日志
阮一峰的网络日志
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Recorded Future
Recorded Future
美团技术团队
The Register - Security
The Register - Security
V
Visual Studio Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
T
Tailwind CSS Blog
爱范儿
爱范儿
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
T
The Blog of Author Tim Ferriss
博客园 - 司徒正美
量子位
B
Blog
F
Fortinet All Blogs
Martin Fowler
Martin Fowler
博客园 - 【当耐特】
MongoDB | Blog
MongoDB | Blog
A
About on SuperTechFans
I
InfoQ
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
有赞技术团队
有赞技术团队
雷峰网
雷峰网
大猫的无限游戏
大猫的无限游戏
J
Java Code Geeks
L
LangChain Blog
Latest news
Latest news
S
SegmentFault 最新的问题
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Blog — PlanetScale
Blog — PlanetScale
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Cisco Talos Blog
Cisco Talos Blog
F
Full Disclosure
C
Cisco Blogs
D
Darknet – Hacking Tools, Hacker News & Cyber Security
W
WeLiveSecurity
T
Tenable Blog
T
Tor Project blog

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Почему animated SVG не работает во Flutter «как в браузере» — и как я попытался это исправить
denisdandy · 2026-05-03 · via Все публикации подряд на Хабре

Уровень сложностиСредний

Время на прочтение9 мин

Охват и читатели396

Кейс

Для контекста: я Flutter-инженер и техлид, последние годы работаю с production-приложениями на Flutter — мобильными, web и гибридными. В моей практике были fintech, маркетплейсы, food delivery и iGaming-продукты, где к UI обычно предъявляют довольно жёсткие требования: сложная графика, анимации, дизайн-системы, производительность, стабильность и предсказуемая доставка фич.

full_flutter_svg (пришлось рисовать через ai из меня плохой художник)

full_flutter_svg (пришлось рисовать через ai из меня плохой художник)

Веб-команда отдаёт SVG. В Chrome он работает. В Safari — тоже.
Во Flutter — внезапно превращается в статичную картинку, частично ломается или требует обходных путей вроде GIF, видео, Lottie или ручной переписи анимации.

Я попробовал решить именно этот класс проблем и в итоге написал пакет full_svg_flutter — SVG-рендерер для Flutter, который старается приблизить поведение SVG к тому, как его ожидаешь увидеть в браузере.

В статье расскажу, почему animated SVG — это совсем не “просто нарисовать path”, какие подсистемы пришлось реализовать, где это реально полезно, а где лучше использовать совсем другие инструменты.

В какой-то момент я столкнулся с задачей, которая звучит просто: “взять SVG, который работает в браузере, и показать его во Flutter”. Но оказалось, что если внутри есть SMIL, CSS-анимации, path morphing, masks, filters или сложные стили, это уже не просто картинка, а почти маленький браузерный пайплайн.

Так появился full_svg_flutter — моя попытка приблизить поведение SVG во Flutter к тому, как мы привыкли видеть его в браузере.

Почему animated SVG не работает во Flutter «как в браузере» — и как я попытался это исправить

Веб-команда отдаёт SVG.
В Chrome он работает.
В Safari — тоже.
Во Flutter — внезапно превращается в статичную картинку, частично ломается или требует обходных путей вроде GIF, видео, Lottie или ручной переписи анимации.

Я попробовал решить именно этот класс проблем и в итоге написал пакет full_svg_flutter — SVG-рендерер для Flutter, который старается приблизить поведение SVG к тому, как его ожидаешь увидеть в браузере.

В статье расскажу:

  • почему animated SVG — это не “просто нарисовать path”;

  • какие подсистемы приходится реализовывать;

  • где такой пакет реально полезен;

  • где он, наоборот, не нужен;

  • и почему в какой-то момент начинаешь понимать, что внутри делаешь маленький браузер.

Пакет

Откуда вообще берётся проблема

иногда мы даже не видим ничего (даже первого кадра)

иногда мы даже не видим ничего (даже первого кадра)

Если говорить про обычные статичные SVG, экосистема Flutter давно уже закрывает много задач достаточно хорошо.

Но как только речь заходит о реальных SVG из production, быстро выясняется, что «поддержка SVG» и «поддержка SVG примерно как в браузере» — это совсем разные уровни.

Типичный сценарий выглядит так:

  1. дизайнер или веб-команда отдаёт SVG;

  2. этот SVG уже работает на web;

  3. внутри него могут быть:

    • SMIL-анимации,

    • CSS keyframes,

    • path morphing,

    • masks,

    • clipping,

    • filters,

    • text / textPath,

    • nested transforms,

    • defs, use, ссылки по id,

    • стили, заданные и атрибутами, и через CSS;

  4. потом этот же файл хотят использовать во Flutter.

И вот здесь начинается самое интересное.

Потому что файл, который кажется “обычным SVG”, на деле может содержать довольно сложное поведение, которое в браузере давно воспринимается как само собой разумеющееся.

Что я хотел получить в итоге

Снаружи задача для разработчика должна выглядеть максимально просто:

FSvgPicture.asset('assets/animated.svg')

Идея в том, что человеку не должно быть нужно думать о том, как:

  • распарсить XML;

  • построить DOM;

  • разрешить стили;

  • вычислить transform tree;

  • отследить timeline анимаций;

  • интерполировать path data;

  • применить filters;

  • понять, что можно кешировать, а что надо пересчитывать каждый frame.

В идеале — передал SVG и получил поведение, максимально близкое к ожидаемому.

Сразу важная оговорка: это не «замена вообще всему»

Я специально хочу проговорить это в начале, потому что иначе статья легко скатывается в маркетинг.

full_svg_flutter — это не универсальная серебряная пуля.

Если коротко:

  • flutter_svg — отлично подходит для большого количества статичных SVG-сценариев;

  • Lottie — отлично подходит, если source of truth у команды именно Lottie;

  • Rive — отлично подходит, если анимация сделана в Rive;

  • PNG/WebP — иногда вообще лучший ответ, если задача чисто декоративная и интерактивность не нужна.

Мой use case другой:

SVG уже существует как исходный артефакт. Он работает в браузере. И хочется, чтобы именно этот SVG работал во Flutter без конвертации в GIF, видео или другой формат.

Почему animated SVG — это сильно сложнее, чем кажется

На первый взгляд кажется: ну что там, SVG - это же просто вектор.

Но как только начинаешь смотреть на реальные файлы, становится ясно, что SVG — это не просто «нарисовать набор path‑ов».

Это сразу несколько слоёв:

  • документная структура;

  • стили и наследование;

  • ссылки между элементами;

  • система координат;

  • таймлайн анимаций;

  • трансформации;

  • фильтры;

  • текст;

  • порядок отрисовки;

  • частичное обновление дерева;

  • кеширование.

То есть по сути ты имеешь дело не просто с картинкой, а с чем-то между:

  • DOM,

  • style engine,

  • animation engine,

  • render tree.

И как только начинаются реальные SVG-анимации, это становится очень заметно.

Какие фичи реальных SVG быстрее всего ломают «простое решение»

Вот список того, что чаще всего делает задачу нетривиальной:

  • <animate>

  • <animateTransform>

  • <animateMotion>

  • CSS @keyframes

  • path morphing

  • animated opacity, fill, stroke, transform

  • masks / clipPath

  • filters

  • gradients

  • defs / use

  • nested groups

  • text, tspan, textPath

  • presentation attributes + inline style + CSS inside <style>

  • inheritance / cascade

Проблема в том, что все эти вещи не живут изолированно.

Например:

  • на группу может быть навешан transform;

  • внутри неё — mask;

  • внутри mask — ещё один shape;

  • у shape цвет задан через CSS-класс;

  • сам класс анимируется через keyframes;

  • а поверх ещё применён filter.

То есть “поддержать одну фичу” часто означает, что надо уже иметь пол-нормальной внутренней модели документа.

Что пришлось реализовать внутри

1. SVG DOM

Первое, без чего всё быстро разваливается, — это внутренний DOM.

Нужно не просто распарсить XML в набор объектов, а иметь структуру, где каждый элемент знает:

  • свои атрибуты;

  • детей;

  • родителя;

  • id, class;

  • computed style;

  • transform;

  • paint properties;

  • visibility / display;

  • связи с defs, use, clipPath, mask, gradients и filters.

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

2. Style resolution и каскад

Одна из самых коварных частей SVG — стили могут приходить отовсюду.

Например, один и тот же fill может быть задан так:

<path fill="red" />

или так:

<path style="fill:red;" />

или так:

<style>
  .logo-shape {
    fill: red;
  }
</style>
<path class="logo-shape" />

И всё это ещё может наследоваться.

Если не построить слой computed styles, очень быстро начинаются странные рассинхроны: в одном SVG что-то красится не тем цветом, в другом ломается stroke, в третьем не применяются классы.

3. Таймлайн анимаций

Как только появляется SMIL или CSS-анимация, нужна система времени.

То есть недостаточно “на старте прочитать параметры анимации”.

Нужно уметь:

  • определять текущий момент времени;

  • вычислять активность анимации;

  • поддерживать begin, dur, repeatCount, repeatDur, fill;

  • интерполировать значения;

  • учитывать easing / splines;

  • применять результат к целевому атрибуту;

  • корректно инвалидировать render state.

И всё это должно быть синхронизировано с frame lifecycle Flutter.

4. Path morphing

Path morphing — один из самых интересных и болезненных случаев.

Если очень упростить, то мало просто взять две строки с d="" и попробовать “смешать” числа.

Чтобы morphing был корректным, нужно:

  • распарсить path data;

  • нормализовать команды;

  • привести относительные и абсолютные команды к совместимому виду;

  • убедиться, что структуры path-ов сопоставимы;

  • интерполировать параметры команд;

  • корректно собирать результат обратно.

На демо-файлах это можно “почти сделать” быстро.
На реальных SVG — начинается веселье.

5. Filters

SVG-фильтры — это отдельный мир.

Даже если брать относительно базовые вещи вроде blur, color matrix, blend/composite или drop shadow, быстро выясняется, что вопрос не только в поддержке операций, но и в том:

  • как считать bounds;

  • как не рисовать слишком много;

  • как кешировать промежуточный результат;

  • как пересчитывать только то, что действительно изменилось;

  • как не убить производительность на анимациях.

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

6. Performance и кеширование

Со статикой всё относительно приятно: можно много кешировать.

С анимированными SVG всё сложнее:

  • часть дерева может быть статичной;

  • часть — меняться каждый frame;

  • часть зависит от стилей;

  • часть — от filter chain;

  • часть — от transform state;

  • часть — от timeline.

Из-за этого пришлось думать в нескольких слоях:

  • parse cache;

  • DOM cache;

  • computed style cache;

  • picture cache;

  • raster cache;

  • частичная invalidation.

И очень быстро пришёл к выводу, что для такого пакета бессмысленно смотреть только на “средний FPS”.

Гораздо важнее:

  • cold parse time;

  • warm render time;

  • average frame time;

  • p90 / p99;

  • worst frame;

  • jank frames;

  • memory delta.

Потому что пользователь замечает не “среднюю температуру по больнице”, а лаги и микрофризы.

Архитектура в двух словах

Если очень грубо, внутренний пайплайн выглядит так:

  1. SVG XML

  2. Parser

  3. SVG DOM

  4. Style resolver / computed styles

  5. Animation engine / timeline

  6. Render tree

  7. Painter / canvas layer

  8. Cache layer

  9. Flutter widget API

Это, конечно, упрощение, но именно такая ментальная модель помогает объяснить, почему задача внезапно оказывается намного шире, чем просто “отрисовать вектор”.

Как это выглядит снаружи

Снаружи хотелось сделать API максимально узнаваемым для Flutter-разработчика.

Например:

FSvgPicture.asset('assets/animated.svg')

Идея в том, чтобы migration path был максимально простым и не требовал “учить новый мир” просто ради того, чтобы получить анимированный SVG.

Где такой пакет реально полезен

Я вижу несколько основных сценариев.

1. Source of truth — именно SVG

Это главный случай.

То есть команда не хочет экспортировать SVG в другой формат, а хочет использовать один и тот же артефакт между web и mobile.

2. Animated SVG из дизайна или веба

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

3. SVG-heavy UI

Например:

  • промо-экраны;

  • dashboard-like интерфейсы;

  • игровые / iGaming-подобные интерфейсы;

  • интерактивные иллюстрации;

  • сложные векторные элементы.

4. Edge cases, которые плохо укладываются в «просто иконки»

Masks, filters, clipping, text, nested transforms, animation combinations — всё это как раз тот класс задач, ради которого вообще имеет смысл заморачиваться отдельным renderer.

Где пакет, наоборот, не нужен

Важно честно проговорить и это.

Я бы не тянул full_svg_flutter, если:

  • у вас обычные статичные иконки и всё уже работает;

  • анимация изначально создаётся под Lottie;

  • анимация делается в Rive;

  • графику можно без потерь заменить на PNG/WebP;

  • UI-эффект проще, надёжнее и дешевле написать нативно во Flutter;

  • важнее максимальная простота и минимальные накладные расходы, чем совместимость SVG.

Иначе можно начать использовать слишком тяжёлый инструмент там, где задача реально решается проще.

Что оказалось самым неожиданным

Наверное, самое интересное ощущение было таким:

чем дальше продвигаешься, тем меньше это похоже на “библиотеку для SVG” и тем больше — на набор маленьких подсистем, которые в браузере воспринимаются как данность.

Например:

  • пока не реализуешь styles — часть SVG выглядит странно;

  • пока не сделаешь transform composition — начинают плыть группы;

  • пока не тронешь filters — кажется, что уже почти всё готово;

  • как только приходят реальные production SVG, вскрываются все слабые места.

И это, пожалуй, главное, что мне дала работа над пакетом: гораздо больше уважения к тому, насколько много логики браузеры прячут под “ну это просто SVG”.


Что сейчас поддерживается

На текущем этапе я сфокусировался на следующем наборе возможностей:

  • static SVG rendering;

  • SMIL animations;

  • CSS animations / keyframes;

  • animated transforms;

  • path morphing;

  • gradients;

  • masks and clipping;

  • filters;

  • text rendering;

  • hit testing;

  • accessibility support;

  • familiar SvgPicture-style API.

Пакет:
https://pub.dev/packages/full_svg_flutter


Что хочу сделать дальше

Сейчас мне особенно интересны две вещи.

1. Реальные edge cases

Я хочу прогонять пакет не только на красивых demo-assets, но и на реальных SVG, которые:

  • работают в Chrome/Safari;

  • становятся статичными во Flutter;

  • используют SMIL или CSS animation;

  • используют mask, clipPath, filter;

  • содержат textPath;

  • ломаются на стыке нескольких фич.

Если у вас есть такие примеры — буду очень рад, если вы скинете их в issues или в комментарии или на почту denis.nadey@gmail.com или telegram @denisdandy


2. Публичный benchmark suite

Я хочу отдельно собрать воспроизводимый benchmark-набор, чтобы смотреть не на абстрактное “быстро/медленно”, а на нормальные метрики:

  • cold parse time;

  • warm render time;

  • dense list/grid scenarios;

  • scroll stress tests;

  • animation frame stability;

  • p90 / p99 frame timing;

  • jank frames;

  • memory delta.

Именно такие цифры дают нормальное представление о том, насколько жизнеспособен renderer на практике.


Итоги

Если коротко, то главный вывод для меня такой:

animated SVG — это не просто картинка, а целый набор подсистем, которые в браузере уже давно живут вместе: DOM, styles, timeline, transforms, filters, render tree.

Именно поэтому “поддержка SVG” и “поддержка SVG примерно как в браузере” — это задачи совершенно разной сложности.

full_svg_flutter — моя попытка приблизить второе во Flutter.

Если у вас есть сложные SVG, которые работают в браузере, но ломаются или упрощаются во Flutter — присылайте.
Такие кейсы для меня сейчас ценнее любой синтетики.

Ссылки:

Возможные темы следующей статьи:

  • как устроен SVG animation timeline;

  • path morphing: почему нельзя просто интерполировать строку d;

  • как работает SVG CSS cascade;

  • как мерить frame stability во Flutter;

  • как тестировать SVG renderer на production-файлах.