In Flutter, everything is a widget. The UI is built by composing widgets into a tree structure. Understanding widgets is the most important Flutter concept.
Widget Tree
Code
MaterialApp
└── Scaffold
├── AppBar (title: Text)
└── Body
└── Column
├── Text ('Hello')
├── Image
└── Row
├── Icon
└── TextStatelessWidget vs StatefulWidget
Dart
// StatelessWidget — immutable, no internal state
class Greeting extends StatelessWidget {
final String name;
const Greeting({super.key, required this.name});
@override
Widget build(BuildContext context) {
return Text('Hello, $name!');
}
}
// StatefulWidget — mutable state, rebuilds on change
class Counter extends StatefulWidget {
const Counter({super.key});
@override
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int _count = 0;
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Count: $_count'),
ElevatedButton(
onPressed: () {
setState(() { // Triggers rebuild
_count++;
});
},
child: const Text('Increment'),
),
],
);
}
}Layout Widgets
Row and Column (Linear)
Dart
// Row — horizontal layout
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(Icons.star, color: Colors.amber),
Icon(Icons.star, color: Colors.amber),
Icon(Icons.star, color: Colors.amber),
],
)
// Column — vertical layout
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text('Title', style: TextStyle(fontSize: 24)),
SizedBox(height: 16),
Text('Subtitle'),
SizedBox(height: 8),
Text('Body text...'),
],
)Stack (Overlay)
Dart
Stack(
children: [
Image.network('https://example.com/photo.jpg'),
Positioned(
bottom: 16,
left: 16,
child: Container(
padding: EdgeInsets.all(8),
color: Colors.black54,
child: Text('Photo Title', style: TextStyle(color: Colors.white)),
),
),
],
)Flex and Expanded
Dart
Row(
children: [
Expanded(
flex: 2,
child: Container(color: Colors.red, height: 100),
),
Expanded(
flex: 1,
child: Container(color: Colors.blue, height: 100),
),
],
)
// Red takes 2/3 width, blue takes 1/3Wrap (Flow Layout)
Dart
Wrap(
spacing: 8,
runSpacing: 8,
children: [
Chip(label: Text('Dart')),
Chip(label: Text('Flutter')),
Chip(label: Text('Firebase')),
Chip(label: Text('Material Design')),
],
)Common Widgets
Dart
// Text with styling
Text(
'Hello World',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.blue,
letterSpacing: 1.2,
),
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
)
// Container (styling + layout in one)
Container(
margin: EdgeInsets.all(16),
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 8)],
),
child: Text('Styled card'),
)
// Image
Image.asset('assets/logo.png', width: 200)
Image.network(url, fit: BoxFit.cover)
Image.network(url, errorBuilder: (ctx, error, stack) => Icon(Icons.error))
// Icon
Icon(Icons.favorite, color: Colors.red, size: 32)
// TextField (Input)
TextField(
decoration: InputDecoration(
labelText: 'Email',
hintText: 'you@example.com',
prefixIcon: Icon(Icons.email),
border: OutlineInputBorder(),
),
keyboardType: TextInputType.emailAddress,
onChanged: (value) => print(value),
)
// ElevatedButton
ElevatedButton.icon(
onPressed: () => print('Pressed'),
icon: Icon(Icons.send),
label: Text('Submit'),
)Scrolling
Dart
// SingleChildScrollView — scroll once
SingleChildScrollView(
child: Column(children: [...]),
)
// ListView — scrollable list
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) => ListTile(
title: Text(items[index].title),
trailing: Icon(Icons.chevron_right),
),
)
// GridView — scrollable grid
GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
),
itemCount: items.length,
itemBuilder: (context, index) => Card(child: Center(child: Text(items[index]))),
)
// CustomScrollView — mixed scrollable slivers
CustomScrollView(
slivers: [
SliverAppBar(title: Text('Title'), floating: true),
SliverList(delegate: SliverChildBuilderDelegate(
(context, index) => ListTile(title: Text('Item $index')),
childCount: 50,
)),
],
)Responsive Layout
Dart
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth > 800) {
return _buildWideLayout(); // Desktop/tablet
} else {
return _buildNarrowLayout(); // Phone
}
},
)
// Or use OrientationBuilder
OrientationBuilder(
builder: (context, orientation) {
return orientation == Orientation.portrait
? _buildPortrait()
: _buildLandscape();
},
)💡 Tip: Prefer
constconstructors whenever possible — they improve performance by preventing unnecessary rebuilds.
Next: State Management