Введение
Flutter — не просто фреймворк для кроссплатформенной разработки. Это экосистема, в которой каждый день появляются новые виджеты, подходы к управлению состоянием и техники анимации. Когда я начинал работать с Flutter в 2019 году (ещё на версии 1.0), самой большой проблемой было не найти документацию, а правильно сформулировать запрос к коллегам или AI-инструментам. Сегодня, в июле 2026 года, промты стали незаменимым инструментом для ускорения работы. В этой подборке — 15 проверенных промтов, которые покрывают три ключевые области: виджеты, state management и анимации. Каждый промт сопровождается реальным примером кода и пояснением, почему он работает.
1. Базовые промты для виджетов
Промт 1: Создание StatelessWidget с кастомным оформлением
Задача: Быстро сгенерировать виджет с заданными стилями.
Промт: «Напиши StatelessWidget на Flutter, который принимает параметры: заголовок (String), описание (String?) и иконку (IconData). Используй Card с тенью и RoundedRectangleBorder. Код должен быть на Dart, с null-safety.»
Пример результата:
class InfoCard extends StatelessWidget {
final String title;
final String? description;
final IconData icon;
const InfoCard({required this.title, this.description, required this.icon});
@override
Widget build(BuildContext context) {
return Card(
elevation: 4,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
Icon(icon, size: 40, color: Theme.of(context).primaryColor),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: Theme.of(context).textTheme.titleMedium),
if (description != null) Text(description!),
],
),
),
],
),
),
);
}
}
Почему это работает: Промт чётко задаёт структуру (StatelessWidget, параметры, виджет-контейнер). Указание «null-safety» и «RoundedRectangleBorder» убирает лишние варианты.
Промт 2: StatefulWidget с TextEditingController
Задача: Форма ввода с валидацией.
Промт: «Создай StatefulWidget LoginForm с двумя TextEditingController (email и password). Добавь валидацию: email должен содержать '@', password — минимум 6 символов. При ошибке показывай SnackBar. Используй Form и GlobalKey
Пример результата: (сокращённо)
class LoginForm extends StatefulWidget { ... }
class _LoginFormState extends State<LoginForm> {
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
controller: _emailController,
validator: (v) => v!.contains('@') ? null : 'Invalid email',
),
TextFormField(
controller: _passwordController,
validator: (v) => v!.length >= 6 ? null : 'Too short',
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
ScaffoldMessenger.of(context).showSnackBar(...);
}
},
child: Text('Login'),
),
],
),
);
}
}
2. Промты для State Management
Промт 3: Простой пример с Provider
Задача: Счётчик с использованием Provider.
Промт: «Напиши простой пример Provider для управления счётчиком. Включай CounterProvider с ChangeNotifier, экран с ElevatedButton и Text, отображающий значение. Используй MultiProvider, если нужно.»
Пример результата:
class CounterProvider extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() { _count++; notifyListeners(); }
}
// В main.dart: runApp(MultiProvider(providers: [ChangeNotifierProvider(create: (_) => CounterProvider())], child: MyApp()));
// В экране: final counter = context.watch<CounterProvider>();
Промт 4: Блок с событиями и состояниями
Задача: Асинхронная загрузка данных через Bloc.
Промт: «Создай Bloc для загрузки списка пользователей из REST API. Определи события: FetchUsers, RefreshUsers. Состояния: UsersInitial, UsersLoading, UsersLoaded(List users), UsersError(String message). Используй http-пакет. Укажи зависимости.»
Пример результата:
abstract class UsersEvent {}
class FetchUsers extends UsersEvent {}
class RefreshUsers extends UsersEvent {}
abstract class UsersState {}
class UsersInitial extends UsersState {}
class UsersLoading extends UsersState {}
class UsersLoaded extends UsersState { final List<User> users; ... }
class UsersError extends UsersState { final String message; ... }
class UsersBloc extends Bloc<UsersEvent, UsersState> {
UsersBloc() : super(UsersInitial()) {
on<FetchUsers>((event, emit) async {
emit(UsersLoading());
try {
final response = await http.get(Uri.parse('https://api.example.com/users'));
emit(UsersLoaded(users: parseUsers(response.body)));
} catch (e) {
emit(UsersError(message: e.toString()));
}
});
}
}
Промт 5: Riverpod с FutureProvider
Задача: Кеширование данных с автоматическим обновлением.
Промт: «Используй Riverpod (flutter_riverpod) для создания FutureProvider, который загружает список задач из JSON API. Добавь возможность ручного обновления через ref.invalidate. Покажи, как использовать AsyncValue в UI.»
Пример результата:
final tasksProvider = FutureProvider<List<Task>>((ref) async {
final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/todos'));
return parseTasks(response.body);
});
// В виджете: final tasksAsync = ref.watch(tasksProvider); tasksAsync.when(data: (tasks) => ..., loading: () => ..., error: (e, _) => ...);
3. Промты для анимаций
Промт 6: AnimatedContainer с кривой анимации
Задача: Плавное изменение размера и цвета.
Промт: «Создай StatefulWidget, который при нажатии на кнопку меняет ширину контейнера с 100 на 200 и цвет с Colors.blue на Colors.red с помощью AnimatedContainer. Используй кривую Curves.easeInOut. Длительность: 500 мс.»
Пример результата:
bool _expanded = false;
AnimatedContainer(
duration: Duration(milliseconds: 500),
curve: Curves.easeInOut,
width: _expanded ? 200 : 100,
height: 100,
color: _expanded ? Colors.red : Colors.blue,
);
Промт 7: TweenAnimationBuilder для числового счётчика
Задача: Анимированное изменение числа.
Промт: «Реализуй TweenAnimationBuilder, который анимирует число от 0 до 100 за 2 секунды. Отображай число в центре экрана с форматированием: 'Цена: 100 ₽'. Используй TextStyle с fontSize: 24.»
Пример результата:
TweenAnimationBuilder<double>(
tween: Tween<double>(begin: 0, end: 100),
duration: Duration(seconds: 2),
builder: (context, value, child) {
return Text('Цена: ${value.toInt()} ₽', style: TextStyle(fontSize: 24));
},
);
Промт 8: Hero-анимация между экранами
Задача: Плавный переход изображения.
Промт: «Создай два экрана: первый показывает список картинок с Hero-тегами, второй — увеличенное изображение. Используй Hero(tag: 'image_$index'). При нажатии на картинку переход через Navigator.push.»
Пример результата:
// Первый экран: Hero(tag: 'image_1', child: Image.network('url'));
// Второй экран: Hero(tag: 'image_1', child: Image.network('url', fit: BoxFit.contain));
Промт 9: Staggered-анимация с AnimationController
Задача: Последовательное появление элементов списка.
Промт: «Напиши виджет с AnimationController, который анимирует появление трёх карточек с задержкой 200 мс между ними. Используй Interval(0.0, 0.3), Interval(0.3, 0.6), Interval(0.6, 1.0). Карточки должны сдвигаться снизу вверх.»
Пример результата:
AnimationController _controller;
// В initState: _controller = AnimationController(vsync: this, duration: Duration(seconds: 1));
// Для каждой карточки: Animation<Offset> slide = Tween<Offset>(begin: Offset(0, 1), end: Offset(0, 0)).animate(CurvedAnimation(
// parent: _controller, curve: Interval(0.0, 0.3, curve: Curves.easeOut),
// ));
Промт 10: CustomPainter для анимированного круга
Задача: Рисование прогресс-бара.
Промт: «Создай CustomPainter, который рисует дугу окружности, зависящую от значения progress (0.0–1.0). Цвет дуги — градиент от зелёного к красному. Используй Canvas.drawArc. Добавь AnimatedBuilder для анимации.»
Пример результата:
class ProgressPainter extends CustomPainter {
final double progress;
ProgressPainter(this.progress);
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..shader = LinearGradient(colors: [Colors.green, Colors.red]).createShader(Rect.fromLTWH(0, 0, size.width, size.height))
..style = PaintingStyle.stroke
..strokeWidth = 8;
canvas.drawArc(Rect.fromLTWH(0, 0, size.width, size.height), -pi / 2, 2 * pi * progress, false, paint);
}
}
4. Продвинутые комбинации
Промт 11: Bloc + анимация списка
Задача: При загрузке данных из Bloc анимировать появление элементов.
Промт: «Объедини Bloc из Промта 4 с staggered-анимацией из Промта 9. При переходе в состояние UsersLoaded запускай AnimationController для последовательного появления карточек. Используй AnimatedList.»
Пример результата: (структура)
// В BlocListener: if (state is UsersLoaded) _controller.forward();
// AnimatedList с GlobalKey<AnimatedListState> и insertItem с задержкой.
Промт 12: Riverpod + CustomPainter для дашборда
Задача: Виджет, отображающий анимированный график на основе данных из Riverpod.
Промт: «Создай провайдер, который возвращает случайное число каждые 2 секунды. Используй Timer. На UI — CustomPainter, который рисует столбчатую диаграмму. Анимируй переходы между значениями с помощью Tween.»
Пример результата: (упрощённо)
final randomProvider = StreamProvider<int>((ref) {
return Stream.periodic(Duration(seconds: 2), (_) => Random().nextInt(100));
});
// В UI: ref.watch(randomProvider).when(data: (value) => BarChart(value), loading: ..., error: ...);
Промт 13: NestedScrollView с анимированным заголовком
Задача: SliverAppBar, который меняет цвет при скролле.
Промт: «Реализуй NestedScrollView с SliverAppBar. При скролле заголовок должен плавно менять цвет с прозрачного на Colors.primary. Используй FlexibleSpaceBar и ScrollController.»
Пример результата:
NestedScrollView(
headerSliverBuilder: (context, innerBoxIsScrolled) => [
SliverAppBar(
expandedHeight: 200,
flexibleSpace: FlexibleSpaceBar(
background: AnimatedContainer(
duration: Duration(milliseconds: 200),
color: innerBoxIsScrolled ? Colors.primary : Colors.transparent,
),
),
),
],
body: ...
);
5. Промты для тестирования и отладки
Промт 14: Unit-тест для Bloc
Задача: Проверка корректности состояний.
Промт: «Напиши unit-тест для UsersBloc из Промта 4. Используй bloc_test. Проверь, что при событии FetchUsers сначала эмитится UsersLoading, затем UsersLoaded или UsersError. Мокируй http-запрос с помощью Mockito.»
Пример результата:
blocTest<UsersBloc, UsersState>(
'emits [Loading, Loaded] when fetch succeeds',
build: () => UsersBloc(),
act: (bloc) => bloc.add(FetchUsers()),
expect: () => [UsersLoading(), UsersLoaded(users: [...])],
);
Промт 15: Widget-тест для анимированного контейнера
Задача: Проверка изменения состояния.
Промт: «Напиши widget-тест для AnimatedContainer из Промта 6. Используй WidgetTester. Проверь, что после нажатия на кнопку ширина меняется с 100 на 200. Используй pumpAndSettle для ожидания анимации.»
Пример результата:
testWidgets('AnimatedContainer changes size on tap', (tester) async {
await tester.pumpWidget(MyApp());
expect(find.byWidgetPredicate((w) => w is AnimatedContainer && w.width == 100), findsOneWidget);
await tester.tap(find.byType(ElevatedButton));
await tester.pumpAndSettle();
expect(find.byWidgetPredicate((w) => w is AnimatedContainer && w.width == 200), findsOneWidget);
});
Заключение
Эти 15 промтов покрывают 90% типовых задач, с которыми сталкивается Flutter-разработчик: от простых виджетов до сложных комбинаций Bloc и анимаций. Главное — формулировать промты с указанием конкретных классов (например, ChangeNotifier, CustomPainter) и параметров (длительность, кривая). В официальной документации Flutter (flutter.dev) и пакетов (pub.dev) можно найти дополнительные примеры. Начните с базовых промтов, затем комбинируйте их для решения сложных задач. Удачи в кодинге!
Комментарии