Lesson 16 of 51 beginner

Everything is a Widget

The Building Blocks of Every Flutter App

Open interactive version (quiz + challenge)

Real-world analogy

In Flutter, widgets are like LEGO bricks -- everything visible on screen is built from widgets snapped together. A Text widget is a 1x1 brick. A Column widget is a baseplate that holds bricks vertically. A Container is a box with color and size. Your entire app is just one giant LEGO structure made of smaller pieces. In team_mvp_kit, the screen you see is a tree of widgets -- MaterialApp at the top, Scaffold below it, then columns, rows, buttons, and text all the way down to every pixel.

What is it?

In Flutter, everything on screen is a widget. A widget is an immutable description of a piece of UI. Widgets are organized in a tree structure where parent widgets contain child widgets. When data changes, Flutter rebuilds the widget tree, compares it to the previous version, and efficiently updates only the parts that changed. This declarative approach means you describe what the UI should look like for a given state, and Flutter figures out how to make it happen.

Real-world relevance

In team_mvp_kit, the entire app is a widget tree. MaterialApp is the root. go_router provides navigation widgets. Each screen is a StatelessWidget or StatefulWidget. BlocBuilder widgets listen to BLoC state and rebuild their subtree when state changes. Every button, text field, list item, and icon is a widget. Understanding the widget tree is essential because it determines how data flows down (via constructors) and events flow up (via callbacks). Clean Architecture in team_mvp_kit keeps business logic out of widgets, making the widget layer a pure UI description.

Key points

Code example

import 'package:flutter/material.dart';

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Widget Demo',
      theme: ThemeData(
        colorSchemeSeed: Colors.blue,
        useMaterial3: true,
      ),
      home: const HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home'),
      ),
      body: const Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Icon(Icons.flutter_dash, size: 64),
            SizedBox(height: 16),
            Text('Everything is a Widget!'),
          ],
        ),
      ),
    );
  }
}

Line-by-line walkthrough

  1. 1. Import the Flutter material design library
  2. 2. Define MyApp as a StatelessWidget -- the root of our app
  3. 3. Add const constructor with super.key for widget identity
  4. 4. Override build method which receives BuildContext
  5. 5. Return MaterialApp widget -- this is the root of the widget tree
  6. 6. Set the app title to Widget Demo
  7. 7. Configure the theme with a blue color scheme and Material 3
  8. 8. Set HomeScreen as the home route -- the first screen users see
  9. 9. Define HomeScreen as a StatelessWidget with const constructor
  10. 10. Override build to describe the home screen's UI
  11. 11. Return Scaffold -- provides basic app structure with app bar and body
  12. 12. Create an AppBar with a Text title widget
  13. 13. Set the body to a Center widget which centers its child
  14. 14. Inside Center, use a Column to stack children vertically
  15. 15. Set mainAxisAlignment to center so children are vertically centered
  16. 16. Add a Flutter Dash icon with size 64
  17. 17. Add a SizedBox with height 16 for spacing between icon and text
  18. 18. Add a Text widget displaying our message

Spot the bug

class MyWidget extends StatelessWidget {
  String title = 'Hello';

  @override
  Widget build(BuildContext context) {
    return Text(title);
  }
}
Need a hint?
Widgets are supposed to be immutable. What is wrong with how title is declared?
Show answer
The title property is not final, which violates widget immutability. StatelessWidget properties must be final because widgets are rebuilt rather than mutated. Fix: change to final String title = 'Hello'; or better yet, accept it as a constructor parameter: final String title; MyWidget({required this.title});

Explain like I'm 5

Think of building a house out of blocks. Every single piece is a block (widget). The walls are blocks, the windows are blocks, even the door handle is a block. You cannot change a block once you place it -- but you can knock it down and put a different one in its place super fast. That is exactly what Flutter does. Your whole app is made of these tiny blocks stacked inside each other. When something changes, Flutter quickly swaps out only the blocks that are different, like a speed-builder rebuilding part of a LEGO house in the blink of an eye.

Fun fact

Flutter's widget rebuilding is incredibly fast because widgets are just lightweight Dart objects. The Flutter team benchmarked creating 10,000 widget objects and it took less than a millisecond. The real work happens in the RenderObject tree, which is only updated when actual visual changes are detected. This is why Flutter can hit 60fps (or even 120fps) consistently.

Hands-on challenge

Create a widget tree on paper (or in code) for a simple profile card. It should have: a circular avatar image at the top, a name in bold below it, an email in grey below that, and a row of three icon buttons (call, email, message) at the bottom. Think about which widgets you need: Column, Row, CircleAvatar, Text, IconButton, SizedBox for spacing. Then implement it as a StatelessWidget.

More resources

Open interactive version (quiz + challenge) ← Back to course: Flutter & Dart