Introduction
Flutter has become one of the most popular frameworks for cross-platform mobile development, powering apps from Google Pay to Reflectly. With over 500,000 apps on the Play Store built with Flutter (source: Google I/O 2024), mastering this toolkit is essential for modern developers. However, writing efficient Flutter code—especially managing state and creating smooth animations—can be challenging. This collection of 16 copy-paste ready prompts will help you generate Flutter widgets, implement state management with Bloc and Riverpod, and craft stunning animations using your favorite AI assistant. Each prompt is designed for real-world use, with examples and code snippets you can adapt immediately.
1. Generate a Custom StatelessWidget
Task: Create a reusable stateless widget for a user profile card.
Prompt:
"""
Generate a Flutter StatelessWidget called 'UserProfileCard' that displays:
- A CircleAvatar with a placeholder icon
- User name in bold
- User email in grey
- A trailing IconButton for editing
Include proper padding, a Card wrapper, and a 'onEdit' callback parameter. Use Material 3 design.
"""
Example:
class UserProfileCard extends StatelessWidget {
final String name;
final String email;
final VoidCallback? onEdit;
const UserProfileCard({super.key, required this.name, required this.email, this.onEdit});
@override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: ListTile(
leading: const CircleAvatar(child: Icon(Icons.person)),
title: Text(name, style: Theme.of(context).textTheme.titleMedium),
subtitle: Text(email, style: Theme.of(context).textTheme.bodySmall?.copyWith(color: Colors.grey)),
trailing: IconButton(icon: const Icon(Icons.edit), onPressed: onEdit),
),
);
}
}
2. Create a StatefulWidget with AnimationController
Task: Build a widget that fades in its content when mounted.
Prompt:
"""
Write a Flutter StatefulWidget called 'FadeInWidget' that takes a child widget and a duration. Use AnimationController and FadeTransition to animate opacity from 0.0 to 1.0 over the given duration. Dispose the controller properly.
"""
Example:
class FadeInWidget extends StatefulWidget {
final Widget child;
final Duration duration;
const FadeInWidget({super.key, required this.child, this.duration = const Duration(milliseconds: 500)});
@override
State<FadeInWidget> 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 = CurvedAnimation(parent: _controller, curve: Curves.easeIn);
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return FadeTransition(opacity: _animation, child: widget.child);
}
}
3. Implement a Bloc for Counter App
Task: Set up a Bloc pattern for a simple counter.
Prompt:
"""
Create a Flutter Bloc for a counter app. Define:
- CounterCubit class with increment() and decrement() methods
- CounterState class with an int count
- A Screen that uses BlocProvider and BlocBuilder to display the count and two buttons
Use the flutter_bloc package (version 8+).
"""
Example:
class CounterCubit extends Cubit<int> {
CounterCubit() : super(0);
void increment() => emit(state + 1);
void decrement() => emit(state - 1);
}
class CounterScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => CounterCubit(),
child: Builder(
builder: (context) {
return Scaffold(
appBar: AppBar(title: const Text('Counter')),
body: Center(
child: BlocBuilder<CounterCubit, int>(
builder: (context, count) {
return Text('$count', style: const TextStyle(fontSize: 48));
},
),
),
floatingActionButton: Column(
mainAxisSize: MainAxisSize.min,
children: [
FloatingActionButton(onPressed: () => context.read<CounterCubit>().increment(), child: const Icon(Icons.add)),
const SizedBox(height: 8),
FloatingActionButton(onPressed: () => context.read<CounterCubit>().decrement(), child: const Icon(Icons.remove)),
],
),
);
},
),
);
}
}
4. Manage State with Riverpod
Task: Use Riverpod for a simple todo list.
Prompt:
"""
Write Flutter code using Riverpod (flutter_riverpod) that:
- Defines a StateNotifier for a list of strings (todos)
- Provides methods addTodo(String) and removeTodo(String)
- Renders a ListView with checkboxes to remove items
- Uses ConsumerWidget for the UI
"""
Example:
class TodoNotifier extends StateNotifier<List<String>> {
TodoNotifier() : super([]);
void addTodo(String todo) => state = [...state, todo];
void removeTodo(String todo) => state = state.where((t) => t != todo).toList();
}
final todoProvider = StateNotifierProvider<TodoNotifier, List<String>>((ref) => TodoNotifier());
class TodoScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final todos = ref.watch(todoProvider);
return Scaffold(
appBar: AppBar(title: const Text('Todos')),
body: ListView.builder(
itemCount: todos.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(todos[index]),
trailing: IconButton(
icon: const Icon(Icons.check),
onPressed: () => ref.read(todoProvider.notifier).removeTodo(todos[index]),
),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () => ref.read(todoProvider.notifier).addTodo('New Todo ${todos.length + 1}'),
child: const Icon(Icons.add),
),
);
}
}
5. Animate with AnimatedContainer
Task: Create a box that changes color and size on tap.
Prompt:
"""
Generate a Flutter widget that uses AnimatedContainer to toggle between two states: a small red box and a large blue box. Use a GestureDetector to trigger the change. Animate over 300ms.
"""
Example:
class AnimatedBox extends StatefulWidget {
@override
State<AnimatedBox> createState() => _AnimatedBoxState();
}
class _AnimatedBoxState extends State<AnimatedBox> {
bool _isExpanded = false;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => setState(() => _isExpanded = !_isExpanded),
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
width: _isExpanded ? 200 : 100,
height: _isExpanded ? 200 : 100,
color: _isExpanded ? Colors.blue : Colors.red,
child: Center(child: Text(_isExpanded ? 'Big' : 'Small', style: const TextStyle(color: Colors.white))),
),
);
}
}
6. Build a Staggered Animation
Task: Animate multiple elements with delays.
Prompt:
"""
Write Flutter code that shows three containers sliding in from the left with a 200ms delay between each. Use AnimationController and Transform.translate. The containers should have different colors.
"""
Example:
class StaggeredAnimation extends StatefulWidget {
@override
State<StaggeredAnimation> createState() => _StaggeredAnimationState();
}
class _StaggeredAnimationState extends State<StaggeredAnimation> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late List<Animation<Offset>> _animations;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 800));
_animations = List.generate(3, (i) {
return Tween<Offset>(begin: const Offset(-1, 0), end: Offset.zero).animate(
CurvedAnimation(parent: _controller, curve: Interval(i * 0.2, 1.0, curve: Curves.easeOut)),
);
});
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Column(
children: List.generate(3, (i) {
return Transform.translate(
offset: _animations[i].value * 200,
child: Container(
margin: const EdgeInsets.all(8),
width: 100,
height: 100,
color: i == 0 ? Colors.red : (i == 1 ? Colors.green : Colors.blue),
),
);
}),
);
},
);
}
}
7. Hero Animation Between Screens
Task: Implement a shared element transition.
Prompt:
"""
Create two Flutter screens connected by a Hero animation. Screen A shows an Image.asset with a Hero tag 'profile_pic'. Screen B shows the same image enlarged. Use Navigator.push with MaterialPageRoute.
"""
Example:
class ScreenA extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: GestureDetector(
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const ScreenB())),
child: Hero(tag: 'profile_pic', child: Image.asset('assets/profile.jpg', width: 100)),
),
),
);
}
}
class ScreenB extends StatelessWidget {
const ScreenB({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Hero(tag: 'profile_pic', child: Image.asset('assets/profile.jpg', width: 300)),
),
);
}
}
8. Custom Paint with Animation
Task: Draw a moving circle using CustomPainter.
Prompt:
"""
Write Flutter code that draws a red circle moving horizontally across the screen using CustomPainter and an AnimationController. The circle should bounce at edges. Use RepaintBoundary for performance.
"""
Example:
class MovingCircle extends StatefulWidget {
@override
State<MovingCircle> createState() => _MovingCircleState();
}
class _MovingCircleState extends State<MovingCircle> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: const Duration(seconds: 2));
_animation = Tween<double>(begin: 0, end: 300).animate(CurvedAnimation(parent: _controller, curve: Curves.easeInOut))
..addListener(() => setState(() {}));
_controller.repeat(reverse: true);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return RepaintBoundary(
child: CustomPaint(
size: const Size(400, 400),
painter: CirclePainter(_animation.value),
),
);
}
}
class CirclePainter extends CustomPainter {
final double x;
CirclePainter(this.x);
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = Colors.red..style = PaintingStyle.fill;
canvas.drawCircle(Offset(x, size.height / 2), 20, paint);
}
@override
bool shouldRepaint(covariant CirclePainter oldDelegate) => oldDelegate.x != x;
}
9. Use Provider for Theme Toggle
Task: Implement dark/light theme switching with Provider.
Prompt:
"""
Generate Flutter code that uses the provider package to toggle between light and dark themes. Define a ThemeProvider that extends ChangeNotifier with a boolean 'isDark'. Use MaterialApp with ThemeMode. Include a button to toggle.
"""
Example:
class ThemeProvider extends ChangeNotifier {
bool _isDark = false;
bool get isDark => _isDark;
ThemeMode get themeMode => _isDark ? ThemeMode.dark : ThemeMode.light;
void toggle() {
_isDark = !_isDark;
notifyListeners();
}
}
void main() {
runApp(ChangeNotifierProvider(create: (_) => ThemeProvider(), child: const MyApp()));
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<ThemeProvider>(
builder: (context, themeProvider, _) {
return MaterialApp(
themeMode: themeProvider.themeMode,
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
home: Scaffold(
appBar: AppBar(title: const Text('Theme Toggle')),
body: Center(
child: ElevatedButton(
onPressed: () => context.read<ThemeProvider>().toggle(),
child: const Text('Toggle Theme'),
),
),
),
);
},
);
}
}
10. Infinite List with ScrollController
Task: Load more items when scrolling to the bottom.
Prompt:
"""
Write Flutter code for an infinite scrolling list. Use ScrollController to detect when the user scrolls to the bottom (within 100 pixels), then add 10 more dummy items to the list. Display item index in each ListTile.
"""
Example:
class InfiniteList extends StatefulWidget {
@override
State<InfiniteList> createState() => _InfiniteListState();
}
class _InfiniteListState extends State<InfiniteList> {
final List<int> _items = List.generate(20, (i) => i);
final ScrollController _controller = ScrollController();
bool _isLoading = false;
@override
void initState() {
super.initState();
_controller.addListener(() {
if (_controller.position.pixels >= _controller.position.maxScrollExtent - 100 && !_isLoading) {
_loadMore();
}
});
}
Future<void> _loadMore() async {
setState(() => _isLoading = true);
await Future.delayed(const Duration(seconds: 1));
setState(() {
_items.addAll(List.generate(10, (i) => _items.length + i));
_isLoading = false;
});
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ListView.builder(
controller: _controller,
itemCount: _items.length + (_isLoading ? 1 : 0),
itemBuilder: (context, index) {
if (index >= _items.length) {
return const Center(child: CircularProgressIndicator());
}
return ListTile(title: Text('Item ${_items[index]}'));
},
);
}
}
11. Form Validation with Bloc
Task: Validate a login form using Bloc.
Prompt:
"""
Create a Flutter Bloc for login form validation. Define:
- LoginCubit with fields email and password
- Methods to update email and password with validation (email must contain @, password min 6 chars)
- A state with error messages and form status (valid/invalid)
- UI that shows errors and a disabled button when invalid
"""
Example:
class LoginCubit extends Cubit<LoginState> {
LoginCubit() : super(LoginState());
void updateEmail(String email) {
final newState = state.copyWith(email: email, emailError: email.contains('@') ? null : 'Invalid email');
emit(newState.copyWith(isValid: newState.emailError == null && newState.passwordError == null));
}
void updatePassword(String password) {
final newState = state.copyWith(password: password, passwordError: password.length >= 6 ? null : 'Too short');
emit(newState.copyWith(isValid: newState.emailError == null && newState.passwordError == null));
}
}
class LoginState {
final String email;
final String password;
final String? emailError;
final String? passwordError;
final bool isValid;
LoginState({this.email = '', this.password = '', this.emailError, this.passwordError, this.isValid = false});
LoginState copyWith({String? email, String? password, String? emailError, String? passwordError, bool? isValid}) {
return LoginState(
email: email ?? this.email,
password: password ?? this.password,
emailError: emailError,
passwordError: passwordError,
isValid: isValid ?? this.isValid,
);
}
}
12. AnimatedList with Add/Remove
Task: Create a list with animated insertions and deletions.
Prompt:
"""
Write Flutter code using AnimatedList. Provide a button to add a new item at the top (animated) and swipe to delete an item (animated remove). Use a GlobalKey
"""
Example:
class AnimatedListScreen extends StatefulWidget {
@override
State<AnimatedListScreen> createState() => _AnimatedListScreenState();
}
class _AnimatedListScreenState extends State<AnimatedListScreen> {
final GlobalKey<AnimatedListState> _listKey = GlobalKey<AnimatedListState>();
final List<String> _items = ['Item 1', 'Item 2', 'Item 3'];
int _counter = 4;
void _addItem() {
_items.insert(0, 'Item $_counter');
_listKey.currentState!.insertItem(0, duration: const Duration(milliseconds: 300));
_counter++;
}
void _removeItem(int index) {
final removedItem = _items.removeAt(index);
_listKey.currentState!.removeItem(
index,
(context, animation) => SizeTransition(
sizeFactor: animation,
child: ListTile(title: Text(removedItem)),
),
duration: const Duration(milliseconds: 300),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Animated List')),
body: AnimatedList(
key: _listKey,
initialItemCount: _items.length,
itemBuilder: (context, index, animation) {
return SizeTransition(
sizeFactor: animation,
child: ListTile(
title: Text(_items[index]),
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () => _removeItem(index),
),
),
);
},
),
floatingActionButton: FloatingActionButton(onPressed: _addItem, child: const Icon(Icons.add)),
);
}
}
13. TweenAnimationBuilder for Color Transition
Task: Smoothly transition between two colors.
Prompt:
"""
Use TweenAnimationBuilder to create a widget that transitions from red to blue over 2 seconds when a button is pressed. The widget should be a Container with width 200 and height 200.
"""
Example:
class ColorTransition extends StatefulWidget {
@override
State<ColorTransition> createState() => _ColorTransitionState();
}
class _ColorTransitionState extends State<ColorTransition> {
Color _targetColor = Colors.red;
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TweenAnimationBuilder<Color?>(
tween: ColorTween(begin: Colors.red, end: _targetColor),
duration: const Duration(seconds: 2),
builder: (context, color, child) {
return Container(width: 200, height: 200, color: color);
},
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () => setState(() => _targetColor = _targetColor == Colors.red ? Colors.blue : Colors.red),
child: const Text('Toggle Color'),
),
],
);
}
}
14. Riverpod with Async Data
Task: Fetch and display data from a fake API.
Prompt:
"""
Write Flutter code using Riverpod that:
- Defines an AsyncNotifierProvider that fetches a list of strings from a fake API (simulate with Future.delayed)
- Shows a loading indicator, error message, or ListView based on state
- Use AsyncValue.when() for clean UI
"""
Example:
class DataNotifier extends AsyncNotifier<List<String>> {
@override
Future<List<String>> build() async {
await Future.delayed(const Duration(seconds: 2));
return ['Apple', 'Banana', 'Cherry'];
}
}
final dataProvider = AsyncNotifierProvider<DataNotifier, List<String>>(() => DataNotifier());
class DataScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final asyncData = ref.watch(dataProvider);
return Scaffold(
appBar: AppBar(title: const Text('Async Data')),
body: asyncData.when(
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, stack) => Center(child: Text('Error: $error')),
data: (items) => ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) => ListTile(title: Text(items[index])),
),
),
);
}
}
15. Implicit Animations with AnimatedOpacity
Task: Toggle visibility with a fade animation.
Prompt:
"""
Create a Flutter widget that uses AnimatedOpacity to show/hide a text widget when a button is pressed. The animation should last 500ms. Use a boolean flag.
"""
Example:
class FadeToggle extends StatefulWidget {
@override
State<FadeToggle> createState() => _FadeToggleState();
}
class _FadeToggleState extends State<FadeToggle> {
bool _visible = true;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AnimatedOpacity(
opacity: _visible ? 1.0 : 0.0,
duration: const Duration(milliseconds: 500),
child: const Text('Hello, Flutter!', style: TextStyle(fontSize: 24)),
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () => setState(() => _visible = !_visible),
child: Text(_visible ? 'Hide' : 'Show'),
),
],
),
),
);
}
}
16. Multi-Bloc Communication
Task: Synchronize two Blocs (counter and logger).
Prompt:
"""
Write Flutter code with two Blocs: CounterCubit (int) and LoggerCubit (list of strings). When CounterCubit emits a new state, LoggerCubit should listen and add a log entry. Use BlocListener to react to changes. Display both states on the screen.
"""
Example:
class CounterCubit extends Cubit<int> {
CounterCubit() : super(0);
void increment() => emit(state + 1);
}
class LoggerCubit extends Cubit<List<String>> {
LoggerCubit() : super([]);
void log(String message) => emit([...state, message]);
}
class MultiBlocScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(create: (_) => CounterCubit()),
BlocProvider(create: (_) => LoggerCubit()),
],
child: Builder(
builder: (context) {
return BlocListener<CounterCubit, int>(
listener: (context, count) {
context.read<LoggerCubit>().log('Counter changed to $count');
},
child: Scaffold(
appBar: AppBar(title: const Text('Multi Bloc')),
body: Column(
children: [
BlocBuilder<CounterCubit, int>(
builder: (context, count) => Text('Count: $count', style: const TextStyle(fontSize: 48)),
),
BlocBuilder<LoggerCubit, List<String>>(
builder: (context, logs) {
return Expanded(
child: ListView.builder(
itemCount: logs.length,
itemBuilder: (context, index) => ListTile(title: Text(logs[index])),
),
);
},
),
FloatingActionButton(
onPressed: () => context.read<CounterCubit>().increment(),
child: const Icon(Icons.add),
),
],
),
),
);
},
),
);
}
}
Conclusion
These 16 prompts cover the essential Flutter patterns—from basic widgets to advanced state management with Bloc and Riverpod, and from simple animations to complex staggered effects. By using these prompts with your AI assistant, you can accelerate your development process and ensure your code follows best practices. Copy, paste, and adapt them to your projects. For further learning, explore the official Flutter documentation at flutter.dev and the Bloc library documentation at bloc-library.dev.
Happy coding!
Comments