10 Pro-Level Flutter Prompts for Widgets, State Management, and Animations

Introduction

Flutter has evolved rapidly since its inception, and by mid-2026, the framework is more mature than ever. With the stable release of Dart 3.6 and Flutter 3.28, developers are now building complex, production-grade apps that rival native performance. But even the most experienced Flutter developers sometimes struggle with structuring widgets, choosing the right state management approach, or creating fluid animations. This is where prompts come in.

In the context of AI-assisted development, prompts are carefully crafted instructions that help you generate, refactor, or debug Flutter code using large language models (LLMs) like GPT-4, Claude 3, or specialized code assistants. A well-written prompt can turn a vague idea into a working widget or a complete Bloc architecture in seconds. This article presents a curated collection of 10 advanced prompts organized into three categories: widgets, state management, and animations. Each prompt includes a task description, the exact prompt text, and a concrete result example.


1. Widgets: Building Reusable UI Components

Widgets are the building blocks of every Flutter app. The following prompts help you generate custom, responsive, and accessible widgets.

Prompt 1: Responsive Adaptive Card Widget

Task: Generate a Flutter widget that adapts its layout based on screen width—showing a horizontal card on tablets and a vertical card on phones.

Prompt:

Create a Flutter stateless widget called AdaptiveCard that accepts:
- title (String)
- subtitle (String)
- imageUrl (String)
- onTap (VoidCallback)

The widget should:
- Use LayoutBuilder to detect screen width
- Display a horizontal Row layout on screens >= 600px (tablets)
- Display a vertical Column layout on screens < 600px (phones)
- Include rounded corners, shadow, and a hero animation for the image
- Use Material 3 design tokens (Card, ListTile, etc.)
- Be fully accessible (Semantics, tooltip)
Provide the full Dart code.

Result Example:

import 'package:flutter/material.dart';

class AdaptiveCard extends StatelessWidget {
  final String title;
  final String subtitle;
  final String imageUrl;
  final VoidCallback onTap;

  const AdaptiveCard({
    super.key,
    required this.title,
    required this.subtitle,
    required this.imageUrl,
    required this.onTap,
  });

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        final isTablet = constraints.maxWidth >= 600;
        return Semantics(
          button: true,
          label: '$title: $subtitle',
          child: Card(
            elevation: 2,
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(16),
            ),
            child: InkWell(
              onTap: onTap,
              borderRadius: BorderRadius.circular(16),
              child: isTablet ? _buildHorizontalLayout() : _buildVerticalLayout(),
            ),
          ),
        );
      },
    );
  }

  Widget _buildHorizontalLayout() {
    return Row(
      children: [
        Hero(
          tag: 'image_$imageUrl',
          child: ClipRRect(
            borderRadius: BorderRadius.horizontal(left: Radius.circular(16)),
            child: Image.network(imageUrl, width: 150, height: 120, fit: BoxFit.cover),
          ),
        ),
        Expanded(
          child: ListTile(
            title: Text(title, style: TextStyle(fontWeight: FontWeight.bold)),
            subtitle: Text(subtitle),
          ),
        ),
      ],
    );
  }

  Widget _buildVerticalLayout() {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Hero(
          tag: 'image_$imageUrl',
          child: ClipRRect(
            borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
            child: Image.network(imageUrl, width: double.infinity, height: 180, fit: BoxFit.cover),
          ),
        ),
        Padding(
          padding: EdgeInsets.all(16),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(title, style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
              SizedBox(height: 8),
              Text(subtitle),
            ],
          ),
        ),
      ],
    );
  }
}

Prompt 2: Custom Form Builder with Validation

Task: Generate a form builder widget that dynamically creates form fields from a JSON configuration and validates them.

Prompt:

Create a Flutter stateful widget called DynamicFormBuilder that:
- Accepts a List<Map<String, dynamic>> configuration where each map has:
  - 'field': String (field name)
  - 'type': String (text, email, password, number, date)
  - 'label': String
  - 'required': bool
  - 'validators': List<String>? (e.g., ['min:3', 'max:50', 'regex:^[a-zA-Z]'])
- Generates corresponding TextFormField or specialized inputs (e.g., date picker)
- Validates fields on submission
- Returns a Map<String, dynamic> with field values
- Uses GlobalKey<FormState> for form management
- Includes a submit button and a callback onSubmit(Map<String, dynamic> values)
Provide the full implementation.

Result Example:

class DynamicFormBuilder extends StatefulWidget {
  final List<Map<String, dynamic>> fields;
  final Function(Map<String, dynamic>) onSubmit;

  const DynamicFormBuilder({super.key, required this.fields, required this.onSubmit});

  @override
  State<DynamicFormBuilder> createState() => _DynamicFormBuilderState();
}

class _DynamicFormBuilderState extends State<DynamicFormBuilder> {
  final _formKey = GlobalKey<FormState>();
  final Map<String, TextEditingController> _controllers = {};
  DateTime? _selectedDate;

  @override
  void initState() {
    super.initState();
    for (var field in widget.fields) {
      _controllers[field['field']] = TextEditingController();
    }
  }

  @override
  void dispose() {
    for (var controller in _controllers.values) {
      controller.dispose();
    }
    super.dispose();
  }

  String? _validateField(String fieldName, Map<String, dynamic> config, String? value) {
    if (config['required'] == true && (value == null || value.isEmpty)) {
      return '${config['label']} is required';
    }
    final validators = config['validators'] as List<String>?;
    if (validators != null && value != null) {
      for (var rule in validators) {
        if (rule.startsWith('min:')) {
          final min = int.parse(rule.split(':')[1]);
          if (value.length < min) return 'Minimum $min characters';
        }
        // Additional validators...
      }
    }
    return null;
  }

  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: Column(
        children: [
          ...widget.fields.map((field) => Padding(
            padding: EdgeInsets.symmetric(vertical: 8),
            child: TextFormField(
              controller: _controllers[field['field']],
              decoration: InputDecoration(labelText: field['label']),
              validator: (value) => _validateField(field['field'], field, value),
            ),
          )),
          ElevatedButton(
            onPressed: () {
              if (_formKey.currentState!.validate()) {
                final values = _controllers.map((key, controller) => MapEntry(key, controller.text));
                widget.onSubmit(values);
              }
            },
            child: Text('Submit'),
          ),
        ],
      ),
    );
  }
}

2. State Management: Bloc and Riverpod Patterns

State management is often the most debated topic in Flutter. These prompts generate production-ready patterns using Bloc and Riverpod.

Prompt 3: Complete Bloc with Freezed and Repository Pattern

Task: Generate a Bloc for user authentication using freezed for events and states, with a repository pattern.

Prompt:

Generate a complete authentication Bloc in Flutter using:
- flutter_bloc 8.5+
- freezed 2.5+ for events and states
- A repository class AuthRepository with methods:
  - Future<User> login(String email, String password)
  - Future<void> logout()
  - Future<User?> getCurrentUser()

The Bloc should handle:
- Login with loading, success, and error states
- Logout
- Check authentication status on app start
- Automatic token refresh (simulate)
Include the full code for:
- auth_event.dart (freezed)
- auth_state.dart (freezed)
- auth_bloc.dart
- auth_repository.dart (abstract and implementation)

Result Example (excerpt):

// auth_state.dart
@freezed
class AuthState with _$AuthState {
  const factory AuthState.initial() = _Initial;
  const factory AuthState.loading() = _Loading;
  const factory AuthState.authenticated(User user) = _Authenticated;
  const factory AuthState.unauthenticated(String? error) = _Unauthenticated;
}

// auth_event.dart
@freezed
class AuthEvent with _$AuthEvent {
  const factory AuthEvent.login(String email, String password) = _Login;
  const factory AuthEvent.logout() = _Logout;
  const factory AuthEvent.checkAuth() = _CheckAuth;
}

// auth_bloc.dart
class AuthBloc extends Bloc<AuthEvent, AuthState> {
  final AuthRepository _repository;

  AuthBloc(this._repository) : super(const AuthState.initial()) {
    on<AuthEvent.login>(_onLogin);
    on<AuthEvent.logout>(_onLogout);
    on<AuthEvent.checkAuth>(_onCheckAuth);
  }

  Future<void> _onLogin(AuthEvent.Login event, Emitter<AuthState> emit) async {
    emit(const AuthState.loading());
    try {
      final user = await _repository.login(event.email, event.password);
      emit(AuthState.authenticated(user));
    } catch (e) {
      emit(AuthState.unauthenticated(e.toString()));
    }
  }

  Future<void> _onLogout(AuthEvent.Logout event, Emitter<AuthState> emit) async {
    await _repository.logout();
    emit(const AuthState.unauthenticated(null));
  }

  Future<void> _onCheckAuth(AuthEvent.CheckAuth event, Emitter<AuthState> emit) async {
    final user = await _repository.getCurrentUser();
    if (user != null) {
      emit(AuthState.authenticated(user));
    } else {
      emit(const AuthState.unauthenticated(null));
    }
  }
}

Prompt 4: Riverpod AsyncNotifier with State Notifier

Task: Generate a Riverpod provider for a shopping cart using AsyncNotifier.

Prompt:

Create a Riverpod 2.5+ provider for a shopping cart using AsyncNotifier:
- CartState should contain:
  - List<CartItem> items
  - double totalPrice
  - bool isSyncing (for background sync)
- CartItem: {id, name, price, quantity}
- Methods:
  - addItem(CartItem)
  - removeItem(String id)
  - updateQuantity(String id, int quantity)
  - clearCart()
  - syncWithServer() (simulate network call)
- Use family provider if needed
- Include proper error handling and loading states
Generate the complete code for:
- cart_state.dart
- cart_provider.dart
- cart_notifier.dart

Result Example (excerpt):

// cart_notifier.dart
class CartNotifier extends AsyncNotifier<CartState> {
  @override
  Future<CartState> build() async {
    // Load initial cart from local storage or server
    return CartState(items: [], totalPrice: 0.0, isSyncing: false);
  }

  Future<void> addItem(CartItem item) async {
    state = AsyncData(state.value!.copyWith(
      items: [...state.value!.items, item],
      totalPrice: state.value!.totalPrice + item.price * item.quantity,
    ));
  }

  Future<void> removeItem(String id) async {
    final updatedItems = state.value!.items.where((item) => item.id != id).toList();
    final newTotal = updatedItems.fold<double>(0, (sum, item) => sum + item.price * item.quantity);
    state = AsyncData(state.value!.copyWith(items: updatedItems, totalPrice: newTotal));
  }

  Future<void> syncWithServer() async {
    state = AsyncData(state.value!.copyWith(isSyncing: true));
    try {
      await Future.delayed(Duration(seconds: 2)); // Simulate network
      // Sync logic...
      state = AsyncData(state.value!.copyWith(isSyncing: false));
    } catch (e) {
      state = AsyncError(e, StackTrace.current);
    }
  }
}

// cart_provider.dart
final cartProvider = AsyncNotifierProvider<CartNotifier, CartState>(
  CartNotifier.new,
);

3. Animations: From Simple to Complex

Animations in Flutter can be declarative with implicit animations or imperative with AnimationController. These prompts cover both.

Prompt 5: Staggered List Animation with AnimatedList

Task: Generate a staggered entrance animation for a list of items.

Prompt:

Create a Flutter widget that displays a list of items with a staggered slide-in animation:
- Each item slides in from the bottom with a slight delay
- The delay is proportional to the item's index (e.g., 100ms * index)
- Use AnimatedList or a custom approach with AnimationController
- Items should have a fade-in effect combined with slide
- The list should support dynamic addition/removal with animations
- Use Curve.easeOutCubic for smooth motion
Generate the complete widget code.

Result Example:

class StaggeredListView extends StatefulWidget {
  final List<Widget> items;
  const StaggeredListView({super.key, required this.items});

  @override
  State<StaggeredListView> createState() => _StaggeredListViewState();
}

class _StaggeredListViewState extends State<StaggeredListView>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late List<Animation<double>> _itemAnimations;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 800),
    );
    _itemAnimations = List.generate(widget.items.length, (index) {
      return Tween<double>(begin: 0.0, end: 1.0).animate(
        CurvedAnimation(
          parent: _controller,
          curve: Interval(
            index * 0.15,
            index * 0.15 + 0.5,
            curve: Curves.easeOutCubic,
          ),
        ),
      );
    });
    _controller.forward();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: widget.items.length,
      itemBuilder: (context, index) {
        return AnimatedBuilder(
          animation: _itemAnimations[index],
          builder: (context, child) {
            return Opacity(
              opacity: _itemAnimations[index].value,
              child: Transform.translate(
                offset: Offset(0, 50 * (1 - _itemAnimations[index].value)),
                child: child,
              ),
            );
          },
          child: widget.items[index],
        );
      },
    );
  }
}

Prompt 6: Custom Animated Bottom Navigation Bar

Task: Generate a bottom navigation bar with custom animation on tab switch.

Prompt:

Create a custom AnimatedBottomNavBar widget that:
- Has 4 tabs with icons and labels
- On tab switch, the selected icon scales up and changes color with a spring animation
- The background of the selected item slides smoothly
- Uses AnimatedContainer for the background and Transform.scale for the icon
- Supports Material 3 shapes (e.g., StadiumBorder)
- Has a notch for the FAB (FloatingActionButton) integration
Provide the complete implementation.

Result Example:

class AnimatedBottomNavBar extends StatefulWidget {
  final int currentIndex;
  final Function(int) onTap;
  const AnimatedBottomNavBar({super.key, required this.currentIndex, required this.onTap});

  @override
  State<AnimatedBottomNavBar> createState() => _AnimatedBottomNavBarState();
}

class _AnimatedBottomNavBarState extends State<AnimatedBottomNavBar>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _scaleAnimation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 300),
    );
    _scaleAnimation = CurvedAnimation(
      parent: _controller,
      curve: Curves.elasticOut,
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 70,
      decoration: BoxDecoration(
        color: Theme.of(context).colorScheme.surface,
        boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 10)],
      ),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        children: List.generate(4, (index) {
          final isSelected = widget.currentIndex == index;
          return GestureDetector(
            onTap: () {
              widget.onTap(index);
              _controller.forward(from: 0.0);
            },
            child: AnimatedContainer(
              duration: Duration(milliseconds: 200),
              padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
              decoration: BoxDecoration(
                color: isSelected ? Theme.of(context).colorScheme.primaryContainer : Colors.transparent,
                borderRadius: BorderRadius.circular(20),
              ),
              child: AnimatedBuilder(
                animation: _scaleAnimation,
                builder: (context, child) {
                  return Transform.scale(
                    scale: isSelected ? 1.0 + _scaleAnimation.value * 0.2 : 1.0,
                    child: Icon(
                      [Icons.home, Icons.search, Icons.favorite, Icons.person][index],
                      color: isSelected ? Theme.of(context).colorScheme.primary : Colors.grey,
                    ),
                  );
                },
              ),
            ),
          );
        }),
      ),
    );
  }
}

Prompt 7: Animated Gradient Background

Task: Generate a widget that animates between gradient colors continuously.

Prompt:

Create a Flutter widget called AnimatedGradientBackground that:
- Cycles through a list of colors (e.g., [Colors.blue, Colors.purple, Colors.pink])
- Uses AnimationController with a duration of 4 seconds per transition
- Interpolates between colors using Color.lerp
- Displays a child widget on top of the gradient (e.g., text or buttons)
- Uses a TweenSequence for smooth back-and-forth transitions
- Is efficient (doesn't rebuild on every frame unnecessarily)
Generate the complete code.

Result Example:

class AnimatedGradientBackground extends StatefulWidget {
  final Widget child;
  const AnimatedGradientBackground({super.key, required this.child});

  @override
  State<AnimatedGradientBackground> createState() => _AnimatedGradientBackgroundState();
}

class _AnimatedGradientBackgroundState extends State<AnimatedGradientBackground>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  final List<Color> _colors = [Colors.blue, Colors.purple, Colors.pink, Colors.teal];
  int _currentIndex = 0;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: Duration(seconds: 4),
    );
    _controller.addListener(() => setState(() {}));
    _controller.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        _currentIndex = (_currentIndex + 1) % _colors.length;
        _controller.reset();
        _controller.forward();
      }
    });
    _controller.forward();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final nextIndex = (_currentIndex + 1) % _colors.length;
    final currentColor = Color.lerp(_colors[_currentIndex], _colors[nextIndex], _controller.value)!;
    final nextColor = Color.lerp(_colors[nextIndex], _colors[(nextIndex + 1) % _colors.length], _controller.value)!;

    return Container(
      decoration: BoxDecoration(
        gradient: LinearGradient(
          begin: Alignment.topLeft,
          end: Alignment.bottomRight,
          colors: [currentColor, nextColor],
        ),
      ),
      child: widget.child,
    );
  }
}

4. Expert-Level Prompts: Combining Concepts

Prompt 8: Bloc + Animation Integration

Task: Generate a Flutter screen where a Bloc state change triggers an animation.

Prompt:

Create a Flutter screen that:
- Uses a CounterBloc (increment/decrement)
- When the counter value changes, a number on screen animates from the previous value to the new value using TweenAnimationBuilder
- The background color smoothly transitions between colors based on the counter parity (even = blue, odd = orange)
- Include a slide animation on the increment/decrement buttons
- Use RepaintBoundary for performance
Generate all files: counter_bloc.dart, counter_screen.dart, counter_event.dart, counter_state.dart

Result Example (excerpt):

class CounterScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) => CounterBloc(),
      child: BlocConsumer<CounterBloc, CounterState>(
        listener: (context, state) {
          // Trigger animation on change
        },
        builder: (context, state) {
          return Scaffold(
            body: Center(
              child: AnimatedContainer(
                duration: Duration(milliseconds: 500),
                color: state.count.isEven ? Colors.blue : Colors.orange,
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    TweenAnimationBuilder<int>(
                      tween: IntTween(begin: 0, end: state.count),
                      duration: Duration(milliseconds: 400),
                      builder: (context, value, child) {
                        return Text('$value', style: TextStyle(fontSize: 48));
                      },
                    ),
                    Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        FloatingActionButton(
                          onPressed: () => context.read<CounterBloc>().add(CounterEvent.decrement()),
                          child: Icon(Icons.remove),
                        ),
                        SizedBox(width: 20),
                        FloatingActionButton(
                          onPressed: () => context.read<CounterBloc>().add(CounterEvent.increment()),
                          child: Icon(Icons.add),
                        ),
                      ],
                    ),
                  ],
                ),
              ),
            ),
          );
        },
      ),
    );
  }
}

Prompt 9: Riverpod + Animated Switcher for Dark Mode

Task: Generate a dark mode toggle using Riverpod and AnimatedSwitcher.

Prompt:

Create a Flutter app with:
- A Riverpod StateProvider<bool> for dark mode
- An AnimatedSwitcher that fades between light and dark theme
- A settings screen with a switch that toggles the theme
- Persist the preference using SharedPreferences
- The theme change should be animated with a duration of 300ms
Generate the complete code for main.dart, theme_provider.dart, and settings_screen.dart

Result Example (excerpt):

// theme_provider.dart
final themeModeProvider = StateNotifierProvider<ThemeModeNotifier, ThemeMode>((ref) {
  return ThemeModeNotifier();
});

class ThemeModeNotifier extends StateNotifier<ThemeMode> {
  ThemeModeNotifier() : super(ThemeMode.light);

  void toggle() {
    state = state == ThemeMode.light ? ThemeMode.dark : ThemeMode.light;
  }
}

// main.dart
class MyApp extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final themeMode = ref.watch(themeModeProvider);
    return AnimatedBuilder(
      animation: themeMode,
      builder: (context, child) {
        return MaterialApp(
          themeMode: themeMode,
          theme: ThemeData.light(),
          darkTheme: ThemeData.dark(),
          home: SettingsScreen(),
        );
      },
    );
  }
}

Prompt 10: Production-Grade Animation with Rive

Task: Generate a Flutter widget that integrates a Rive animation and controls it via state.

Prompt:

Create a Flutter widget that:
- Uses the rive package (0.13+) to load a .riv file from assets
- Has two animation states: 'idle' and 'active'
- Switches between states based on a boolean isActive
- Uses RiveAnimationController to play specific animations
- Includes error handling for missing assets
- Is wrapped in a RepaintBoundary for performance
Provide the complete code.

Result Example:

class RiveStateMachineWidget extends StatefulWidget {
  final String riveAsset;
  final bool isActive;
  const RiveStateMachineWidget({super.key, required this.riveAsset, required this.isActive});

  @override
  State<RiveStateMachineWidget> createState() => _RiveStateMachineWidgetState();
}

class _RiveStateMachineWidgetState extends State<RiveStateMachineWidget> {
  Artboard? _artboard;
  StateMachineController? _controller;
  SMIInput<bool>? _activeInput;

  @override
  void initState() {
    super.initState();
    _loadRiveFile();
  }

  Future<void> _loadRiveFile() async {
    try {
      final file = await RiveFile.asset(widget.riveAsset);
      final artboard = file.mainArtboard;
      final controller = StateMachineController.fromArtboard(artboard, 'State Machine 1');
      if (controller != null) {
        artboard.addController(controller);
        _activeInput = controller.findInput<bool>('active');
        _controller = controller;
      }
      setState(() => _artboard = artboard);
    } catch (e) {
      // Handle error
    }
  }

  @override
  void didUpdateWidget(RiveStateMachineWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.isActive != oldWidget.isActive && _activeInput != null) {
      _activeInput!.value = widget.isActive;
    }
  }

  @override
  void dispose() {
    _controller?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return RepaintBoundary(
      child: _artboard != null
          ? Rive(artboard: _artboard!)
          : Center(child: CircularProgressIndicator()),
    );
  }
}

Conclusion

These 10 prompts demonstrate how AI-assisted development can accelerate Flutter app creation without sacrificing code quality. From responsive widgets to sophisticated state management with Bloc and Riverpod, and from simple list animations to Rive integration, each prompt is designed to be production-ready with minimal adjustments.

As of 2026, the Flutter ecosystem continues to grow. Tools like Dart Frog for backend, Drift for local databases, and the new Impeller rendering engine have made Flutter apps faster and more reliable. By mastering these prompts, you can reduce development time by up to 40% according to a 2025 survey by the Flutter team (source: flutter.dev).

Remember: the best prompts are specific, include constraints, and reference concrete packages and versions. Use them as a starting point, then customize to your project's needs. Happy coding!

← All posts

Comments