FLUTTER: Flutter Widgets

Widgets are the fundamental building blocks of every Flutter UI. From displaying text and images to handling user input and arranging layouts, every element on screen is a widget. Understanding the full widget taxonomy unlocks the ability to build any UI Flutter can express.

Flutter takes inspiration from React in its widget model — the UI is a function of the current data state. When state changes, Flutter efficiently rebuilds only the affected widgets. Mastering widgets means mastering Flutter itself.


What Is a Widget?

A widget is an immutable description of part of the user interface. Widgets form a hierarchical widget tree, where parent widgets contain child widgets. Flutter traverses this tree to determine what to render on screen.

md
Widget Tree Example
-------------------
MaterialApp
  └── Scaffold
        ├── AppBar
        │     └── Text ("My App")
        └── Body
              └── Column
                    ├── Text ("Hello")
                    ├── Icon (Icons.star)
                    └── ElevatedButton
                              └── Text ("Press Me")

Widgets serve two purposes:

  • Display data — render text, images, icons, and custom shapes
  • Capture user input — respond to taps, swipes, text entry, and gestures

Widget Types

md
Widgets
├── Stateless   (immutable, no internal state)
│     Examples: Text, Icon, Image
└── Stateful    (maintains mutable state over time)
      Examples: Checkbox, Slider, TextField

Stateless widgets are rebuilt only when their parent rebuilds them, making them efficient for static content. Stateful widgets track their own mutable data and can rebuild themselves independently when that data changes.


Basic Widgets

Basic widgets build the core visible content of an app.

Text

The most fundamental display widget. Renders a string with optional styling.

dart
Text(
  'Hello, Flutter!',
  style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
)

Image

Loads images from the network, assets, or files.

dart
// From network URL
Image.network('https://example.com/photo.jpg')

// From local assets
Image.asset('assets/images/logo.png')

Icon

Renders a Material Design icon from the built-in icon set.

dart
Icon(Icons.star, color: Colors.amber, size: 32)

ElevatedButton

A raised, interactive button with an elevation shadow.

dart
ElevatedButton(
  onPressed: () {
    print('Button tapped!');
  },
  child: const Text('Press Me'),
)


Layout Widgets

Layout widgets arrange other widgets on screen. They do not render visible content themselves — they define structure and position.

Column

Arranges children vertically from top to bottom.

dart
Column(
  mainAxisAlignment: MainAxisAlignment.center,
  crossAxisAlignment: CrossAxisAlignment.start,
  children: [
    Text('First'),
    Text('Second'),
    Text('Third'),
  ],
)

Row

Arranges children horizontally from left to right.

dart
Row(
  mainAxisAlignment: MainAxisAlignment.spaceBetween,
  children: [
    Text('Left'),
    Text('Center'),
    Text('Right'),
  ],
)

Container

A versatile box that applies padding, margins, borders, background color, and size constraints to a single child.

dart
Container(
  padding: const EdgeInsets.all(16.0),
  margin: const EdgeInsets.symmetric(vertical: 8.0),
  color: Colors.blue,
  child: const Text('Inside a container'),
)

Stack

Overlaps children on top of one another, positioned relative to the stack's edges.

dart
Stack(
  children: [
    Container(color: Colors.blue, width: 200, height: 200),
    Positioned(
      top: 10,
      left: 10,
      child: Container(color: Colors.red, width: 80, height: 80),
    ),
  ],
)


Input Widgets

Input widgets receive data from the user.

TextField

Allows users to enter text. Use a TextEditingController to read the entered value programmatically.

dart
final TextEditingController _controller = TextEditingController();

TextField(
  controller: _controller,
  decoration: const InputDecoration(
    labelText: 'Enter your name',
    border: OutlineInputBorder(),
  ),
)

Checkbox

Allows the user to select or deselect a boolean option.

dart
bool _checked = false;

Checkbox(
  value: _checked,
  onChanged: (bool? value) {
    setState(() => _checked = value ?? false);
  },
)

Radio

Allows selection of a single option from a group.

dart
int _selectedValue = 1;

Radio<int>(
  value: 1,
  groupValue: _selectedValue,
  onChanged: (int? value) {
    setState(() => _selectedValue = value!);
  },
)

Switch

Toggles between on and off states.

dart
bool _isSwitched = false;

Switch(
  value: _isSwitched,
  onChanged: (bool value) {
    setState(() => _isSwitched = value);
  },
)


Form and Validation

Wrap multiple TextField widgets inside a Form widget to enable group validation and submission.

dart
final _formKey = GlobalKey<FormState>();

Form(
  key: _formKey,
  child: Column(
    children: [
      TextFormField(
        validator: (value) {
          if (value == null || value.isEmpty) {
            return 'This field is required';
          }
          return null; // Valid
        },
        decoration: const InputDecoration(labelText: 'Email'),
      ),
      ElevatedButton(
        onPressed: () {
          if (_formKey.currentState!.validate()) {
            // Form is valid, proceed
          }
        },
        child: const Text('Submit'),
      ),
    ],
  ),
)

Validators are functions that return an error message String if the input is invalid, or null if it is valid.


Button Widgets

WidgetDescription
ElevatedButtonRaised button with shadow — for primary actions
TextButtonFlat button without border — for subtle actions
OutlinedButtonButton with a visible border — for secondary actions
IconButtonTappable icon without text — for toolbar actions

dart
ElevatedButton(onPressed: () {}, child: const Text('Primary'))
TextButton(onPressed: () {}, child: const Text('Secondary'))
OutlinedButton(onPressed: () {}, child: const Text('Outlined'))
IconButton(icon: const Icon(Icons.share), onPressed: () {})


List Widgets

ListView

Renders a scrollable list of widgets. Use ListView.builder for large or dynamic datasets — it creates items lazily as they scroll into view.

dart
ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    return ListTile(
      leading: const Icon(Icons.label),
      title: Text(items[index]),
      subtitle: Text('Item $index'),
      onTap: () => print('Tapped: ${items[index]}'),
    );
  },
)


Scaffold

The Scaffold widget provides the standard Material Design page structure. It occupies the entire screen and offers slots for the most common layout components.

dart
Scaffold(
  appBar: AppBar(title: const Text('Page Title')),
  drawer: Drawer(child: ListView(...)),
  body: Center(child: Text('Main content')),
  floatingActionButton: FloatingActionButton(
    onPressed: () {},
    child: const Icon(Icons.add),
  ),
  bottomNavigationBar: BottomNavigationBar(items: [...]),
)

Scaffold SlotPurpose
appBarTop navigation bar with title and actions
bodyMain content area
drawerSlide-in side navigation menu
floatingActionButtonProminent action button
bottomNavigationBarTab navigation at the bottom
snackBarTemporary notification messages

Responsive UI

Flutter provides tools to build UIs that adapt to different screen sizes and orientations.

dart
// Detect screen orientation
OrientationBuilder(
  builder: (context, orientation) {
    return GridView.count(
      crossAxisCount: orientation == Orientation.portrait ? 2 : 4,
      children: itemWidgets,
    );
  },
)

// Use MediaQuery for screen dimensions
final size = MediaQuery.of(context).size;
final width = size.width;
final height = size.height;

Size hierarchy for child widgets:

md
Fixed size    -->  SizedBox(width: 100, height: 50)
Expands       -->  Expanded(child: ...)
Flexible      -->  Flexible(child: ..., flex: 2)
Dynamic       -->  Container (adapts to content)

Best practices for responsive Flutter UIs:

  • Use Expanded and Flexible instead of hardcoded sizes where possible
  • Apply MediaQuery to adapt layouts to screen dimensions
  • Test on multiple device sizes and orientations
  • Break large, complex widgets into smaller, focused components