AI coding для Flutter: workflow с обязательной проверкой

Автор: Обновлено

Что такое AI coding для Flutter?

AI coding для Flutter — использование coding agent для анализа, изменения и проверки Dart/Flutter-репозитория в явных границах проекта. Результат работы — не сгенерированный код сам по себе, а проверенный diff, который учитывает зафиксированные версии packages, generated-file boundaries, архитектуру и требования платформ и проходит analyzer, tests и нужные проверки на устройстве.

TL;DR

  • -Сначала agent должен прочитать pubspec.yaml, pubspec.lock, analysis_options.yaml, соседнюю implementation и tests. Не просите его вспоминать актуальный Flutter или package API.
  • -Зафиксируйте source files и generated outputs. Меняйте annotations, models, ARB resources и другие inputs, затем запускайте существующую команду генерации.
  • -В async callbacks проверяйте BuildContext.mounted после каждого relevant async gap. Для Riverpod используйте lifecycle API только из версии, зафиксированной в проекте.
  • -Компилируемый widget ещё не прошёл проверку. Проверьте local constraints, длинные переводы, text scaling, keyboard insets, orientation и поддерживаемые form factors.
  • -Работайте коротким циклом: format, analyze, targeted tests, затем более широкие tests и реальный flow на device или emulator, если затронута платформа.
  • -Официальный Dart and Flutter MCP server даёт agent доступ к analysis, tests, package search, runtime errors и widget tree, но остаётся experimental и не заменяет review.

AI coding agent способен написать валидный Dart и всё равно сломать приложение.

Обычно проблема не в том, что модель «не знает Flutter». Чаще она использовала API не той версии, изменила generated artifact, пересекла async lifecycle boundary или остановилась после compilation, не проверив UI.

Лечится это не длинным prompt. Нужен короткий инженерный цикл: изучить repository, ограничить change, получить небольшой diff и проверить именно ту границу, которая изменилась.

Так я работаю с Flutter-проектами, включая JourneyBay. Подход применим к Claude Code, Codex, Gemini CLI, Cursor и другим agents.

1. Начинайте с repository, а не с памяти модели

До предложения implementation agent должен прочитать:

pubspec.yaml
pubspec.lock
analysis_options.yaml
README или project instructions
ближайшую похожую implementation
релевантные tests
routing и state-management setup
build.yaml и l10n.yaml, если они есть

pubspec.lock принципиален. «Используй актуальный Riverpod API» — неоднозначная просьба. «Используй API из locked flutter_riverpod и повтори локальный provider pattern» — проверяемая.

То же относится к GoRouter, Freezed, JSON serialization, platform plugins и Flutter SDK. Не разрешайте agent обновлять dependency только для того, чтобы remembered syntax скомпилировался. Dependency change — отдельное решение и отдельный review.

Первый prompt полезно сделать read-only:

Изучи перечисленные файлы и объясни:
1. существующую architecture и state-management pattern;
2. package и SDK constraints для этой задачи;
3. generated files и их source inputs;
4. самые узкие tests и commands для проверки change.

Не редактируй файлы. Не добавляй и не обновляй dependencies.
Отдельно перечисли assumptions, которых нет в repository.

Сначала утвердите план, потом просите код.

2. Дайте agent ограниченный change contract

«Добавь избранное» — не contract. Опишите observable behavior и допустимые слои.

Goal:
Signed-in user может удалить сохранённое место с details screen.

Observable behavior:
- tap по Remove открывает confirmation;
- подтверждение один раз вызывает existing repository;
- success закрывает dialog и обновляет visible state;
- failure оставляет screen открытым и показывает existing error component.

Allowed files:
- place_details_page.dart
- saved_places_controller.dart
- их существующие test files

Do not:
- менять routes и public repository interfaces;
- редактировать generated files;
- добавлять packages;
- трогать Android и iOS config.

Verification:
flutter analyze
flutter test test/features/saved_places/

Такая рамка останавливает scope creep. Agent знает, где можно работать, что нельзя «заодно улучшить» и чем подтверждать результат.

Большую feature лучше делить по observable slices, а не заказывать сразу domain, data, state, UI, localization и platform configuration. Тот же короткий цикл разобран в TDD с coding agents.

3. Считайте generated code результатом сборки

Во Flutter-репозиториях Dart часто генерируется из annotations и resource files:

Freezed model        → *.freezed.dart
json_serializable    → *.g.dart
Riverpod annotation  → *.g.dart
Injectable config    → *.config.dart
ARB resources        → AppLocalizations output

Точная карта принадлежит конкретному repository. Изучите headers generated files, build.yaml, l10n.yaml и scripts, а не считайте все .g.dart одинаковыми.

Dart описывает build_runner как слой команд для builders, включая json_serializable (build_runner). Flutter localization guide указывает ARB как inputs, а AppLocalizations как generated output (internationalization).

Безопасный change проходит четыре шага:

  1. изменить source model, annotation или ARB resource;
  2. проверить source diff;
  3. запустить существующую команду генерации;
  4. отдельно проверить generated changes.

Не нужно навсегда запрещать agent запускать генерацию. Разрешите известную команду после review source diff, при чистом worktree и с заранее известным списком outputs. Остановитесь, если generator переписал посторонние файлы.

Запусти существующую команду code generation.
Не меняй dependencies и generator config.
После завершения перечисли каждый changed generated file.
Остановись, если изменился файл вне expected list.

4. Проверяйте async lifecycle, а не только syntax

Частый Flutter defect появляется после await:

onPressed: () async {
  await controller.save();
  if (!context.mounted) return;
  context.pop();
}

Документация Flutter для BuildContext рекомендует не кэшировать context и проверять mounted, если он используется после async gap (BuildContext).

Проверка необходима, но review на ней не заканчивается:

  • Можно ли нажать кнопку повторно до завершения первого operation?
  • Возможен ли cancellation?
  • Используется ли context нужного router или navigator scope?
  • Что произойдёт при failure?
  • Может ли late response перезаписать более новый state?
  • Сбрасывается ли loading state в finally?

У Riverpod собственный lifecycle, зависящий от версии. Riverpod 3 добавил Ref.mounted, automatic retry и lifecycle changes; документация прямо рекомендует аккуратную migration (изменения Riverpod 3, migration guide). Используйте эти API только при совместимом locked package.

Не просите agent «исправить stale context везде». Дайте один flow, один reproduction и точное expected behavior.

5. Опишите проверяемые UI constraints

Prompt под один screenshot обычно даёт implementation под один screenshot. Flutter layout зависит от constraints, текста, insets и положения в widget tree.

Для нового component перечислите states и boundaries:

States:
loading, loaded, empty, error, offline

Content:
long title, missing image, localized price, optional subtitle

Constraints:
работает в parent width из LayoutBuilder
поддерживает text scaling
учитывает keyboard и safe-area insets
оставляет primary action доступным
использует существующие breakpoints и design tokens

Interactions:
tap, back, retry, double tap, loading lock

Flutter adaptive guidance различает размер app window и local widget constraints: MediaQuery.sizeOf нужен для окна, LayoutBuilder — когда component реагирует на ограничения parent (adaptive layout guidance). «Проверить на телефоне» недостаточно для split-screen, tablet, foldable, desktop и крупного текста.

Попросите agent добавить widget test для состояний component. Затем откройте реальный screen на поддерживаемых widths и platforms. Golden tests ловят visual drift, но не доказывают navigation, keyboard, accessibility и работу native plugin.

6. Выбирайте test по изменённой границе

Flutter выделяет три основных уровня: unit, widget и integration tests (testing overview).

Используйте их по назначению:

  • Unit test: pure mapping, validation, policy и controller logic.
  • Widget test: rendering и interactions во Flutter widget environment.
  • Integration test: полный flow на target device или emulator.
  • Platform build или device check: Gradle, Xcode, permissions, deep links, notifications, camera, WebView и другие native boundaries.

Agent часто создаёт слишком много mocks, потому что так проще написать test. Требуйте пройти через production code path под review и подменять только external boundary. Test скопированной implementation почти ничего не доказывает.

Для native user flows оставьте небольшой E2E suite. В нашем гайде по Maestro для Flutter разобраны permissions и cross-platform flows, а официальный integration_test покрывает app-level Flutter tests на devices и emulators (integration tests).

7. Используйте официальный MCP server как мост к tools

Начиная с Dart 3.9 доступен experimental Dart and Flutter MCP server. Он открывает analyzer diagnostics, symbol information, tests, formatting, pub.dev search, runtime errors, widget-tree inspection и взаимодействие с запущенным app (официальный MCP server).

Базовая команда для поддерживаемых clients:

dart mcp-server

Server помогает заменить «код выглядит правильно» прямым feedback. Но не делает безопасным любое действие. Package management меняет project config; runtime interaction может требовать debug extension; roots и client permissions определяют доступ agent.

Применяйте те же least-privilege rules, что и для production MCP servers. Начните с analysis, tests и read-only runtime inspection. Требуйте approval для dependencies, config changes, массовой code generation и команд за пределами repository.

Flutter также публикует адаптируемые AI rule templates разного размера (official AI rules). Возьмите их за baseline и добавьте только конкретные, проверяемые правила своего проекта:

Source of truth for localization: lib/l10n/*.arb
Generated outputs: lib/generated/**; never edit directly
State pattern: copy the nearest feature; no new framework
Navigation: use named routes from app_router.dart
Dependencies and native config: require approval
Required checks: dart format, flutter analyze, targeted tests

Rules file должен указывать на source files и commands, а не дублировать architecture manual, который быстро устареет.

8. Завершайте задачу доказательствами

Хороший completion report короткий:

Changed:
- saved_places_controller.dart: guarded remove flow
- place_details_page.dart: confirmation и existing error state
- two tests: success и repository failure

Ran:
- dart format <changed Dart files>
- flutter analyze
- flutter test test/features/saved_places/

Not run:
- iOS build; native files не менялись
- full integration suite; flow изолирован

Risks:
- offline retry behavior не менялся и остаётся вне scope

Базовая verification ladder:

dart format <changed-dart-files>
flutter analyze
flutter test <targeted-path>
flutter test

Добавляйте generation, golden updates, integration tests или platform builds только по требованию границы. После команд, способных переписать файлы, всегда проверяйте git diff.

Ускоряет ли AI Flutter-разработку?

Честного универсального процента нет.

В randomized study METR опытные разработчики знакомых open-source repositories выполняли задачи дольше с AI tools начала 2025 года (исследование). Оно не измеряло Flutter-команды как отдельную категорию и не доказывает, что любой coding agent замедляет каждого developer.

Измеряйте свой workflow:

  • accepted diffs без ручного rewrite;
  • defects, найденные до review;
  • время review и rework;
  • escaped regressions;
  • время по классам задач: models, state, UI, native config, tests.

В моей Flutter-работе agents полезнее всего при явном contract и наличии локального pattern для копирования. Меньше всего им стоит доверять, когда visual behavior, asynchronous ownership и native platform config оставлены неявными.

Практическое правило: используйте agent, чтобы сократить edit-feedback loop, но не убирайте feedback.

Часто задаваемые вопросы

Можно ли AI-agent редактировать .g.dart или .freezed.dart?
Не в том случае, если это generated files вашего проекта. Измените source annotation или model и запустите существующий generator. Сначала проверьте headers, build.yaml, l10n.yaml, scripts и правила version control: suffixes и политика committed outputs различаются.
Достаточно ли context.mounted для navigation после await?
Эта проверка отвечает только на вопрос, остаётся ли BuildContext mounted после async gap. Ещё нужно использовать context правильного Navigator или GoRouter scope, учесть повторные taps, cancellation и errors. mounted — lifecycle check, а не navigation design.
Можно ли поручить agent выбор нового Flutter package?
Agent может исследовать варианты, но dependency change требует одобрения человека. До изменения pubspec.yaml проверьте поддержку, license, platforms, transitive dependencies и совместимость с locked SDK.
Какие проверки нужны после AI-generated Flutter change?
Минимум: dart format для изменённых Dart files, flutter analyze и targeted tests. Добавьте code generation, widget или golden tests, integration tests и Android/iOS builds, если этого требует изменённая граница.