Введение
Flutter — это не просто фреймворк, а целая экосистема для создания кроссплатформенных приложений. С момента выхода Flutter 3.0 в 2022 году и до текущей версии 3.24 (июль 2026) количество пакетов на pub.dev превысило 50 000. Но даже опытные разработчики тратят до 30% времени на написание шаблонного кода, подбор правильных виджетов и настройку state management. Именно здесь на помощь приходят промты — готовые шаблоны запросов к AI, которые позволяют за минуты получить рабочий код, архитектурное решение или объяснение сложной концепции.
В этой подборке — 12 проверенных промтов, которые я использую ежедневно. Каждый промт сопровождается реальным примером кода и пояснением, как его адаптировать под свой проект. Никакой воды — только практика.
1. Промт для генерации кастомного StatelessWidget
Промт: «Создай StatelessWidget на Flutter, который принимает имя пользователя и отображает приветствие с аватаром. Используй Material Design 3, добавь обработку пустого имени. Код должен быть на Dart, с комментариями.»
Пример вывода AI:
import 'package:flutter/material.dart';
class GreetingCard extends StatelessWidget {
final String userName;
const GreetingCard({Key? key, this.userName = ''}) : super(key: key);
@override
Widget build(BuildContext context) {
final displayName = userName.isEmpty ? 'Гость' : userName;
final theme = Theme.of(context);
return Card(
elevation: 2,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
CircleAvatar(
backgroundColor: theme.colorScheme.primary,
child: Text(displayName[0].toUpperCase()),
),
const SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Привет, $displayName!', style: theme.textTheme.titleMedium),
Text('Добро пожаловать', style: theme.textTheme.bodySmall),
],
),
],
),
),
);
}
}
Как использовать: Замените userName на динамические данные из вашего приложения. Промт особенно полезен, когда нужно быстро создать UI-компонент для списка контактов или профиля.
2. Промт для StatefulWidget с анимацией
Промт: «Напиши StatefulWidget с анимацией появления (fade-in) при первом рендере. Используй AnimationController и Tween
Пример вывода AI:
import 'package:flutter/material.dart';
class FadeInWidget extends StatefulWidget {
final Widget child;
final Duration duration;
const FadeInWidget({Key? key, required this.child, this.duration = const Duration(milliseconds: 500)}) : super(key: key);
@override
_FadeInWidgetState createState() => _FadeInWidgetState();
}
class _FadeInWidgetState extends State<FadeInWidget> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: widget.duration);
_animation = Tween<double>(begin: 0.0, end: 1.0).animate(_controller);
_controller.forward();
}
void replay() {
_controller.reset();
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
FadeTransition(opacity: _animation, child: widget.child),
ElevatedButton(onPressed: replay, child: const Text('Повторить')),
],
);
}
}
Как использовать: Этот промт экономит время на написание boilerplate для анимаций. Замените child на любой виджет — изображение, текст или карточку.
3. Промт для реализации Bloc (Cubit)
Промт: «Создай Cubit для управления счётчиком на Flutter с использованием flutter_bloc. Реализуй три состояния: initial, loading, loaded. Добавь методы increment и decrement с проверкой на отрицательное значение.»
Пример вывода AI:
// counter_cubit.dart
import 'package:flutter_bloc/flutter_bloc.dart';
// Состояния
abstract class CounterState {}
class CounterInitial extends CounterState {}
class CounterLoading extends CounterState {}
class CounterLoaded extends CounterState {
final int count;
CounterLoaded(this.count);
}
// Cubit
class CounterCubit extends Cubit<CounterState> {
CounterCubit() : super(CounterInitial());
void increment() {
emit(CounterLoading());
Future.delayed(const Duration(milliseconds: 100), () {
final currentState = state;
if (currentState is CounterLoaded) {
emit(CounterLoaded(currentState.count + 1));
} else {
emit(CounterLoaded(1));
}
});
}
void decrement() {
emit(CounterLoading());
Future.delayed(const Duration(milliseconds: 100), () {
final currentState = state;
if (currentState is CounterLoaded && currentState.count > 0) {
emit(CounterLoaded(currentState.count - 1));
} else if (currentState is! CounterLoaded) {
emit(CounterLoaded(0));
}
});
}
}
Как использовать: Подключите Cubit в BlocProvider и используйте BlocBuilder в UI. Промт легко адаптировать под любую бизнес-логику — корзину, авторизацию или загрузку данных.
4. Промт для Riverpod с AsyncValue
Промт: «Напиши провайдер Riverpod (v2) для асинхронной загрузки списка пользователей из REST API. Используй AsyncValue и FutureProvider. Покажи, как обработать ошибки и состояние загрузки.»
Пример вывода AI:
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
class User {
final int id;
final String name;
final String email;
User({required this.id, required this.name, required this.email});
factory User.fromJson(Map<String, dynamic> json) => User(id: json['id'], name: json['name'], email: json['email']);
}
final userListProvider = FutureProvider<List<User>>((ref) async {
final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/users'));
if (response.statusCode == 200) {
final List<dynamic> jsonList = json.decode(response.body);
return jsonList.map((json) => User.fromJson(json)).toList();
} else {
throw Exception('Ошибка загрузки: ${response.statusCode}');
}
});
Как использовать: В UI используйте Consumer или ConsumerWidget для подписки на провайдер. Промт подходит для всех типов асинхронных данных — новости, товары, заказы.
5. Промт для кастомного анимированного виджета
Промт: «Создай анимированный виджет для отображения прогресс-бара с текстом внутри. Используй AnimatedBuilder и TweenAnimationBuilder. Цвет должен меняться в зависимости от прогресса (зелёный для 0-50%, жёлтый для 51-80%, красный для 81-100%).»
Пример вывода AI:
import 'package:flutter/material.dart';
class AnimatedProgressBar extends StatefulWidget {
final double progress; // от 0.0 до 1.0
final double height;
const AnimatedProgressBar({Key? key, required this.progress, this.height = 20}) : super(key: key);
@override
_AnimatedProgressBarState createState() => _AnimatedProgressBarState();
}
class _AnimatedProgressBarState extends State<AnimatedProgressBar> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 300));
_animation = Tween<double>(begin: 0.0, end: widget.progress).animate(_controller);
_controller.forward();
}
@override
void didUpdateWidget(AnimatedProgressBar oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.progress != widget.progress) {
_animation = Tween<double>(begin: _animation.value, end: widget.progress).animate(_controller);
_controller.forward(from: 0.0);
}
}
Color _getColor(double progress) {
if (progress <= 0.5) return Colors.green;
if (progress <= 0.8) return Colors.orange;
return Colors.red;
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Container(
height: widget.height,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(widget.height / 2),
color: Colors.grey[300],
),
child: Stack(
children: [
FractionallySizedBox(
widthFactor: _animation.value,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(widget.height / 2),
color: _getColor(_animation.value),
),
),
),
Center(
child: Text('${(_animation.value * 100).toStringAsFixed(0)}%',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
),
],
),
);
},
);
}
}
Как использовать: Промт идеален для создания кастомных индикаторов загрузки, шкал выполнения задач или отображения заполненности профиля.
6. Промт для работы с формами и валидацией
Промт: «Создай форму регистрации с полями email, password и confirmPassword. Используй GlobalKey
Пример вывода AI:
import 'package:flutter/material.dart';
class RegistrationForm extends StatefulWidget {
@override
_RegistrationFormState createState() => _RegistrationFormState();
}
class _RegistrationFormState extends State<RegistrationForm> {
final _formKey = GlobalKey<FormState>();
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final _confirmPasswordController = TextEditingController();
@override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
_confirmPasswordController.dispose();
super.dispose();
}
String? _validateEmail(String? value) {
if (value == null || value.isEmpty) return 'Введите email';
final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
if (!emailRegex.hasMatch(value)) return 'Некорректный email';
return null;
}
String? _validatePassword(String? value) {
if (value == null || value.isEmpty) return 'Введите пароль';
if (value.length < 8) return 'Пароль должен быть не менее 8 символов';
return null;
}
String? _validateConfirmPassword(String? value) {
if (value != _passwordController.text) return 'Пароли не совпадают';
return null;
}
void _submit() {
if (_formKey.currentState!.validate()) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Регистрация успешна')),
);
// Здесь можно отправить данные на сервер
}
}
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(controller: _emailController, validator: _validateEmail, decoration: const InputDecoration(labelText: 'Email')),
TextFormField(controller: _passwordController, validator: _validatePassword, obscureText: true, decoration: const InputDecoration(labelText: 'Пароль')),
TextFormField(controller: _confirmPasswordController, validator: _validateConfirmPassword, obscureText: true, decoration: const InputDecoration(labelText: 'Подтвердите пароль')),
const SizedBox(height: 20),
ElevatedButton(onPressed: _submit, child: const Text('Зарегистрироваться')),
],
),
);
}
}
Как использовать: Замените поля на свои (например, добавьте телефон или дату рождения). Промт подходит для любых форм — логин, заказ, анкета.
7. Промт для работы с HTTP-запросами и отловом ошибок
Промт: «Напиши сервис на Flutter для GET-запроса к API с обработкой ошибок: таймаут, 404, 500, отсутствие интернета. Используй пакет http и try-catch. Верни результат в виде Either из dartz или кастомного класса Result.»
Пример вывода AI:
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'dart:async';
class Result<T> {
final T? data;
final String? error;
Result.success(this.data) : error = null;
Result.failure(this.error) : data = null;
}
class ApiService {
static const _timeout = Duration(seconds: 10);
Future<Result<Map<String, dynamic>>> fetchData(String url) async {
try {
final response = await http.get(Uri.parse(url)).timeout(_timeout);
if (response.statusCode == 200) {
return Result.success(json.decode(response.body));
} else if (response.statusCode == 404) {
return Result.failure('Ресурс не найден');
} else if (response.statusCode == 500) {
return Result.failure('Ошибка сервера');
} else {
return Result.failure('Ошибка: ${response.statusCode}');
}
} on TimeoutException {
return Result.failure('Таймаут запроса');
} on http.ClientException {
return Result.failure('Нет подключения к интернету');
} catch (e) {
return Result.failure('Неизвестная ошибка: $e');
}
}
}
Как использовать: Промт даёт готовую обёртку для всех сетевых запросов. Интегрируйте с любым state management — Bloc, Riverpod или Provider.
8. Промт для создания кастомного ScrollView с бесконечной подгрузкой
Промт: «Создай InfiniteScrollView на Flutter, который подгружает новые данные при прокрутке вниз. Используй ScrollController и слушай событие на 80% от конца списка. Покажи индикатор загрузки внизу.»
Пример вывода AI:
import 'package:flutter/material.dart';
class InfiniteScrollView extends StatefulWidget {
final Future<List<Widget>> Function(int page) fetchItems;
final int initialPage;
const InfiniteScrollView({Key? key, required this.fetchItems, this.initialPage = 1}) : super(key: key);
@override
_InfiniteScrollViewState createState() => _InfiniteScrollViewState();
}
class _InfiniteScrollViewState extends State<InfiniteScrollView> {
final _scrollController = ScrollController();
final List<Widget> _items = [];
int _currentPage = 1;
bool _isLoading = false;
bool _hasMore = true;
@override
void initState() {
super.initState();
_loadMore();
_scrollController.addListener(() {
if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent * 0.8 && !_isLoading && _hasMore) {
_loadMore();
}
});
}
Future<void> _loadMore() async {
setState(() => _isLoading = true);
final newItems = await widget.fetchItems(_currentPage);
setState(() {
_items.addAll(newItems);
_currentPage++;
_isLoading = false;
if (newItems.isEmpty) _hasMore = false;
});
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ListView.builder(
controller: _scrollController,
itemCount: _items.length + (_isLoading ? 1 : 0),
itemBuilder: (context, index) {
if (index < _items.length) return _items[index];
return const Center(child: CircularProgressIndicator());
},
);
}
}
Как использовать: Передайте в fetchItems функцию, которая возвращает виджеты для каждой страницы. Промт подходит для ленты новостей, каталога товаров или списка постов.
9. Промт для локализации (i18n) с помощью intl
Промт: «Настрой локализацию Flutter-приложения с поддержкой русского и английского языков. Используй пакет intl и ARB-файлы. Покажи пример перевода строки приветствия.»
Пример вывода AI:
// app_localizations.dart
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class AppLocalizations {
static AppLocalizations? of(BuildContext context) {
return Localizations.of<AppLocalizations>(context, AppLocalizations);
}
String get hello {
return Intl.message('Hello', name: 'hello', desc: 'Greeting');
}
}
// main.dart
import 'package:flutter_localizations/flutter_localizations.dart';
void main() => runApp(MaterialApp(
localizationsDelegates: [
AppLocalizationsDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [Locale('en'), Locale('ru')],
home: MyApp(),
));
Как использовать: Создайте ARB-файлы для каждого языка и используйте AppLocalizations.of(context)!.hello. Промт экономит часы на ручной настройке локализации.
10. Промт для работы с камерой и галереей
Промт: «Напиши виджет для выбора изображения из галереи или камеры на Flutter. Используй пакеты image_picker и permission_handler. Покажи выбранное изображение в CircleAvatar.»
Пример вывода AI:
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
class ImagePickerWidget extends StatefulWidget {
@override
_ImagePickerWidgetState createState() => _ImagePickerWidgetState();
}
class _ImagePickerWidgetState extends State<ImagePickerWidget> {
final ImagePicker _picker = ImagePicker();
XFile? _imageFile;
Future<void> _pickImage(ImageSource source) async {
final XFile? pickedFile = await _picker.pickImage(source: source, maxWidth: 512, maxHeight: 512);
setState(() => _imageFile = pickedFile);
}
void _showPickerDialog() {
showModalBottomSheet(
context: context,
builder: (context) => Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(leading: const Icon(Icons.camera_alt), title: const Text('Камера'), onTap: () { _pickImage(ImageSource.camera); Navigator.pop(context); }),
ListTile(leading: const Icon(Icons.photo_library), title: const Text('Галерея'), onTap: () { _pickImage(ImageSource.gallery); Navigator.pop(context); }),
],
),
);
}
@override
Widget build(BuildContext context) {
return Column(
children: [
CircleAvatar(
radius: 50,
backgroundImage: _imageFile != null ? FileImage(File(_imageFile!.path)) : null,
child: _imageFile == null ? const Icon(Icons.person, size: 50) : null,
),
const SizedBox(height: 10),
ElevatedButton(onPressed: _showPickerDialog, child: const Text('Выбрать фото')),
],
);
}
}
Как использовать: Промт даёт готовый UI для выбора изображения. Добавьте permission_handler для запроса разрешений на Android 13+ (API 33).
11. Промт для реализации темы (светлая/тёмная)
Промт: «Создай переключатель темы в Flutter с сохранением выбора в SharedPreferences. Используй ThemeMode.system, light и dark темы. Реализуй с помощью ChangeNotifier и Provider.»
Пример вывода AI:
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class ThemeProvider extends ChangeNotifier {
ThemeMode _themeMode = ThemeMode.system;
final String _key = 'theme_mode';
ThemeMode get themeMode => _themeMode;
ThemeProvider() {
_loadTheme();
}
Future<void> _loadTheme() async {
final prefs = await SharedPreferences.getInstance();
final value = prefs.getString(_key) ?? 'system';
switch (value) {
case 'light':
_themeMode = ThemeMode.light;
break;
case 'dark':
_themeMode = ThemeMode.dark;
break;
default:
_themeMode = ThemeMode.system;
}
notifyListeners();
}
Future<void> setThemeMode(ThemeMode mode) async {
_themeMode = mode;
final prefs = await SharedPreferences.getInstance();
String value;
switch (mode) {
case ThemeMode.light:
value = 'light';
break;
case ThemeMode.dark:
value = 'dark';
break;
default:
value = 'system';
}
await prefs.setString(_key, value);
notifyListeners();
}
}
Как использовать: Оборачивайте приложение в ChangeNotifierProvider и используйте Consumer<ThemeProvider>. Промт подходит для любого приложения с кастомизацией внешнего вида.
12. Промт для анимации переходов между экранами
Промт: «Создай кастомную анимацию перехода между экранами в Flutter с использованием SlideTransition и FadeTransition. Переход: текущий экран уезжает влево, новый появляется с прозрачности.»
Пример вывода AI:
import 'package:flutter/material.dart';
class SlideFadeTransition extends PageRouteBuilder {
final Widget page;
SlideFadeTransition({required this.page})
: super(
pageBuilder: (context, animation, secondaryAnimation) => page,
transitionsBuilder: (context, animation, secondaryAnimation, child) {
const begin = Offset(1.0, 0.0);
const end = Offset.zero;
const curve = Curves.easeInOut;
var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
var fadeAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(animation);
return SlideTransition(
position: animation.drive(tween),
child: FadeTransition(opacity: fadeAnimation, child: child),
);
},
transitionDuration: const Duration(milliseconds: 300),
);
}
// Использование:
// Navigator.push(context, SlideFadeTransition(page: SecondScreen()));
Как использовать: Замените SecondScreen() на любой виджет. Промт можно адаптировать под любые направления анимации — вверх, вниз, масштабирование.
Заключение
Эти 12 промтов покрывают 80% повседневных задач Flutter-разработчика: от создания виджетов до сложной анимации и state management. Каждый промт — это не просто шаблон, а готовое решение, которое можно вставить в проект и сразу использовать. Главное преимущество — вы экономите время на рутинном коде и сосредотачиваетесь на бизнес-логике.
Попробуйте адаптировать любой из промтов под свой текущий проект. Например, возьмите промт для Cubit и переделайте его под управление корзиной в интернет-магазине. Или используйте промт для анимации, чтобы оживить загрузку данных. Практика — лучший способ закрепить знания.
Если вы хотите углубиться в Flutter и научиться писать такие промты самостоятельно, обратите внимание на наши курсы. А пока — сохраните эту подборку в закладки и используйте как шпаргалку.
Комментарии