The core choice
Use StatelessWidget for UI that does not manage changing data. Use StatefulWidget when the widget must remember something that can change while the app runs.
Your widget’s build() method describes the UI. In a stateful widget, the State class holds the changing values and calls setState() when they change.
Think in widgets
In Flutter, almost everything on screen is a widget. Text, buttons, rows, columns, padding, and alignment are all widgets.
You build larger screens by composing smaller widgets. Some widgets take one child. Others take many children.
Pick the type
Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.
0 of 4 done
Small counter screen
This screen remembers one number. The button changes the number, so it needs StatefulWidget and setState().
import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(home: DemoCounterScreen()));
}
class DemoCounterScreen extends StatefulWidget {
const DemoCounterScreen({super.key});
@override
State<DemoCounterScreen> createState() => _DemoCounterScreenState();
}
class _DemoCounterScreenState extends State<DemoCounterScreen> {
int likes = 0;
void addLike() {
setState(() {
likes = likes + 1;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Campus Demo')),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text('Project likes'),
Text('$likes', style: const TextStyle(fontSize: 40)),
ElevatedButton(
onPressed: addLike,
child: const Text('Add like'),
),
],
),
),
);
}
}Split static parts
If a screen feels long, extract a static part into a StatelessWidget. For example, a project title card can receive text through its constructor and just display it.
This makes your repo easier for a recruiter to read. It also makes your final app less messy when you add navigation, API data, and storage later.
Common beginner mistake
Do not change a state variable without setState(). Flutter may not redraw the screen, so your value changes in memory but the UI still looks old.
Also avoid making every widget stateful. If nothing changes inside that widget, stateless is simpler and clearer.
Static widget example
This card has no changing value inside it. It only displays what it receives, so it should be stateless.
class ProjectCard extends StatelessWidget {
const ProjectCard({
super.key,
required this.name,
required this.tagline,
});
final String name;
final String tagline;
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(name, style: const TextStyle(fontSize: 20)),
Text(tagline),
],
),
),
);
}
}Do this now
Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.
0 of 5 done