Любой полезный инструмент начинается с боли. Эта статья — честная история о том, как боль от рутинной SEO-проверки текстов превратилась в полноценное Flutter-приложение. Мы пройдем весь путь: от наивного прототипа и фидбэка коллег до борьбы с зависанием UI и неожиданных открытий о работе буфера обмена.
Глава 1: Прототип v0.1 — Боль, страдания и Ctrl+F
Все началось с простой задачи: проверить, соответствует ли текст копирайтера техническому заданию (ТЗ). Каждый раз это был один и тот же ритуал:
1. Скопировать текст в один сервис для подсчета символов.
2. Вручную (Ctrl+F) искать каждое ключевое слово, считая вхождения.
3. Пытаться понять, все ли темы из структуры ТЗ раскрыты в подзаголовках.
Это было долго, нудно и чревато ошибками. Так родилась идея: создать инструмент, который бы делал все это автоматически.
Глава 2: Первый фидбэк и первые проблемы
Первая версия была собрана «на коленке»: два поля TextField, кнопка «Проверить» и простейшая логика text.contains(keyword). Проблемы обнаружились сразу, как только я показал инструмент коллегам:
• «Он не находит ключи, которые точно есть!» — String.contains() не понимал падежей. «Купить машину» и «купить машина» для него были разными фразами.
• «В ТЗ куча всего, а ты проверяешь только ключи» — парсер ТЗ был слишком примитивен и не умел извлекать структуру, LSI-слова, мета-теги.
• «В объем текста попадает всё подряд» — алгоритм не мог правильно «отрезать» конец секции и забирал в «Объем» половину ТЗ.
Стало ясно, что наивный подход провалился. Нужна была настоящая «математика».
Глава 3: Эволюция — от строк к семантике
Алгоритмы вместо Contains(). Простой поиск был заменен на более надежный, вдохновленный классическими алгоритмами вроде Бойера-Мура... Главным прорывом стала борьба с морфологией. На помощь пришел алгоритм стемминга. Теперь перед анализом и текст, и ключи проходили «очистку», превращаясь в набор основ слов.
Парсер ТЗ 2.0. Парсер был полностью переписан. Теперь алгоритм искал стартовый маркер, начинал собирать строки и останавливался, как только встречал следующий известный маркер или несколько пустых строк подряд.
Глава 4: Фидбэк Лены и рождение v1.0
После всех улучшений инструмент стал гораздо умнее. Настало время для серьезного тест-драйва. Я отдал его на проверку коллеге Лене Мельниковой (@oh_laalaa). Ее фидбэк стал катализатором для превращения прототипа в настоящий рабочий инструмент.
Проблема №1: «Текст — это не просто буквы!» Решение: Интеграция flutter_quill. Этот Rich Text редактор, хранящий документ в формате Delta, стал сердцем новой версии.
Проблема №2: «А где проверка орфографии?» Решение: Интеграция с API Яндекс.Спеллера.
Проблема №3: «Считает не так, как Миратекст!» Решение: Глубокое погружение в математику. Алгоритм был допилен, чтобы учитывать «занятые» диапазоны.
Глава 5: Главный враг — зависание интерфейса (UI Freeze)
При нажатии на кнопку «Запустить анализ» приложение замирало на 5-10 секунд. Причина — блокировка UI-потока.
Решение: Изоляты и compute(). Вся логика анализа была вынесена в отдельную глобальную функцию и запущена через compute(), что освободило основной поток и сделало анимацию плавной.
Но даже после этого лоадер «дергался». Финальным штрихом стало добавление Future.delayed перед запуском тяжелых операций.
Глава 6: Битва за UX и фиаско с RTF
Последней нерешенной задачей оставалась вставка из Google Docs. Отладка показала, что Google Docs не кладет в буфер обмена ничего, кроме простого текста!
Элегантный поворот: функция «Распознать структуру». Так родилась идея кнопки «🪄 Распознать структуру», которая запускает эвристический алгоритм и применяет форматирование по правилам: короткая строка без точки — заголовок, строка с • или * — пункт списка.
Заключение: итерации, компромиссы и фокус на пользователе
Путь создания этого инструмента — классический пример итеративной разработки. От неработающего прототипа и критики коллег, через решение фундаментальных проблем производительности, до столкновения с ограничениями внешнего мира и поиска элегантных обходных путей. Ключевой вывод: идеального решения не существует. Но можно, шаг за шагом, приближаться к нему, постоянно держа в фокусе главную цель — решить «боль» пользователя максимально простым и эффективным способом.