Flutter Widgets — what it is, widget tree and build

Author: IT Sectr Published: 2026-02-21 Reading time: 8 min

Flutter Widgets — the basic building blocks of the user interface in the Google Flutter framework, released in stable version 1.0 in December 2018. In Flutter, everything is a widget: from screens and buttons to padding and alignment. Widgets form a widget tree, which Flutter renders on its own Skia/Impeller engine, bypassing native platform components. According to Google I/O (2025), Flutter is used in over 500,000 apps on Google Play and the App Store. The Flutter architecture divides widgets into StatelessWidget (immutable) and StatefulWidget (with mutable state via a State object).

Key Takeaways

  • Flutter Widgets — all UI elements in Flutter: from screens and buttons to padding and alignment; the framework does not use native platform components.
  • StatelessWidget — a widget without mutable state; displays data passed through the constructor and does not change after build.
  • StatefulWidget — a widget with mutable state stored in a separate State object that survives rebuilds.
  • Widget Tree — a hierarchy of widgets describing the UI; Flutter transforms it into an Element Tree and Render Tree for rendering.
  • BuildContext — the build context providing access to the widget's position in the tree, theme, MediaQuery, and InheritedWidget.

What Are Flutter Widgets?

Flutter Widgets — all UI elements in the Flutter framework, implemented as immutable Dart classes. A widget is a configuration of a UI element: its size, color, position, and event handlers. Unlike Android (XML + View) or iOS (Storyboard + UIView), Flutter does not use native components — everything is drawn by the Skia engine (Android/iOS/Linux) or Impeller (iOS/macOS) on a Canvas. This ensures a consistent look across all platforms without platform discrepancies. Flutter ships with two sets of widgets: Material Widgets (Google Material Design 3 for Android/web) and Cupertino Widgets (Apple HIG for iOS/macOS). Developers can combine both sets in a single application. Each widget has a build() method that returns a child widget or a composition of them — this is how the widget tree is built.

The Three Flutter Trees

Flutter operates with three trees: Widget Tree (configuration, created by the developer), Element Tree (mapping a widget to its position in the tree, managed by the framework), and Render Tree (objects that compute layout and perform painting). A Widget creates an Element, which creates a RenderObject. An Element is reused when the configuration changes (rebuild) — this is Flutter's optimization mechanism: if the widget type and key match, the Element is not recreated, but updated with the new configuration. Understanding the three trees is key to optimizing Flutter application performance.

StatelessWidget: Immutable Widgets

StatelessWidget — a widget that does not store internal state. It receives parameters through the constructor and displays them without the ability to change after build. StatelessWidget is ideal for static elements: headers, icons, avatars, lists with fixed data. The build() method is called once at creation and when the parent widget changes. Since StatelessWidget has no state, it does not require a separate State object — build() is defined directly in the widget class.

dart
import 'package:flutter/material.dart';

class ProfileHeader extends StatelessWidget {
  final String name;
  final String avatarUrl;

  const ProfileHeader({super.key, required this.name, required this.avatarUrl});

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        CircleAvatar(
          radius: 24,
          backgroundImage: NetworkImage(avatarUrl),
        ),
        const SizedBox(width: 12),
        Text(
          name,
          style: const TextStyle(
            fontSize: 18,
            fontWeight: FontWeight.bold,
          ),
        ),
      ],
    );
  }
}

ProfileHeader — a StatelessWidget that accepts name and avatarUrl through the constructor. CircleAvatar displays the avatar, Text displays the name. The widget cannot change these values after creation. Using a const constructor (const ProfileHeader) allows Flutter to reuse the widget instance during parent rebuild — a micro-optimization recommended for all StatelessWidgets.

StatefulWidget: Widgets with State

StatefulWidget — a widget with mutable state stored in a separate State object. The StatefulWidget itself is immutable, like StatelessWidget. The mutable part is delegated to State: State is an object created once and survives multiple widget rebuilds. When the state changes (setState()), Flutter calls build() on the State, updating the UI. StatefulWidget is used for interactive elements: counters, text fields, checkboxes, animations, timers.

dart
import 'package:flutter/material.dart';

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

  @override
  State<LikeButton> createState() => _LikeButtonState();
}

class _LikeButtonState extends State<LikeButton> {
  bool liked = false;
  int count = 42;

  void _toggleLike() {
    setState(() {
      liked = !liked;
      count += liked ? 1 : -1;
    });
  }

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: _toggleLike,
      child: Text(liked ? '♥ $count' : '♡ $count'),
    );
  }
}

LikeButton — a StatefulWidget with the State class _LikeButtonState. The fields liked and count are state stored in State and are not reset on rebuild. setState() notifies Flutter that the UI needs to be rebuilt. Flutter does not recreate State on every parent rebuild — only when the configuration changes (key or widget type). At IT Sectr, we use StatelessWidget for display and StatefulWidget for interactive components, following the principle of minimizing StatefulWidget to the boundaries of mutation.

Widget Tree: From Widget to RenderObject

Widget Tree — a hierarchical structure of widgets describing the application UI. Flutter converts the Widget Tree into an Element Tree: each Widget creates an Element, which tracks its position in the tree and its connection to a RenderObject. The Render Tree performs layout (determining sizes and positions) and paint (rendering). The Element is a key optimization layer: if the widget type and key have not changed during a rebuild, the Element is reused, updating the configuration without recreating the RenderObject. This ensures Flutter's performance at high frame rates (60/120 FPS).

dart
import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Flutter Widgets')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Icon(Icons.flutter_dash, size: 80),
            const SizedBox(height: 16),
            const Text(
              'Hello, Flutter!',
              style: TextStyle(fontSize: 24),
            ),
          ],
        ),
      ),
    );
  }
}

HomeScreen widget tree: Scaffold → AppBar + Center → Column → [Icon, SizedBox, Text]. Scaffold is the root Material Design layout widget. Center centers its child Column. Column arranges children vertically with center alignment (mainAxisAlignment.center). Flutter computes layout top-down (constraints are passed from parent) and sizes bottom-up (sizedByParent or sizing from children).

BuildContext: Build Context and Tree Access

BuildContext — a descriptor of the widget's position in the Element Tree. Each build() method receives a BuildContext, through which the widget can access: MediaQuery (screen size, orientation, pixel density, safe area), Theme (MaterialThemeData or CupertinoThemeData), Navigator (navigation stack), ScaffoldMessenger (SnackBar), InheritedWidget (passing data down the tree). BuildContext should not be saved after build() completes or used outside of a synchronous context — this leads to errors like “Scaffold.of() called with a context that does not contain a Scaffold”. Instead of saving context, use the Builder widget or the InheritedWidget pattern for data access.

Key: Widget Identification in the Tree

Key — an optional constructor parameter for every widget (super.key). Key helps Flutter identify a widget when the order or number of items in a list changes. Without a key, Flutter compares widgets by type and position; when an item is added to the middle of a list without a key, Flutter reuses the Element for the wrong widget, causing state bugs (StatefulWidget) and animation issues. Use ValueKey (unique identifier) for lists, ObjectKey (based on data object), UniqueKey (guaranteed unique on every rebuild — for forced recreation).

Flutter Widget Catalog: Material, Cupertino, Layout

Flutter SDK (stable version 3.3+, Dart 3+) includes 200+ built-in widgets divided into categories: Layout (Row, Column, Stack, Container, Expanded, Flexible, Wrap, GridView, ListView, CustomScrollView), Material Design (Scaffold, AppBar, Card, Drawer, BottomNavigationBar, FloatingActionButton, TabBar, Chip, DataTable), Cupertino (CupertinoPageScaffold, CupertinoNavigationBar, CupertinoButton, CupertinoTabBar), Painting & Effects (Opacity, DecoratedBox, ClipRect, Transform, BackdropFilter, ShaderMask), Async (FutureBuilder, StreamBuilder) and Accessibility (Semantics, MergeSemantics, ExcludeSemantics). Layout widgets are the foundation of any Flutter UI: Row for horizontal sequences, Column for vertical, Stack for overlapping elements, Container with BoxDecoration for decoration.

CategoryKey WidgetsPurpose
LayoutRow, Column, Stack, Container, Expanded, FlexiblePositioning and sizing of child elements
MaterialScaffold, AppBar, Card, Drawer, FAB, TabBarMaterial Design 3 UI components
CupertinoCupertinoPageScaffold, CupertinoNavigationBar, CupertinoButtoniOS-styled components
PaintingOpacity, ClipRect, Transform, DecoratedBox, BackdropFilterVisual effects and clipping
AsyncFutureBuilder, StreamBuilderAsync data loading with UI states
AccessibilitySemantics, MergeSemanticsSemantic markup for screen readers

Frequently Asked Questions

How does StatelessWidget differ from StatefulWidget?

StatelessWidget has no internal state — all data is passed through the constructor and does not change. StatefulWidget stores mutable state in a separate State object that survives rebuilds. Changing the state calls setState(), and Flutter rebuilds the UI. Use StatelessWidget for immutable elements; use StatefulWidget for interactive ones.

What is BuildContext in Flutter?

BuildContext is a descriptor of the widget's position in the Element Tree, passed to the build() method. Through context, a widget accesses MediaQuery, Theme, Navigator, ScaffoldMessenger, and InheritedWidget. BuildContext should not be saved after build() completes. For data access, use the Builder widget or InheritedWidget.

Do you need to know Dart to work with Flutter?

Yes, Flutter uses Dart as its sole programming language. Dart is a statically typed language with null safety, sound null safety, extension methods, and pattern matching. To get started, basic Dart knowledge is sufficient: classes, functions, async/await, collections. Dart compiles to native code (AOT) via Dart Native and to JavaScript (for the web) via Dart2JS.

Is Flutter only for mobile platforms?

No, Flutter supports six platforms: iOS, Android, Web, macOS, Windows, Linux. Over 90% of code is shared across all platforms. Platform adaptations (camera, file system) are implemented through plugins or platform channels. For desktop, Flutter uses the Flutter Desktop windowing shell; for web, it uses the CanvasKit (WebGL) or HTML renderer.

How does Flutter achieve 60 FPS performance?

Flutter achieves 60/120 FPS through: its own 2D engine (Skia/Impeller), the absence of a native bridge (unlike React Native), three trees (Widget → Element → Render) with Element reuse when type/key remain unchanged, a layout algorithm with a unidirectional pass (constraints down, sizes up), and RepaintBoundary for isolating repainting of individual screen areas.

Summary

  • Flutter Widgets — all UI elements: from Scaffold to SizedBox; Flutter renders them itself on Skia/Impeller, without using native components.
  • StatelessWidget — a widget without state; build() returns UI based on immutable constructor parameters.
  • StatefulWidget — a widget with mutable state in State; setState() triggers UI rebuild.
  • Three Flutter Trees: Widget (configuration) → Element (position) → Render (layout + paint); Element is reused for performance.
  • BuildContext — the widget's position in the tree; access to MediaQuery, Theme, Navigator, InheritedWidget.
  • Material and Cupertino — two widget sets for Google Material Design 3 and Apple HIG respectively.
  • Key (ValueKey, ObjectKey, UniqueKey) — widget identification when order or quantity changes in a list.

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also