12 промтов для Flutter: от виджетов до Bloc и Riverpod

Введение

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 для валидации. Добавь проверку email через RegExp, пароль не менее 8 символов, подтверждение должно совпадать с паролем. При успехе выведи SnackBar.»

Пример вывода 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 и научиться писать такие промты самостоятельно, обратите внимание на наши курсы. А пока — сохраните эту подборку в закладки и используйте как шпаргалку.

← Все статьи

Комментарии

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

Умный полив с AI: пошаговое руководство по интеграции датчика дождя и влажности почвы с ASI Biont через MQTT и ESP32

20 июля 2026

Computer Vision (OpenMV, OV2640) + ASI Biont: автоматизация контроля качества через AI-агента

20 июля 2026

Модель адаптивного стриминга 2026: как читать уравнения и использовать скриншоты для оптимизации видеопотоков

20 июля 2026

15 промтов для отладки и поиска багов в коде: шпаргалка для разработчиков

20 июля 2026

Как запустить 35B-модель на ноутбуке с 8 ГБ VRAM: опыт команды Ornith с AMD Strix Halo

20 июля 2026

Искусство плетения алгоритмов: как Talk и Vibe Coding меняют разработку в 2026 году

20 июля 2026

Интеграция OV2640 + ESP32 (Face Detection) с AI-агентом ASI Biont: Edge AI для автономного контроля доступа

20 июля 2026

Corporate Governance — корпоративное управление и совет директоров: как AI-обучение на asibiont.com готовит лидеров нового поколения

20 июля 2026

Как интеграция ASI Biont с Klaviyo увеличила повторные продажи на 28% и сэкономила 70% времени на email-маркетинг

20 июля 2026