Flutter остаётся одной из самых быстрорастущих кросс-платформенных технологий. По данным отчёта Statista за 2026 год, доля Flutter среди мобильных фреймворков превысила 32%, а количество пакетов на pub.dev перевалило за 45 000. Но новички и даже опытные разработчики часто тратят часы на написание однотипного кода: настройку state management, создание анимаций или верстку сложных виджетов. В этой подборке я собрал 16 проверенных промтов для Flutter, которые помогут ускорить разработку в разы — от базовых виджетов до продвинутого Bloc и анимаций. Каждый промт сопровождается кодом и пояснением, чтобы вы могли сразу применить его в реальном проекте.
Базовый уровень: виджеты и DTO
1. Генерация StatelessWidget с кастомными параметрами
Задача: Создать простой виджет с именованными параметрами и конструктором.
Промт: «Создай Flutter StatelessWidget под названием UserCard с параметрами name (String), age (int) и optionalImage (ImageProvider?). Используй Card, ListTile и Padding. Добавь комментарии к каждому параметру.»
Результат:
import 'package:flutter/material.dart';
class UserCard extends StatelessWidget {
final String name;
final int age;
final ImageProvider? optionalImage;
const UserCard({
super.key,
required this.name,
required this.age,
this.optionalImage,
});
@override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.all(8.0),
child: ListTile(
leading: optionalImage != null
? CircleAvatar(backgroundImage: optionalImage)
: const CircleAvatar(child: Icon(Icons.person)),
title: Text(name),
subtitle: Text('Возраст: $age'),
),
);
}
}
Пояснение: Промт генерирует готовый виджет с типизированными параметрами, что снижает вероятность ошибок. Использование ImageProvider? позволяет передавать как AssetImage, так и NetworkImage.
2. StatefulWidget с таймером обратного отсчёта
Задача: Создать виджет с таймером, который обновляет UI каждую секунду.
Промт: «Создай StatefulWidget CountdownTimer с параметром seconds (int). Используй Timer.periodic, обновляй текст каждую секунду. При достижении 0 останови таймер и покажи SnackBar.»
Результат:
import 'dart:async';
import 'package:flutter/material.dart';
class CountdownTimer extends StatefulWidget {
final int seconds;
const CountdownTimer({super.key, required this.seconds});
@override
State<CountdownTimer> createState() => _CountdownTimerState();
}
class _CountdownTimerState extends State<CountdownTimer> {
late int _remaining;
Timer? _timer;
@override
void initState() {
super.initState();
_remaining = widget.seconds;
_startTimer();
}
void _startTimer() {
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (_remaining <= 0) {
timer.cancel();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Время вышло!')),
);
} else {
setState(() => _remaining--);
}
});
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Center(
child: Text(
'$_remaining секунд',
style: Theme.of(context).textTheme.headlineLarge,
),
);
}
}
Пояснение: Промт автоматически добавляет dispose для предотвращения утечек памяти — типичная ошибка новичков. Timer.periodic идеален для анимаций и опросов API.
3. Модель данных с JSON-сериализацией
Задача: Сгенерировать DTO-класс с fromJson/toJson.
Промт: «Создай Dart-класс User с полями id (int), email (String), isActive (bool). Добавь конструктор, fromJson(Map
Результат:
class User {
final int id;
final String email;
final bool isActive;
User({required this.id, required this.email, required this.isActive});
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'] as int,
email: json['email'] as String,
isActive: json['is_active'] as bool? ?? false,
);
}
Map<String, dynamic> toJson() => {
'id': id,
'email': email,
'is_active': isActive,
};
}
Пояснение: Ручное написание fromJson/toJson помогает избежать зависимости от codegen-библиотек (json_serializable). Промт учитывает snake_case в JSON и null-безопасность.
Средний уровень: State Management
4. Provider с ChangeNotifier для списка задач
Задача: Реализовать простой список задач с добавлением и удалением.
Промт: «Создай ChangeNotifier TaskProvider с полем List
Результат:
import 'package:flutter/foundation.dart';
class TaskProvider extends ChangeNotifier {
final List<String> _tasks = [];
List<String> get tasks => List.unmodifiable(_tasks);
void addTask(String task) {
_tasks.add(task);
notifyListeners();
}
void removeTask(int index) {
_tasks.removeAt(index);
notifyListeners();
}
}
Пояснение: ChangeNotifier — основа Provider. Использование List.unmodifiable защищает данные от внешних изменений. Промт подходит для простых форм и списков.
5. Cubit для подсчёта с историей изменений
Задача: Создать Cubit с состояниями и undo.
Промт: «Создай CounterCubit на основе Cubit
Результат:
import 'package:flutter_bloc/flutter_bloc.dart';
class CounterCubit extends Cubit<int> {
final List<int> _history = [];
CounterCubit() : super(0);
void increment() {
_history.add(state);
emit(state + 1);
}
void decrement() {
_history.add(state);
emit(state - 1);
}
void undo() {
if (_history.isNotEmpty) {
emit(_history.removeLast());
}
}
}
Пояснение: Cubit проще Bloc для простых сценариев. История изменений полезна для отладки и UX. Промт генерирует готовый бизнес-логический слой.
6. Bloc с событиями и состояниями для авторизации
Задача: Реализовать полноценный блок для логина с валидацией.
Промт: «Создай AuthBloc с событиями LoginEvent (email, password) и LogoutEvent. Состояния: AuthInitial, AuthLoading, AuthSuccess(User user), AuthFailure(String error). Используя StreamController и mapEventToState. Добавь валидацию email.»
Результат:
import 'package:flutter_bloc/flutter_bloc.dart';
// Events
abstract class AuthEvent {}
class LoginEvent extends AuthEvent {
final String email;
final String password;
LoginEvent(this.email, this.password);
}
class LogoutEvent extends AuthEvent {}
// States
abstract class AuthState {}
class AuthInitial extends AuthState {}
class AuthLoading extends AuthState {}
class AuthSuccess extends AuthState {
final User user;
AuthSuccess(this.user);
}
class AuthFailure extends AuthState {
final String error;
AuthFailure(this.error);
}
// Bloc
class AuthBloc extends Bloc<AuthEvent, AuthState> {
AuthBloc() : super(AuthInitial()) {
on<LoginEvent>((event, emit) async {
emit(AuthLoading());
// Валидация
if (!event.email.contains('@')) {
emit(AuthFailure('Некорректный email'));
return;
}
try {
// Имитация API-запроса
await Future.delayed(const Duration(seconds: 1));
emit(AuthSuccess(User(id: 1, email: event.email, isActive: true)));
} catch (e) {
emit(AuthFailure(e.toString()));
}
});
on<LogoutEvent>((event, emit) {
emit(AuthInitial());
});
}
}
Пояснение: Bloc — стандарт для сложных приложений. Промт включает валидацию и имитацию асинхронного запроса. Для продакшена замените Future.delayed на реальный репозиторий.
Продвинутый уровень: Анимации и кастомные виджеты
7. Анимированный переключатель с TweenAnimationBuilder
Задача: Создать виджет, который плавно меняет цвет и размер при нажатии.
Промт: «Создай StatefulWidget AnimatedToggle с параметром isOn (bool). Используй TweenAnimationBuilder для анимации цвета (зелёный/серый) и размера (100/120). Добавь GestureDetector.»
Результат:
import 'package:flutter/material.dart';
class AnimatedToggle extends StatefulWidget {
final bool isOn;
const AnimatedToggle({super.key, required this.isOn});
@override
State<AnimatedToggle> createState() => _AnimatedToggleState();
}
class _AnimatedToggleState extends State<AnimatedToggle> {
late bool _isOn;
@override
void initState() {
super.initState();
_isOn = widget.isOn;
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => setState(() => _isOn = !_isOn),
child: TweenAnimationBuilder<double>(
tween: Tween(begin: 0, end: _isOn ? 1.0 : 0.0),
duration: const Duration(milliseconds: 300),
builder: (context, value, child) {
return Container(
width: 100 + value * 20,
height: 100,
decoration: BoxDecoration(
color: Color.lerp(Colors.grey, Colors.green, value),
borderRadius: BorderRadius.circular(20),
),
child: Center(child: Text(_isOn ? 'ON' : 'OFF')),
);
},
),
);
}
}
Пояснение: TweenAnimationBuilder — мощный инструмент для анимаций без явного AnimationController. Промт демонстрирует интерполяцию цвета и размера.
8. Staggered анимация списка
Задача: Анимировать появление элементов списка с задержкой.
Промт: «Создай виджет StaggeredList, который принимает List
Результат:
import 'package:flutter/material.dart';
class StaggeredList extends StatefulWidget {
final List<String> items;
const StaggeredList({super.key, required this.items});
@override
State<StaggeredList> createState() => _StaggeredListState();
}
class _StaggeredListState extends State<StaggeredList> {
late List<bool> _visible;
@override
void initState() {
super.initState();
_visible = List.filled(widget.items.length, false);
for (int i = 0; i < widget.items.length; i++) {
Future.delayed(Duration(milliseconds: 200 * i), () {
if (mounted) setState(() => _visible[i] = true);
});
}
}
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: widget.items.length,
itemBuilder: (context, index) {
return AnimatedOpacity(
opacity: _visible[index] ? 1.0 : 0.0,
duration: const Duration(milliseconds: 300),
child: ListTile(title: Text(widget.items[index])),
);
},
);
}
}
Пояснение: Staggered-анимации создают эффект «волны» и улучшают восприятие UX. Промт использует проверку mounted для предотвращения ошибок после dispose.
9. Кастомный виджет с наследованием InheritedWidget
Задача: Создать тему, доступную всем дочерним виджетам без Provider.
Промт: «Создай InheritedWidget под названием AppTheme с полями Color primaryColor, double fontSize. Добавь статический метод of(BuildContext). Создай пример использования с Text и Container.»
Результат:
import 'package:flutter/material.dart';
class AppTheme extends InheritedWidget {
final Color primaryColor;
final double fontSize;
const AppTheme({
super.key,
required this.primaryColor,
required this.fontSize,
required super.child,
});
static AppTheme of(BuildContext context) {
final widget = context.dependOnInheritedWidgetOfExactType<AppTheme>();
assert(widget != null, 'No AppTheme found');
return widget!;
}
@override
bool updateShouldNotify(AppTheme oldWidget) {
return primaryColor != oldWidget.primaryColor || fontSize != oldWidget.fontSize;
}
}
Пояснение: InheritedWidget — низкоуровневый механизм Flutter для передачи данных вниз по дереву. Промт полезен для понимания того, как работают Provider и Bloc под капотом.
10. Анимация с CustomPainter
Задача: Нарисовать анимированную волну.
Промт: «Создай StatefulWidget WaveAnimation с CustomPainter. Используйте AnimationController и TickerProviderStateMixin. Рисуй синусоиду, меняющую фазу.»
Результат:
import 'package:flutter/material.dart';
class WaveAnimation extends StatefulWidget {
@override
State<WaveAnimation> createState() => _WaveAnimationState();
}
class _WaveAnimationState extends State<WaveAnimation>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 2),
)..repeat();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return CustomPaint(
painter: WavePainter(phase: _controller.value * 2 * 3.14159),
size: const Size(300, 100),
);
},
);
}
}
class WavePainter extends CustomPainter {
final double phase;
WavePainter({required this.phase});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.blue
..strokeWidth = 2.0
..style = PaintingStyle.stroke;
final path = Path();
for (double x = 0; x < size.width; x++) {
final y = size.height / 2 + 20 * (x / 20 + phase).sin();
if (x == 0) {
path.moveTo(x, y);
} else {
path.lineTo(x, y);
}
}
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(WavePainter oldDelegate) => oldDelegate.phase != phase;
}
Пояснение: CustomPainter даёт полный контроль над графикой. Промт идеален для индикаторов загрузки, графиков и фоновых эффектов.
Экспертный уровень: Интеграция и паттерны
11. Repository с кэшированием и ошибками
Задача: Создать слой данных с локальным кэшем.
Промт: «Создай абстрактный класс UserRepository с методами Future> fetchUsers() и Future
Результат:
abstract class UserRepository {
Future<List<User>> fetchUsers();
Future<User> getUser(int id);
}
class UserRepositoryImpl implements UserRepository {
final Map<int, User> _cache = {};
@override
Future<List<User>> fetchUsers() async {
try {
// Имитация API
await Future.delayed(const Duration(seconds: 1));
final users = [User(id: 1, email: 'test@test.com', isActive: true)];
for (var user in users) {
_cache[user.id] = user;
}
return users;
} catch (e) {
debugPrint('Error fetching users: $e');
return [];
}
}
@override
Future<User> getUser(int id) async {
if (_cache.containsKey(id)) return _cache[id]!;
// Иначе запрос к API
throw Exception('User not found');
}
}
Пояснение: Паттерн Repository отделяет бизнес-логику от источника данных. Кэш уменьшает количество запросов к сети. Промт легко расширить добавлением Dio или Hive.
12. Stream с RxDart для поиска
Задача: Реализовать debounced-поиск с потоком.
Промт: «Создай класс SearchBloc с полем StreamController> результатов. Добавь метод dispose.»
Результат:
import 'dart:async';
import 'package:rxdart/rxdart.dart';
class SearchBloc {
final StreamController<String> _queryController = StreamController<String>.broadcast();
final List<String> _allItems = ['Flutter', 'Dart', 'Widget', 'Animation'];
late final Stream<List<String>> resultsStream;
SearchBloc() {
resultsStream = _queryController.stream
.debounceTime(const Duration(milliseconds: 300))
.map((query) {
return _allItems
.where((item) => item.toLowerCase().contains(query.toLowerCase()))
.toList();
});
}
void search(String query) => _queryController.add(query);
void dispose() {
_queryController.close();
}
}
Пояснение: RxDart — мощное расширение для работы с потоками. Debounce предотвращает лишние запросы при быстром вводе. Промт готов к интеграции с UI через StreamBuilder.
13. StatefulWidget с Hero-анимацией
Задача: Анимировать переход между экранами с общим элементом.
Промт: «Создай два экрана: ScreenA с круглым контейнером, ScreenB с увеличенной версией. Используй Hero с тегом 'heroCircle'. При нажатии на контейнер вызывай Navigator.push.»
Результат:
// ScreenA
class ScreenA extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Screen A')),
body: Center(
child: GestureDetector(
onTap: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => ScreenB()),
),
child: Hero(
tag: 'heroCircle',
child: Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
),
),
),
),
),
);
}
}
// ScreenB
class ScreenB extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Screen B')),
body: Center(
child: Hero(
tag: 'heroCircle',
child: Container(
width: 200,
height: 200,
decoration: BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
),
),
),
),
);
}
}
Пояснение: Hero-анимация — встроенный механизм Flutter для плавных переходов. Промт работает без дополнительных библиотек.
14. Кастомный модальный bottom sheet с анимацией
Задача: Создать выезжающую снизу панель с кастомной анимацией.
Промт: «Создай функцию showCustomBottomSheet(BuildContext context) с использованием showModalBottomSheet и DraggableScrollableSheet. Добавь закруглённые углы и тень.»
Результат:
import 'package:flutter/material.dart';
void showCustomBottomSheet(BuildContext context) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
),
builder: (context) => DraggableScrollableSheet(
initialChildSize: 0.5,
minChildSize: 0.2,
maxChildSize: 0.8,
expand: false,
builder: (context, scrollController) {
return Container(
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
boxShadow: [
BoxShadow(blurRadius: 10, color: Colors.black26),
],
),
child: ListView.builder(
controller: scrollController,
itemCount: 20,
itemBuilder: (context, index) => ListTile(
leading: CircleAvatar(child: Text('$index')),
title: Text('Item $index'),
),
),
);
},
),
);
}
Пояснение: Комбинация showModalBottomSheet и DraggableScrollableSheet даёт гибкий UI. Промт подходит для форм выбора и фильтров.
15. Freezed-модель с sealed-классами
Задача: Создать иммутабельную модель с помощью freezed.
Промт: «Создай sealed-класс Result с вариантами Success(T data) и Failure(String error). Используй пакет freezed. Сгенерируй fromJson и toJson.»
Результат:
import 'package:freezed_annotation/freezed_annotation.dart';
part 'result.freezed.dart';
part 'result.g.dart';
@freezed
sealed class Result<T> with _$Result<T> {
const factory Result.success(T data) = Success<T>;
const factory Result.failure(String error) = Failure<T>;
factory Result.fromJson(Map<String, dynamic> json, T Function(Object?) fromJsonT) =>
_$ResultFromJson(json, fromJsonT);
}
Пояснение: Freezed генерирует copyWith, equality и сериализацию. Промт требует запуска build_runner. Идеален для сложных моделей с множеством состояний.
16. Анимация с ImplicitlyAnimatedWidget
Задача: Создать виджет, который анимирует своё свойство при изменении.
Промт: «Создай класс AnimatedSquare, расширяющий ImplicitlyAnimatedWidget. Параметр: Color color. Используй Tween
Результат:
import 'package:flutter/material.dart';
class AnimatedSquare extends ImplicitlyAnimatedWidget {
final Color color;
const AnimatedSquare({
super.key,
required this.color,
super.duration = const Duration(milliseconds: 500),
});
@override
AnimatedSquareState createState() => AnimatedSquareState();
}
class AnimatedSquareState extends AnimatedWidgetBaseState<AnimatedSquare> {
ColorTween? _colorTween;
@override
void forEachTween(TweenVisitor<dynamic> visitor) {
_colorTween = visitor(
_colorTween,
widget.color,
(dynamic value) => ColorTween(begin: value as Color?),
) as ColorTween?;
}
@override
Widget build(BuildContext context) {
return Container(
width: 100,
height: 100,
color: _colorTween?.evaluate(animation),
);
}
}
Пояснение: ImplicitlyAnimatedWidget — альтернатива AnimatedContainer для кастомных анимаций. Промт подходит для создания библиотеки анимированных виджетов.
Заключение
Эти 16 промтов покрывают ключевые сценарии разработки на Flutter: от базовых виджетов до state management и анимаций. Практикуйтесь, комбинируйте их и адаптируйте под свои задачи. Помните: лучший код — тот, который вы понимаете. Начните с простых промтов, постепенно переходя к сложным. Если возникнут вопросы — пишите в комментариях. Удачной разработки!
Комментарии