🏠 Home / Hub

Flutter 02 — Core Widgets

Everything in Flutter is a widget. Learn the most essential widgets you will use in every app.

1. Widget Philosophy

In Flutter, everything is a widget — text, buttons, images, layouts, padding, animations, even the app itself. The UI is described as a widget tree: a hierarchy of nested widget objects.

TypeDescriptionExample
StatelessWidgetNo mutable state; rebuilds only when its parent provides new dataText, Icon, Image
StatefulWidgetHolds mutable state; calls setState() to rebuildCheckbox, Slider, custom counter
InheritedWidgetPasses data down the tree efficientlyTheme, MediaQuery
Prefer StatelessWidget whenever possible — it is simpler. Only use StatefulWidget when the widget needs to track changing data over time.

2. StatelessWidget Skeleton

A StatelessWidget has one required method: build(). It receives a BuildContext and returns a Widget. It is rebuilt every time its parent rebuilds and passes new constructor arguments.

import 'package:flutter/material.dart';

// The widget class (immutable — all fields must be final)
class GreetingCard extends StatelessWidget {
  final String name;
  final String message;

  // Constructor — use const for performance
  const GreetingCard({
    super.key,
    required this.name,
    required this.message,
  });

  @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, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 8),
            Text(message),
          ],
        ),
      ),
    );
  }
}

// Usage
GreetingCard(name: 'Alice', message: 'Welcome back!')
Always add const to constructors and widget instantiations when the values are compile-time constants. This lets Flutter skip rebuilding those subtrees.

3. StatefulWidget + setState

A StatefulWidget is split into two classes: the widget class (immutable) and the State class (mutable). The State class holds the data and the build() method.

class CounterWidget extends StatefulWidget {
  const CounterWidget({super.key});

  @override
  State<CounterWidget> createState() => _CounterWidgetState();
}

// The State class — can hold mutable variables
class _CounterWidgetState extends State<CounterWidget> {
  int _count = 0;     // underscore prefix = private
  bool _isActive = false;

  // setState() triggers a rebuild of this widget
  void _increment() {
    setState(() {
      _count++;
      _isActive = _count > 0;
    });
  }

  void _reset() {
    setState(() {
      _count = 0;
      _isActive = false;
    });
  }

  // initState — called once when the widget is first created
  @override
  void initState() {
    super.initState();
    // initialize things here (e.g., load data, start animations)
  }

  // dispose — called when the widget is removed from the tree
  @override
  void dispose() {
    // clean up controllers, streams, etc.
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Count: $_count', style: const TextStyle(fontSize: 32)),
        Text(_isActive ? 'Active' : 'Zero'),
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(onPressed: _increment, child: const Text('+')),
            const SizedBox(width: 12),
            ElevatedButton(onPressed: _reset, child: const Text('Reset')),
          ],
        ),
      ],
    );
  }
}

4. Text Widget

The Text widget displays a string. Style it with TextStyle.

// Basic text
const Text('Hello, Flutter!')

// With style
Text(
  'Hello, Flutter!',
  style: TextStyle(
    fontSize: 24,
    fontWeight: FontWeight.bold,
    fontStyle: FontStyle.italic,
    color: Colors.blue,
    letterSpacing: 1.5,
    height: 1.4,           // line height multiplier
    decoration: TextDecoration.underline,
  ),
)

// Overflow handling (when text is too long)
Text(
  'This is a very long text that might overflow the container width...',
  maxLines: 2,
  overflow: TextOverflow.ellipsis,   // show "..." at end
  softWrap: true,
)

// RichText — mix multiple styles in one text block
RichText(
  text: TextSpan(
    style: const TextStyle(color: Colors.black, fontSize: 16),
    children: [
      const TextSpan(text: 'Hello, '),
      TextSpan(
        text: 'Flutter',
        style: const TextStyle(
          color: Colors.blue,
          fontWeight: FontWeight.bold,
        ),
      ),
      const TextSpan(text: '!'),
    ],
  ),
)

// SelectableText — user can copy the text
const SelectableText('Copy me!')

5. Container Widget

Container is the most flexible single-child layout widget. It combines sizing, padding, margin, color, decoration, and child alignment.

Container(
  // Size
  width: 200,
  height: 100,

  // Spacing
  padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
  margin: const EdgeInsets.all(12),

  // Alignment of child inside
  alignment: Alignment.center,

  // Simple color (cannot use with decoration)
  // color: Colors.blue,

  // Decoration — for border, borderRadius, gradient, shadow
  decoration: BoxDecoration(
    color: Colors.blue.shade100,
    borderRadius: BorderRadius.circular(12),
    border: Border.all(color: Colors.blue, width: 2),
    boxShadow: [
      BoxShadow(
        color: Colors.black26,
        blurRadius: 8,
        offset: const Offset(2, 4),
      ),
    ],
    gradient: const LinearGradient(
      colors: [Colors.blue, Colors.purple],
      begin: Alignment.topLeft,
      end: Alignment.bottomRight,
    ),
  ),

  child: const Text('Styled Container'),
)

// Shorthand EdgeInsets constructors
EdgeInsets.all(16)                        // all four sides = 16
EdgeInsets.symmetric(vertical: 8, horizontal: 16)
EdgeInsets.only(top: 8, left: 12)
EdgeInsets.fromLTRB(12, 8, 12, 8)        // left, top, right, bottom
You cannot set both color and decoration on the same Container. Put the color inside BoxDecoration when you also need other decoration properties.

6. Image Widget

// Image from asset (declare in pubspec.yaml first)
// pubspec.yaml:
//   flutter:
//     assets:
//       - assets/images/photo.jpg

Image.asset(
  'assets/images/photo.jpg',
  width: 300,
  height: 200,
  fit: BoxFit.cover,    // how image fills the space
)

// Image from URL
Image.network(
  'https://picsum.photos/300/200',
  width: 300,
  height: 200,
  fit: BoxFit.cover,
  loadingBuilder: (context, child, progress) {
    if (progress == null) return child;
    return const Center(child: CircularProgressIndicator());
  },
  errorBuilder: (context, error, stackTrace) {
    return const Icon(Icons.broken_image, size: 64);
  },
)

// BoxFit options
// BoxFit.cover    — fills box, may crop
// BoxFit.contain  — fits inside box, may letterbox
// BoxFit.fill     — stretches to fill (may distort)
// BoxFit.fitWidth — fits width, may crop height
// BoxFit.fitHeight — fits height, may crop width
// BoxFit.none     — original size, centered

// CircleAvatar — circular image commonly used for profile pictures
CircleAvatar(
  radius: 40,
  backgroundImage: NetworkImage('https://picsum.photos/80'),
  backgroundColor: Colors.grey.shade200,
)

7. Button Widgets

// ElevatedButton — filled, raised button (primary actions)
ElevatedButton(
  onPressed: () => print('Tapped!'),
  style: ElevatedButton.styleFrom(
    backgroundColor: Colors.blue,
    foregroundColor: Colors.white,
    padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
  ),
  child: const Text('Submit'),
)

// ElevatedButton with icon
ElevatedButton.icon(
  onPressed: () {},
  icon: const Icon(Icons.send),
  label: const Text('Send'),
)

// TextButton — no background (secondary, less prominent)
TextButton(
  onPressed: () {},
  child: const Text('Cancel'),
)

// OutlinedButton — border only, no fill
OutlinedButton(
  onPressed: () {},
  style: OutlinedButton.styleFrom(
    side: const BorderSide(color: Colors.blue, width: 2),
  ),
  child: const Text('Learn More'),
)

// IconButton — circular tap area around an icon
IconButton(
  onPressed: () {},
  icon: const Icon(Icons.favorite),
  color: Colors.red,
  iconSize: 32,
  tooltip: 'Add to favorites',
)

// FloatingActionButton — the circular button usually at bottom-right
FloatingActionButton(
  onPressed: () {},
  tooltip: 'Add',
  child: const Icon(Icons.add),
)

// Small FAB
FloatingActionButton.small(
  onPressed: () {},
  child: const Icon(Icons.add),
)

// Disable a button by passing null to onPressed
ElevatedButton(
  onPressed: null,     // null = disabled, greyed out
  child: const Text('Disabled'),
)

8. Icon Widget

// Basic icon
const Icon(Icons.home)

// With size and color
Icon(
  Icons.favorite,
  size: 48,
  color: Colors.red,
)

// Common Material Icons
Icons.home          Icons.settings      Icons.person
Icons.search        Icons.menu          Icons.close
Icons.add           Icons.edit          Icons.delete
Icons.check         Icons.arrow_back    Icons.arrow_forward
Icons.email         Icons.phone         Icons.lock
Icons.visibility    Icons.star          Icons.share
Icons.camera_alt    Icons.photo         Icons.attach_file
Icons.notifications Icons.shopping_cart Icons.logout

// Icon with semantic label (accessibility)
Icon(
  Icons.warning,
  color: Colors.orange,
  semanticLabel: 'Warning',
)

// Icon inside a Circle
CircleAvatar(
  backgroundColor: Colors.blue,
  child: const Icon(Icons.person, color: Colors.white),
)

9. Common Widget Quick Reference

WidgetPurposeKey Properties
TextDisplay stringstyle, maxLines, overflow
ContainerBox with size/color/decowidth, height, padding, margin, decoration
Image.assetLocal imagefit, width, height
Image.networkRemote imagefit, loadingBuilder, errorBuilder
ElevatedButtonPrimary action buttononPressed, style, child
TextButtonLow-emphasis buttononPressed, child
OutlinedButtonOutlined buttononPressed, style, child
IconButtonTappable icononPressed, icon, color, iconSize
IconMaterial icon glyphIcons.xxx, size, color
CircleAvatarCircular image/iconradius, backgroundImage, backgroundColor
CardElevated surfaceelevation, shape, child
ChipLabel pilllabel, avatar, onDeleted
DividerHorizontal separatorthickness, color, indent
CircularProgressIndicatorLoading spinnervalue, color, strokeWidth
LinearProgressIndicatorProgress barvalue, backgroundColor, color
TooltipHover/long-press hintmessage, child
SelectableTextCopyable textstyle, maxLines
RichTextMulti-style texttext: TextSpan

📌 Study Checklist