Flutter Web и SEO: Как это сделано на shastovsky.ru

Flutter Web по умолчанию создает Single Page Application (SPA), отрисовывая весь интерфейс на одном HTML-элементе <canvas>. Для поисковых роботов это «черный ящик». Эта статья — подробный разбор того, как именно этот сайт решает проблему SEO с помощью собственного механизма генерации статических сайтов (SSG).

Схема работы пререндеринга для Flutter Web

Проблема: Canvas-рендеринг и поисковые роботы

Когда поисковый робот заходит на стандартный Flutter Web сайт, он видит почти пустой HTML-файл. Весь контент — текст, заголовки, ссылки, картинки — скрыт внутри <canvas>. Робот не может это проанализировать, и с точки зрения SEO страница пуста. Наша задача — отдать ему чистый, структурированный HTML, который он поймет.

Стратегия: «Два режима» — умный рендеринг для всех

В основе решения лежит принцип «двух режимов». Каждая страница сайта умеет рендериться двумя способами:

1. Для пользователя: Запускается полноценное интерактивное Flutter-приложение с красивым UI, анимациями и CanvasKit-рендерингом.

2. Для пререндеринга (и поисковых роботов): Страница отрисовывает только специальный набор невидимых виджетов, которые генерируют семантически верное дерево HTML-элементов (<h1>, <p>, <img>).

Сигналом для переключения в режим пререндеринга служит GET-параметр в URL: ?prerender=true.

Компонент №1: Семантические виджеты

Чтобы Flutter мог генерировать HTML-теги, были созданы специальные виджеты-обертки, такие как SemanticH1, SemanticP, SemanticImg. Их задача — используя Dart-библиотеки для веба, создать реальный HTML-элемент и добавить его в DOM-дерево. Сами виджеты имеют нулевой размер и не видны пользователю.

Посмотреть код SemanticWidget

Компонент №2: Dart-скрипт для пререндеринга

Это сердце всей системы. После каждой сборки (`flutter build web`) запускается скрипт на Dart, который автоматизирует весь процесс:

1. Запускает локальный веб-сервер для раздачи собранного приложения.

2. Запускает headless-браузер (Chrome) с помощью пакета Puppeteer.

3. В цикле обходит все заданные роуты сайта, добавляя к каждому URL параметр ?prerender=true.

4. На каждой странице ждет, пока Flutter сгенерирует и наполнит специальный HTML-контейнер.

5. Извлекает сгенерированный HTML, а также актуальные мета-теги из <head>.

6. Вставляет полученный контент в шаблонный index.html и сохраняет результат в нужную папку (например, /build/web/marocco/index.html).

Посмотреть ключевую часть prerender.dart

Компонент №3: «Двухрежимная» логика на страницах

Каждая страница, которую нужно пререндерить, содержит простую условную логику. В методе initState она проверяет URL. Если параметр ?prerender=true найден, устанавливается внутренний флаг. Метод build затем использует этот флаг, чтобы решить, что рендерить: полноценный Scaffold с Flutter-виджетами или простую Column с семантическими HTML-виджетами.

Посмотреть пример кода страницы

Финальный штрих: Конфигурация Nginx

Последний шаг — научить веб-сервер правильно отдавать сгенерированные файлы. Nginx настраивается так, чтобы любой запрос к странице (например, shastovsky.ru/marocco) он внутренне перенаправлял на соответствующий статический файл (shastovsky.ru/marocco/index.html).

Пример конфигурации Nginx

Результат и выводы

Этот подход, хоть и требует ручной работы по дублированию контента в семантические виджеты, дает полный контроль над HTML, который увидят поисковые роботы. В результате сайт на Flutter Web становится полностью SEO-дружелюбным, сохраняя при этом всю мощь и красоту интерактивного SPA для реальных пользователей.