Flutter против React Native: промты, которые заставляют LLM писать навигацию, состояние и тесты без хардкода — разбор на реальном кейсе

Когда команда впервые просит LLM «сгенерируй экран на Flutter», в ответ приходит виджет с захардкоженным списком внутри build и навигацией через Navigator.push прямо в кнопке. То же самое происходит с React Native: экран, где useState живёт в каждом компоненте, а fetch вызывается в useEffect без отмены. Модель не виновата — виноват промт, в котором не сказано, где хранится состояние, как устроен роутинг и что считать тестом.

Ниже — подборка промтов, которые я использую в реальной работе над двумя кодовыми базами: Flutter (Dart, Riverpod, go_router) и React Native (TypeScript, Zustand, React Navigation). Критерий выбора простой. Если у команды сильная JS/TS-база и нужна быстрая интеграция с веб-кодом — берите блок для React Native. Если важны единый рендеринг, предсказуемый UI и производительность на слабых устройствах — блок для Flutter.

Ключевое различие Flutter-промт должен фиксировать дерево виджетов и слой провайдеров, а React Native-промт — границы хуков и мемоизации. Один и тот же запрос «сделай экран корзины» даёт разный результат, если не указать стек.

Навигация: где LLM чаще всего хардкодит

Проблема навигации в том, что модель по умолчанию использует самый простой API: Navigator.push в Flutter и navigation.navigate в React Native. Это работает в демо и ломается в проде, когда нужны deep links, восстановление стека и защита от повторного перехода.

Промт для Flutter (go_router)

Ты — Flutter-разработчик. Спроектируй навигацию через go_router для приложения с экранами: каталог, карточка товара, корзина, профиль.
Требования:
1. Маршруты — константы в отдельном файле, без строк в коде виджетов.
2. Вложенные маршруты: каталог -> карточка товара через path 'product/:id'.
3. Deep link /product/42 должен открывать карточку и позволять вернуться в каталог.
4. Покажи redirect для неавторизованного пользователя на /login с сохранением исходного пути.
5. Никаких Navigator.push внутри виджетов. Только context.go / context.push.
Выведи: файл маршрутов, пример GoRouter, пример перехода из виджета, объясни, что произойдёт при повторном переходе на /product/42.

В ответе важно проверить пункт про повторный переход: go_router по умолчанию добавляет страницу в стек, и если пользователь дважды тапнет по карточке, получит две копии. В промте это можно закрыть явно — попросить context.pushReplacement или проверку текущего location.

Промт для React Native (React Navigation)

Ты — React Native разработчик на TypeScript. Спроектируй навигацию на React Navigation (native stack) для тех же экранов: каталог, карточка товара, корзина, профиль.
Требования:
1. Типизируй RootStackParamList, экраны и хуки useNavigation/useRoute без any.
2. Deep link: схема app://product/:id, конфиг linking с getStateFromPath.
3. Условный старт: если токена нет — стартовый экран Login, после логина — возврат на сохранённый маршрут.
4. Не вызывай navigation.navigate в useEffect без условия — объясни, как избежать двойного перехода.
Выведи: типы, NavigationContainer с linking, пример экрана каталога, обработку возврата после логина.

Здесь ловушка — useEffect с навигацией: модель любит писать useEffect(() => navigation.navigate('Cart'), []), и при быстром ремаунте экран прыгает дважды. Просите явно показать guard.

Что проверить первым В обоих стеках прогоните сценарий «пользователь тапнул по одной и той же карточке дважды за 300 мс». Если стек вырос на две страницы — промт не закрыл guard, и это вылезет в проде на медленных устройствах.

State management: где LLM плодит дубли

Вторая типичная ошибка — состояние размазано по компонентам. В Flutter модель кладёт setState в StatefulWidget, в React Native — useState в каждый экран, и корзина на двух экранах живёт двумя копиями.

Промт для Flutter (Riverpod)

Ты — Flutter разработчик, используешь Riverpod 2.x без codegen.
Задача: состояние корзины, доступное каталогу, карточке и бейджу в AppBar.
Требования:
1. StateNotifier или Notifier для корзины: add, remove, clear, total.
2. Провайдеры: cartProvider, cartCountProvider (производный), checkoutProvider (FutureProvider).
3. Покажи, как экран читает состояние через ref.watch и меняет через ref.read(...).notifier.
4. Объясни, почему нельзя вызывать ref.read в build.
5. Тест: unit-тест Notifier без виджетов.
Выведи: класс Notifier, провайдеры, пример виджета бейджа, тест.

Проверяйте пункт 4 в ответе: если модель написала ref.read в build — это красный флаг, при пересборке виджета состояние не обновится, и бейдж застынет на старом числе.

Промт для React Native (Zustand)

Ты — React Native разработчик. Состояние корзины на Zustand + TypeScript.
Требования:
1. Store: items, addItem, removeItem, clear, селектор total и count.
2. Селекторы — отдельными функциями, чтобы компонент не ререндерился при изменении чужого поля.
3. Покажи компонент бейджа с useCartStore(state => state.count) и объясни, почему нельзя возвращать весь store.
4. Персистентность: persist middleware с AsyncStorage, partialize только items.
5. Тест стора без React: вызовы экшенов и проверка селекторов.
Выведи: store, селекторы, бейдж, тест.

Самая частая ошибка здесь — useCartStore() без селектора. Модель так пишет по умолчанию, и любой чих в сторе ререндерит весь экран.

Стек Инструмент Что фиксируем в промте
Flutter Riverpod ref.watch в build, ref.read в колбэках
Flutter Bloc события и состояния отдельными классами
React Native Zustand селекторы, persist, отсутствие ререндеров
React Native Redux Toolkit слайсы, типизация PayloadAction

API и анимации: где промт экономит день

Сетевой слой — третье место, где LLM халтурит: fetch без отмены, без таймаута и без обработки ошибок. Промт должен требовать явный контракт.

Ты — мобильный разработчик. Слой API для каталога товаров.
Flutter-вариант: dio + retrofit-подобный клиент, отмена запроса при dispose экрана, таймаут 10 с, маппинг ошибок в sealed-класс Result<T>.
React Native-вариант: fetch с AbortController, отмена в cleanup useEffect, таймаут через AbortSignal.timeout, тот же Result<T> на TypeScript.
Требования к обоим:
1. Тип ответа Product { id, title, price }.
2. Ошибки: сеть, таймаут, 401, 5xx — разные ветки.
3. Покажи экран со списком, где отмена реально срабатывает при быстром переходе назад.
4. Объясни, что произойдёт, если ответ придёт после dispose.

Пункт 4 — неочевидный, но важный: в Flutter после dispose setState бросит исключение, а в React Native обновление состояния размонтированного компонента тихо проигнорируется. Разное поведение — разная стратегия проверки.

Анимации лучше просить отдельным промтом с требованием указать, что происходит на слабом устройстве: попросите модель оценить, сколько кадров она ожидает, и покажите, где используется AnimatedBuilder во Flutter или useNativeDriver в React Native.

Тесты: промт, который закрывает ветки

Последний блок — тесты. Без явного требования модель пишет один happy-path тест и останавливается.

Ты — инженер по тестам. Для модуля корзины напиши тесты.
Flutter: unit-тест Riverpod Notifier через ProviderContainer, без pumpWidget. Покрой: добавление, удаление, очистку, total при пустой корзине, повторное добавление того же товара.
React Native: тест Zustand-стора без React Testing Library — прямые вызовы экшенов и проверка селекторов.
Требования: не менее 6 тестов, каждый с явным ожидаемым результатом, без сна и таймеров. Покажи, какая ветка покрыта каждым тестом.
Где чаще ошибаются Просят «напиши тесты» без указания инструмента — получают widget-тесты на Flutter и рендер-тесты на React Native, которые падают на анимациях и таймерах. Указывайте ProviderContainer и прямой вызов стора: это быстрее и стабильнее.

Как выбрать стек под промты

Выбор не про «что лучше», а про то, какой промт ваша команда сможет поддерживать. Если в команде есть веб-фронтенд на React и общий TypeScript-код — React Native-промты дадут быстрый старт и переиспользование типов. Если UI критичен, а устройства слабые — Flutter-промты с фиксацией дерева виджетов и Riverpod дают предсказуемый рендеринг.

Мой рабочий приём: держите промты в репозитории рядом с кодом, в папке prompts/, и обновляйте их, когда меняется версия библиотеки. Промт — такой же артефакт, как тест. Если модель начала выдавать Navigator.push вместо context.go, значит, промт устарел, а не модель сломалась.

Начните с одного промта — навигации — и прогоните двойной тап. Если стек не дублируется и deep link открывает экран, остальные блоки подтянутся быстрее. Больше разборов инструментов и подходов к работе с LLM — в LLM-инженерия с LangChain.

← Все статьи

Спросить AI по этой статье

Задайте вопрос по этой статье — ASI Biont ответит, опираясь на её текст.

Читайте также

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

27 октября 2026

Юнит-тесты на Python с LLM: 12 промтов от pytest-фикстур до Hypothesis, которые закрывают непокрытые ветки

27 октября 2026

Расторжение договора с фитнес-клубом или салоном красоты и возврат денег: разбор условий, сроков и претензий в 2026 году

27 октября 2026

Full-Stack AI Engineer: как RAG-пайплайны, векторные базы данных и развертывание LLM на самом деле работают в продакшене

28 октября 2026

Сверка актов вручную или промптом: где LLM экономит бухгалтеру часы, а где создаёт риски

12 ноября 2026

Продакт-менеджмент и рост: стратегия, метрики и решение, которое действительно меняет ситуацию

11 ноября 2026

Уголовное дело: защита и стратегия — как не сломать себе жизнь на первом допросе

28 октября 2026

Нейросети для начинающих: как понять, что происходит внутри ChatGPT и Midjourney, и начать использовать AI без кода

29 октября 2026

Поступление в вуз на бюджет: стратегия выбора и пошаговый план для абитуриента и родителей

29 октября 2026