Dart Tutorial

Flutter Lesson 6 of 83 2 min read

Flutter Widgets Explained: The Widget Tree

Learn what a widget is in Flutter, how widgets nest to form a tree, what BuildContext is, and how to compose your own widgets.

On this page

A widget is a description of part of the user interface. Widgets are small and are meant to be combined. A screen is built by nesting them.

Widgets nest to form a tree #

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Widget tree')),
        body: const Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              Icon(Icons.flutter_dash, size: 64, color: Colors.blue),
              SizedBox(height: 12),
              Text('Hello!'),
            ],
          ),
        ),
      ),
    ),
  );
}
MaterialApp
└── Scaffold
    ├── AppBar
    │   └── Text
    └── Center
        └── Column
            ├── Icon
            ├── SizedBox
            └── Text

child and children #

  • A widget that wraps one widget takes child:. Examples: Center, Padding, Container.
  • A widget that arranges several takes children:, a list. Examples: Column, Row, Stack, ListView.

Kinds of widget #

KindExamples
Shows somethingText, Icon, Image
Lays out other widgetsRow, Column, Stack, Padding, Center
Responds to inputElevatedButton, TextField, GestureDetector
Structures a pageScaffold, AppBar, Drawer
Does something invisibleTheme, MediaQuery, Navigator

Even padding and centring are widgets. There are no layout properties on Text; you wrap it in a Padding or a Center.

Composition: build your own #

You make new widgets by combining existing ones in a class.

import 'package:flutter/material.dart';

class ProfileHeader extends StatelessWidget {
  const ProfileHeader({super.key, required this.name, required this.role});

  final String name;
  final String role;

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        CircleAvatar(child: Text(name[0])),
        const SizedBox(width: 12),
        Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(name, style: Theme.of(context).textTheme.titleMedium),
            Text(role),
          ],
        ),
      ],
    );
  }
}

void main() {
  runApp(
    const MaterialApp(
      home: Scaffold(
        body: Padding(
          padding: EdgeInsets.all(24),
          child: ProfileHeader(name: 'Asha Rai', role: 'Flutter developer'),
        ),
      ),
    ),
  );
}

ProfileHeader can now be used anywhere, like any built-in widget. Whenever a build method gets long, pull part of it out into its own widget class.

BuildContext #

Every build method receives a BuildContext. It is the widget’s location in the tree. You use it to look upwards for things provided by ancestors:

final theme = Theme.of(context);          // nearest theme
final size = MediaQuery.sizeOf(context);  // screen size
Navigator.of(context).pop();              // nearest navigator

Widgets are cheap and immutable #

A widget’s fields are final. To change what is on screen you do not modify a widget. Flutter calls build again and gets new widgets, then updates only what differs on screen. Creating widgets is fast, so rebuilding is normal.

Try it yourself #

Create a PriceTag widget that takes a label and a price and shows them in a Row. Use it three times inside a Column.

Practise in the playground Updated by Santosh Adhikari