Introduction
Flutter has become one of the most popular frameworks for building cross-platform applications, and its expressive widget system, combined with powerful state management solutions and animation capabilities, allows developers to create stunning and performant apps. However, even experienced Flutter developers can get stuck when choosing the right widget for a specific layout, deciding between Bloc and Riverpod for state management, or crafting smooth animations. That's where well-structured prompts come in—they help you think through problems systematically and generate high-quality code or architecture decisions faster.
In this article, we present a curated collection of 10 practical prompts for Flutter development, covering widgets, state management, and animations. Each prompt is designed to be copy-paste ready, with a clear explanation of the task it solves and a real-world example. Whether you're a beginner or a seasoned pro, these prompts will save you time and improve your code quality.
Section 1: Widgets — Building Blocks of Flutter UIs
Widgets are the core of every Flutter application. From basic containers to complex slivers, choosing the right widget can make or break your UI performance and maintainability. The following prompts help you design layouts, handle responsive design, and build custom widgets.
Prompt 1: Responsive Layout with LayoutBuilder and MediaQuery
Task: Create a responsive layout that adapts to different screen sizes without hardcoding breakpoints.
Prompt:
Act as a Flutter expert. Write a widget that uses LayoutBuilder and MediaQuery to build a responsive card layout. On screens wider than 600px, display two cards side by side; on smaller screens, stack them vertically. Use a Column with Expanded for the small screen and a Row for the large screen. Include padding and shadows for aesthetics.
Explanation: This prompt forces you to think about adaptive layouts using Flutter's built-in tools. LayoutBuilder gives you the parent constraints, while MediaQuery provides device-level information. The combination is ideal for responsive UIs.
Usage Example:
class ResponsiveCardGrid extends StatelessWidget {
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth > 600) {
return Row(
children: [
Expanded(child: Card(child: Text('Card 1'))),
SizedBox(width: 8),
Expanded(child: Card(child: Text('Card 2'))),
],
);
} else {
return Column(
children: [
Card(child: Text('Card 1')),
SizedBox(height: 8),
Card(child: Text('Card 2')),
],
);
}
},
);
}
}
Prompt 2: Custom Sliver AppBar with Collapsing Effect
Task: Build a custom sliver app bar that collapses as the user scrolls, with a background image and overlay.
Prompt:
Act as a Flutter UI specialist. Write a CustomScrollView with a SliverAppBar that has an expanded height of 200 pixels, a flexibleSpace containing a background image with a gradient overlay, and a title that fades in when collapsed. Use the stretch effect and ensure the app bar snaps on scroll.
Explanation: Sliver widgets are essential for advanced scrolling experiences. This prompt covers SliverAppBar properties like expandedHeight, flexibleSpace, and collapsedHeight, as well as the stretch property for modern iOS-style scrolling.
Usage Example:
CustomScrollView(
slivers: [
SliverAppBar(
expandedHeight: 200,
flexibleSpace: FlexibleSpaceBar(
background: Image.network('https://example.com/image.jpg', fit: BoxFit.cover),
title: Text('My Title'),
),
stretch: true,
snap: true,
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => ListTile(title: Text('Item $index')),
childCount: 50,
),
),
],
)
Section 2: State Management — Bloc vs Riverpod
State management is arguably the most debated topic in Flutter. Two of the most popular solutions are Bloc (Business Logic Component) and Riverpod. Both have strong communities and are production-ready, but they differ in philosophy and usage patterns. Below are prompts that help you implement common state management scenarios with both libraries.
Prompt 3: Counter App with Bloc
Task: Implement a simple counter app using the Bloc pattern.
Prompt:
Act as a Flutter Bloc expert. Create a counter app with increment and decrement buttons. Define a CounterCubit that manages the state as an integer. Use BlocProvider to provide the cubit to the widget tree, and BlocBuilder to rebuild the UI when the state changes. Ensure the cubit is closed properly.
Explanation: Bloc relies on events and states; Cubit is a simpler version without events. This prompt teaches the fundamentals of BlocProvider and BlocBuilder.
Usage Example:
class CounterCubit extends Cubit<int> {
CounterCubit() : super(0);
void increment() => emit(state + 1);
void decrement() => emit(state - 1);
}
// In main.dart
BlocProvider(
create: (context) => CounterCubit(),
child: MaterialApp(home: CounterPage()),
);
class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Bloc Counter')),
body: BlocBuilder<CounterCubit, int>(
builder: (context, count) => Center(child: Text('$count', style: TextStyle(fontSize: 48))),
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(onPressed: () => context.read<CounterCubit>().increment(), child: Icon(Icons.add)),
SizedBox(height: 8),
FloatingActionButton(onPressed: () => context.read<CounterCubit>().decrement(), child: Icon(Icons.remove)),
],
),
);
}
}
Prompt 4: Async Data Fetching with Riverpod
Task: Fetch and display a list of users from a REST API using Riverpod.
Prompt:
Act as a Flutter Riverpod specialist. Write a provider that fetches a list of users from 'https://jsonplaceholder.typicode.com/users' using the http package. Use FutureProvider.family to allow passing a limit parameter. Display the data in a ListView using ConsumerWidget. Handle loading and error states gracefully.
Explanation: Riverpod's FutureProvider simplifies async state management. The .family modifier allows parameterized providers, which is great for filtering or pagination.
Usage Example:
final userListProvider = FutureProvider.family<List<User>, int>((ref, limit) async {
final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/users?_limit=$limit'));
if (response.statusCode == 200) {
final List<dynamic> data = jsonDecode(response.body);
return data.map((json) => User.fromJson(json)).toList();
} else {
throw Exception('Failed to load users');
}
});
class UserList extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final usersAsync = ref.watch(userListProvider(10));
return usersAsync.when(
data: (users) => ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) => ListTile(title: Text(users[index].name)),
),
loading: () => CircularProgressIndicator(),
error: (error, stack) => Text('Error: $error'),
);
}
}
Prompt 5: Comparing Bloc and Riverpod for Complex Forms
Task: Build a multi-step form with validation using both Bloc and Riverpod.
Prompt:
Act as a Flutter architect. Design a two-step registration form (name, email, password) with validation using Bloc. Then refactor the same logic using Riverpod with StateNotifierProvider. Compare the boilerplate, testability, and scalability of both approaches in a short analysis.
Explanation: This prompt encourages a practical comparison of the two state management solutions for a real-world scenario. Bloc requires separate event and state classes, while Riverpod's StateNotifier is more concise but less formalized.
Analysis:
- Bloc: More boilerplate (events, states, bloc class) but enforces strict separation of concerns. Great for large teams.
- Riverpod: Less boilerplate, better support for dependency injection, and built-in code generation for immutable state. Preferred for solo or small projects.
Section 3: Animations — From Implicit to Explicit
Flutter's animation system is both powerful and flexible. From simple implicit animations to complex explicit ones with custom curves and controllers, the following prompts cover the spectrum.
Prompt 6: Implicit Animation with AnimatedContainer
Task: Create a toggleable square that changes color and size smoothly.
Prompt:
Act as a Flutter animation expert. Write a StatefulWidget that toggles a boolean on tap. Use AnimatedContainer to animate the width (100 to 200), height (100 to 200), and color (blue to red) with a duration of 500 milliseconds and a Curves.easeInOut curve.
Explanation: AnimatedContainer is the easiest way to add smooth transitions between different property states without managing controllers manually.
Usage Example:
class AnimatedSquare extends StatefulWidget {
@override
_AnimatedSquareState createState() => _AnimatedSquareState();
}
class _AnimatedSquareState extends State<AnimatedSquare> {
bool _isBig = false;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => setState(() => _isBig = !_isBig),
child: AnimatedContainer(
duration: Duration(milliseconds: 500),
curve: Curves.easeInOut,
width: _isBig ? 200 : 100,
height: _isBig ? 200 : 100,
color: _isBig ? Colors.red : Colors.blue,
),
);
}
}
Prompt 7: Explicit Animation with AnimationController
Task: Build a custom loading spinner that rotates continuously.
Prompt:
Act as a Flutter animation specialist. Create a custom widget that rotates a CircularProgressIndicator 360 degrees using an AnimationController with a duration of 2 seconds and a repeating TickerMode. Use with TickerProviderStateMixin and dispose the controller properly.
Explanation: Explicit animations give you full control over timing and interpolation. This prompt demonstrates the correct lifecycle management of AnimationController.
Usage Example:
class RotatingSpinner extends StatefulWidget {
@override
_RotatingSpinnerState createState() => _RotatingSpinnerState();
}
class _RotatingSpinnerState extends State<RotatingSpinner> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: Duration(seconds: 2));
_animation = Tween<double>(begin: 0, end: 360).animate(_controller);
_controller.repeat();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animation,
builder: (context, child) => Transform.rotate(
angle: _animation.value * (3.14159 / 180),
child: CircularProgressIndicator(),
),
);
}
}
Prompt 8: Hero Animation Between Screens
Task: Animate an image from a list item to a detail page.
Prompt:
Act as a Flutter UI developer. Implement a Hero animation where tapping on a ListTile with an avatar image navigates to a detail page that displays the image larger. Use the same tag ('hero_avatar') on both the source and destination widgets. Ensure the transition is smooth with a default duration.
Explanation: Hero animations are perfect for shared element transitions. The tag must be unique across the app and consistent on both screens.
Usage Example:
// List screen
ListTile(
leading: Hero(tag: 'hero_avatar', child: CircleAvatar(backgroundImage: NetworkImage(url))),
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => DetailScreen(url: url))),
);
// Detail screen
Scaffold(
appBar: AppBar(),
body: Center(child: Hero(tag: 'hero_avatar', child: Image.network(widget.url, width: 300, height: 300))),
);
Prompt 9: Staggered Animation for List Items
Task: Animate list items appearing one by one with a staggered delay.
Prompt:
Act as a Flutter animation expert. Create a list of 10 items that animate in from the bottom with an opacity transition, each delayed by 100 milliseconds relative to the previous item. Use StaggeredAnimation with a single AnimationController and multiple Interval tweens.
Explanation: Staggered animations create visually appealing reveals. This prompt teaches how to use Interval to split the animation timeline.
Usage Example:
class StaggeredList extends StatefulWidget {
@override
_StaggeredListState createState() => _StaggeredListState();
}
class _StaggeredListState extends State<StaggeredList> with SingleTickerProviderStateMixin {
late AnimationController _controller;
List<Animation<double>> _opacities = [];
List<Animation<Offset>> _offsets = [];
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: Duration(seconds: 2));
for (int i = 0; i < 10; i++) {
final start = i * 0.1;
_opacities.add(Tween<double>(begin: 0, end: 1).animate(CurvedAnimation(
parent: _controller,
curve: Interval(start, start + 0.5, curve: Curves.easeIn),
)));
_offsets.add(Tween<Offset>(begin: Offset(0, 0.5), end: Offset.zero).animate(CurvedAnimation(
parent: _controller,
curve: Interval(start, start + 0.5, curve: Curves.easeOut),
)));
}
_controller.forward();
}
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: 10,
itemBuilder: (context, index) => AnimatedBuilder(
animation: _opacities[index],
builder: (context, child) => Opacity(
opacity: _opacities[index].value,
child: SlideTransition(
position: _offsets[index],
child: child,
),
),
child: ListTile(title: Text('Item $index')),
),
);
}
}
Prompt 10: Custom Painter Animation
Task: Animate a custom drawn shape (e.g., a waving flag) using CustomPainter and AnimationController.
Prompt:
Act as a Flutter graphics expert. Write a CustomPainter that draws a sine wave representing a flag waving. Use an AnimationController to animate the phase of the sine wave over 3 seconds. The canvas should be filled with a gradient. Use shouldRepaint to optimize repaints.
Explanation: CustomPainter combined with animations allows limitless creative effects. This prompt covers Canvas, Paint, and animation-driven drawing.
Usage Example:
class FlagPainter extends CustomPainter {
final double phase;
FlagPainter(this.phase);
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..shader = LinearGradient(colors: [Colors.blue, Colors.red]).createShader(Rect.fromLTWH(0, 0, size.width, size.height))
..strokeWidth = 4
..style = PaintingStyle.stroke;
final path = Path();
for (double x = 0; x <= size.width; x++) {
double y = size.height / 2 + 50 * sin((x / 20) + phase);
if (x == 0) path.moveTo(x, y);
else path.lineTo(x, y);
}
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant FlagPainter oldDelegate) => oldDelegate.phase != phase;
}
Conclusion
These 10 prompts cover the most common and powerful aspects of Flutter development: widgets for layout, state management with Bloc and Riverpod, and animations from simple to advanced. By using these prompts as a starting point, you can quickly prototype features, compare architectural choices, and learn best practices. The key is to adapt them to your specific project needs—experiment with different curves, providers, and widget combinations to find what works best for your app.
Remember that the Flutter ecosystem evolves rapidly. Stay updated with the official documentation and community packages, and always test your implementations on real devices. Happy coding!
Comments