Widgets and Layout

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
                └── Text

StatelessWidget 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/3

Wrap (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 const constructors whenever possible — they improve performance by preventing unnecessary rebuilds.

Next: State Management