Widget — ce este, tipuri și compoziție în Flutter

Autor: IT Sectr Publicat: 2026-06-30 Timp de citire: 10 min

Widget — este conceptul central al framework-ului Flutter, care descrie configurația unui element de interfață utilizator. Fiecare componentă vizuală, de la un buton până la o animație complexă, este un Widget. Spre deosebire de alte framework-uri, unde UI este descris prin fișiere XML separate sau este desenat imperativ, Flutter construiește interfața prin compoziția Widget — combinarea elementelor mici indivizibile într-un arbore ierarhic. Conform Flutter Documentation (2025), biblioteca Flutter SDK include peste 260 de Widget încorporate, acoperind Material Design, Cupertino și stiluri personalizate.

Principalele idei

  • Widget — blocul de construcție de bază al UI în Flutter, care descrie configurația elementului.
  • Compoziția — UI se construiește prin înnestarea Widgeturilor unul în altul, nu prin moștenire.
  • StatelessWidget — widget care nu se schimbă după randare (text, iconiță, spațiere).
  • StatefulWidget — widget cu stare modificabilă (formulare, animații, liste cu date).
  • Element tree — Flutter menține trei arbori: Widget, Element și RenderObject.

Ce este Widget în Flutter

Widget în Flutter este o descriere imuabilă (immutable) a unei părți a interfeței utilizator. Fiecare Widget conține proprietăți de configurare: dimensiune, culoare, poziție, text, gestionari de evenimente și Widgeturi copil. Widgeturile în sine nu sunt randate direct — ele sunt rețete (blueprints) pe baza cărora Flutter Engine creează un RenderObject — un obiect grafic real pe ecran.

Filozofia Flutter spune: „Everything is a Widget”. Aceasta înseamnă că nu doar elementele vizibile (Text, Image, Button) sunt Widgeturi, ci și blocurile structurale (Padding, Center, Column, Stack), blocurile comportamentale (GestureDetector, AnimatedBuilder) și chiar aplicația în sine (MaterialApp, CupertinoApp). Această abordare asigură uniformitate: orice element al ecranului poate fi combinat cu orice altul printr-o simplă înnestare.

Conform datelor Google I/O 2024 — Flutter Widgets Deep Dive, o aplicație medie pe Flutter conține între 200 și 1500 de Widgeturi într-un moment dat. În ciuda acestui număr, Flutter menține 60 FPS chiar și pe dispozitive bugetare datorită optimizărilor la nivel de motor C++ Skia/Impeller. Widgeturile sunt obiecte ușoare (40–80 de octeți fiecare), deci crearea lor nu este un blocaj de performanță.

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

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: Scaffold(
        body: Center(
          child: Text('Salut, Flutter!'),
        ),
      ),
    );
  }
}

Cei trei arbori Flutter: Widget, Element și RenderObject

Pentru a înțelege cum funcționează Widget, trebuie să cunoașteți arhitectura Flutter formată din trei arbori interconectați. Primul — Widget tree — descrie configurația UI. Este un arbore ușor care poate fi complet reconstruit la fiecare cadru (garbage collector elimină Widgeturile vechi și creează altele noi). Widgeturile sunt imuabile (immutable): dacă s-a schimbat culoarea textului — se creează un nou Widget Text cu noua culoare, cel vechi este eliminat.

Al doilea arbore — Element tree — este veriga de legătură între Widget și RenderObject. Elementul conține o referință către Widget (configurație) și către RenderObject (randare). Când Widget se schimbă, Flutter compară noul Widget cu Elementul vechi și decide: să actualizeze RenderObject-ul existent (dacă Widget este de același tip) sau să creeze unul nou (dacă tipul Widget s-a schimbat). Acest proces se numește Reconciliation și este analogul React Virtual DOM.

Al treilea arbore — RenderObject tree — este responsabil pentru randarea reală pe ecran. RenderObject conține dimensiuni concrete, poziții și metode de paint. Flutter Engine (C++ Skia sau Impeller) parcurge RenderObject tree și randeează fiecare nod. RenderObject tree este cel mai greu arbore, de aceea Flutter își minimizează modificările, reutilizând RenderObject la schimbarea Widget de același tip.

ArboreScopImuabil?Ciclu de viață
WidgetConfigurație UI (rețetă)DaRecreat la fiecare build
ElementLegătură Widget ↔ RenderObjectNuExistă cât timp widgetul este în arbore
RenderObjectRandare și layoutNuGrea, reutilizată când este posibil

StatelessWidget vs StatefulWidget

Flutter împarte Widgeturile în două tipuri fundamentale: StatelessWidget și StatefulWidget. StatelessWidget este un widget care nu conține stare modificabilă. Aspectul StatelessWidget este complet determinat de constructorul său și nu se poate schimba după randare. Exemple: Text, Icon, Divider, Padding. Toate proprietățile StatelessWidget sunt declarate ca final în constructor și sunt doar pentru citire.

StatefulWidget este un widget cu stare modificabilă. Este format din două clase: Widgetul în sine (configurație imuabilă, ca și StatelessWidget) și State (stare modificabilă). Separarea Widget de State este o decizie arhitecturală cheie a Flutter. Widgetul este recreat la fiecare build, dar obiectul State rămâne viu pe tot parcursul ciclului de viață al widgetului în arbore, păstrându-și starea.

Când este apelată metoda setState(), Flutter marchează State ca „murdar” și la următorul cadru apelează metoda build() pentru reconstruirea subarborelui. Important: setState() nu re-creează Widgetul în sine — declanșează doar apelul build() în State-ul existent. Aceasta înseamnă că StatefulWidget poate actualiza UI fără a pierde starea Widgeturilor copil, atâta timp cât cheile (Key) elementelor copil rămân stabile.

dart
// StatelessWidget — aspectul nu se schimbă niciodată
class GreetingWidget extends StatelessWidget {
  const GreetingWidget({super.key, required this.name});
  final String name;

  @override
  Widget build(BuildContext context) {
    return Text('Salut, $name');
  }
}

// StatefulWidget — contor cu stare modificabilă
class CounterWidget extends StatefulWidget {
  const CounterWidget({super.key});

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

class _CounterWidgetState extends State<CounterWidget> {
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Contor: $_count'),
        ElevatedButton(
          onPressed: () => setState(() => _count++),
          child: const Text('Incrementează'),
        ),
      ],
    );
  }
}

Tipuri de Widget: layout, painting, interactive

Toate Widgeturile în Flutter pot fi împărțite în trei categorii principale după scopul funcțional. Layout Widget — sunt responsabile pentru amplasarea elementelor copil pe ecran. Row și Column plasează copiii în linie, Stack suprapune unul peste altul, Expanded și Flexible distribuie spațiul disponibil. Layout Widgeturile nu au reprezentare vizuală proprie — ele gestionează poziția și dimensiunea widgeturilor copil.

Painting Widget — sunt responsabile pentru aspectul vizual. Container combină decorațiunile (culoare, gradient, umbră, margine) cu proprietățile de layout. Padding adaugă spațieri, DecoratedBox desenează fundalul, Transform aplică transformări (rotire, scalare). Painting Widgeturile sunt blocurile de construcție ale stilului vizual și sunt adesea folosite împreună cu layout Widgeturi pentru a obține aspectul dorit.

Interactive Widget — procesează intrarea utilizatorului. GestureDetector detectează gesturi (tap, swipe, pinch), InkWell adaugă efectul ripple Material, TextField primește intrare text, Slider și Switch oferă elemente de control standard. Interactive Widgeturile ridică evenimente prin funcții callback care sunt transmise în constructor sau gestionate prin furnizori de stare.

CategorieExemple de WidgetScop
LayoutRow, Column, Stack, Expanded, Flexible, AlignAmplasarea și dimensiunea elementelor copil
PaintingContainer, Padding, DecoratedBox, RotatedBoxCuloare, fundal, margini, umbre, transformări
InteractiveGestureDetector, InkWell, TextField, SliderProcesarea atingerilor, intrărilor, gesturilor
PlatformMaterialApp, CupertinoApp, Theme, MediaQueryIntegrare cu platforma, teme, adaptare
AsyncFutureBuilder, StreamBuilder, ValueListenableBuilderActualizare reactivă din date asincrone

Conform datelor Flutter Widget of the Week (Google, 2025), comunitatea Flutter folosește activ combinația de layout + painting + interactive Widget pentru a construi aproape orice interfață. De exemplu, un buton: InkWell (interactive) + Container (painting) + Text (static) + Padding (layout). O astfel de modularitate permite reutilizarea blocurilor standard în diferite contexte fără duplicarea codului.

Compoziția Widget și BuildContext

Compoziția Widget este procesul de construire a UI prin înnestarea unor Widgeturi în altele. Spre deosebire de moștenirea clasică (extends), unde clasa copil moștenește comportamentul părintelui, Flutter folosește agregarea: fiecare Widget conține alte Widgeturi prin parametrul child (pentru unul) sau children (pentru mai multe). Această abordare oferă o mai mare flexibilitate și reutilizare.

BuildContext este al doilea concept cel mai important după Widget. BuildContext este un descriptor al poziției Widget în arborele de elemente. Prin BuildContext, Widget poate accesa Widgeturile ancestoare (Theme.of(context), MediaQuery.of(context), Navigator.of(context)). BuildContext este transmis în metoda build() și este folosit pentru interacțiunea cu elementele părinte și copil. Fiecare Widget are exact un BuildContext care îi identifică unic poziția în arbore.

Conform Flutter Architectural Overview (Google, 2025), BuildContext este baza pentru InheritedWidget — un mecanism care permite transmiterea datelor în josul arborelui fără transmiterea explicită prin constructori. Theme, MediaQuery, Navigator și Provider folosesc InheritedWidget sub capotă. Orice Widget profund înnestat poate accesa datele strămoșului prin BuildContext.dependOnInheritedWidgetOfExactType, ceea ce face din BuildContext cheia arhitecturii reactive Flutter.

dart
// Compoziția Widget prin înnestare
Scaffold(
  appBar: AppBar(title: const Text('Aplicația Mea')),
  body: Center(
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        const Text('Bun venit în Flutter',
          style: TextStyle(fontSize: 24)),
        const SizedBox(height: 16),
        ElevatedButton(
          onPressed: () { /* nav */ },
          child: const Text('Începeți'),
        ),
      ],
    ),
  ),
)

// Acces la teme prin BuildContext
Text(
  'Text Stilizat',
  style: Theme.of(context).textTheme.headlineMedium,
)

Greșeli tipice cu Widget

Prima și cea mai frecventă greșeală — folosirea StatefulWidget acolo unde StatelessWidget este suficient. Mulți dezvoltatori Flutter începători creează StatefulWidget pentru toate widgeturile, chiar dacă starea este stocată într-un furnizor extern (Provider, Riverpod, BLoC). Acest lucru este redundant și degradează performanța. Regula: folosiți StatelessWidget dacă starea este gestionată din exterior sau dacă widgetul nu are propria sa stare modificabilă.

A doua greșeală — crearea Widgeturilor în interiorul metodei build fără constructor const. Fiecare Widget creat fără const este realocat la fiecare build. Dacă în interiorul build() se creează Widgeturi cu constructor const, Flutter poate reutiliza aceeași instanță, reducând încărcarea garbage collector-ului. Adăugați const peste tot unde este posibil — în special pentru Text, Icon, SizedBox, Padding și alte StatelessWidgeturi.

A treia greșeală — lucrul incorect cu cheile (Key). Flutter folosește Key pentru identificarea Widgeturilor la reconstruirea arborelui. Dacă o listă de Widgeturi este reconstruită fără Key, Flutter poate încurca ordinea elementelor, ceea ce duce la animații incorecte sau pierderea stării. Adăugați întotdeauna Key (de exemplu, ValueKey sau ObjectKey) pentru elementele din liste, în special când folosiți ListView.builder cu date dinamice.

Întrebări frecvente

Care este diferența dintre StatelessWidget și StatefulWidget?

StatelessWidget — widget fără stare modificabilă, aspectul său este complet determinat de constructor. StatefulWidget — widget cu stare modificabilă, care este stocată într-un obiect State separat și poate fi actualizată prin setState() fără a recrea widgetul în sine. Folosiți StatelessWidget peste tot unde este posibil, StatefulWidget — când este necesară o stare locală.

De ce Widgeturile sunt numite immutable?

Widget immutable — este o decizie arhitecturală Flutter pentru performanță. Dacă Widgeturile ar fi mutable, Flutter nu ar putea compara în siguranță configurația veche cu cea nouă la fiecare build. Imutabilitatea permite Flutter să determine rapid dacă Widgetul s-a schimbat (prin operatorul ==) și să reutilizeze RenderObject-ul existent, minimizând operațiile costisitoare de randare.

Ce este BuildContext și la ce servește?

BuildContext este un descriptor al poziției Widget în arborele de elemente. Prin el, Widgetul accesează Widgeturile ancestoare (Theme, MediaQuery, Navigator) și InheritedWidget. BuildContext este folosit și pentru navigare (Navigator.of(context)), afișarea SnackBar și interacțiunea cu Provider. Fiecare Widget primește BuildContext prin metoda build() și îl transmite moștenitorilor săi.

Cum să aleg între Row, Column și Stack pentru layout?

Folosiți Row pentru aranjarea orizontală a elementelor, Column — pentru cea verticală, Stack — pentru suprapunerea elementelor. Row și Column funcționează pe principiul flexbox: children ocupă spațiul conform mainAxisSize, mainAxisAlignment și crossAxisAlignment. Stack folosește positioned-children pentru poziționarea precisă față de margini sau centru.

Cum menține Flutter 60 FPS cu mii de Widgeturi?

Flutter atinge performanță ridicată prin trei mecanisme: (1) Widget are cheap — obiecte imuabile ușoare (40–80 de octeți), crearea lor nu încarcă GC-ul. (2) RenderObject reuse — la schimbarea Widget de același tip, RenderObject este reutilizat, evitând recrearea costisitoare. (3) Skia/Impeller engine — randarea la nivel C++ cu minimizarea apelurilor de desenare prin repaint boundaries.

Rezumat

  • Widget — configurație imuabilă a unei părți de UI în Flutter, bloc de construcție de bază care descrie aspectul și comportamentul.
  • Trei arbori — Flutter folosește Widget tree (configurație), Element tree (legătură), RenderObject tree (randare) pentru randare optimă.
  • Stateless vs Stateful — StatelessWidget fără stare, StatefulWidget cu State modificabil și metoda setState() pentru actualizarea UI.
  • Compoziția — UI se construiește prin înnestarea Widget prin child/children, fără moștenire, asigurând flexibilitate și reutilizare.
  • BuildContext — descriptor al poziției Widget în arbore pentru acces la Theme, Navigator, MediaQuery și InheritedWidget.
  • Performanța — Widgeturile sunt recreate la fiecare build (60 FPS), dar RenderObject este reutilizat la potrivirea tipului.
  • Categorii — Widgeturile se împart în layout (Row, Column, Stack), painting (Container, Padding), interactive (GestureDetector, TextField) și platform (MaterialApp, Theme).

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și