Widget — je ústředním pojmem frameworku Flutter, který popisuje konfiguraci prvku uživatelského rozhraní. Každá vizuální komponenta, od tlačítka po složitou animaci, je Widget. Na rozdíl od jiných frameworků, kde je UI popsáno samostatnými XML soubory nebo je imperativně vykreslováno, Flutter vytváří rozhraní pomocí kompozice Widget — spojování malých nedělitelných prvků do hierarchického stromu. Podle Flutter Documentation (2025) knihovna Flutter SDK obsahuje více než 260 vestavěných Widgetů, které pokrývají Material Design, Cupertino a vlastní styly.
Hlavní body
Widget ve Flutter je neměnný (immutable) popis části uživatelského rozhraní. Každý Widget obsahuje konfigurační vlastnosti: velikost, barvu, pozici, text, obsluhy událostí a potomky Widget. Samotné Widgety nejsou vykreslovány přímo — jsou to recepty (blueprints), na jejichž základě Flutter Engine vytváří RenderObject — skutečný grafický objekt na obrazovce.
Filozofie Flutter říká: „Everything is a Widget”. To znamená, že nejen viditelné prvky (Text, Image, Button) jsou Widgety, ale také strukturální bloky (Padding, Center, Column, Stack), behaviorální bloky (GestureDetector, AnimatedBuilder) a dokonce i samotná aplikace (MaterialApp, CupertinoApp). Tento přístup zajišťuje jednotnost: jakýkoli prvek obrazovky lze kombinovat s jakýmkoli jiným pomocí jednoduchého vnoření.
Podle údajů z Google I/O 2024 — Flutter Widgets Deep Dive obsahuje průměrná aplikace na Flutter 200 až 1500 Widgetů v jednom okamžiku. Navzdory tomuto počtu Flutter udržuje 60 FPS i na levných zařízeních díky optimalizacím na úrovni C++ Skia/Impeller engine. Widgety jsou lehké objekty (40–80 bajtů každý), takže jejich vytváření není úzkým hrdlem výkonu.
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('Ahoj, Flutter!'),
),
),
);
}
}
Abychom pochopili, jak Widget funguje, je třeba poznat architekturu Flutter sestávající ze tří propojených stromů. První — Widget tree — popisuje konfiguraci UI. Je to lehký strom, který může být při každém snímku zcela přestavěn (garbage collector odstraňuje staré Widgety a vytváří nové). Widgety jsou neměnné (immutable): pokud se změnila barva textu — vytvoří se nový Text Widget s novou barvou, starý je vyřazen.
Druhý strom — Element tree — je spojovacím článkem mezi Widget a RenderObject. Element obsahuje odkaz na Widget (konfiguraci) a na RenderObject (vykreslení). Když se Widget změní, Flutter porovná nový Widget se starým Elementem a rozhodne: aktualizovat stávající RenderObject (pokud je Widget stejného typu) nebo vytvořit nový (pokud se typ Widget změnil). Tento proces se nazývá Reconciliation a je analogický React Virtual DOM.
Třetí strom — RenderObject tree — je zodpovědný za skutečné vykreslení na obrazovce. RenderObject obsahuje konkrétní rozměry, pozice a metody paint. Flutter Engine (C++ Skia nebo Impeller) prochází RenderObject tree a vykresluje každý uzel. RenderObject tree je nejtěžší strom, proto Flutter minimalizuje jeho změny opětovným použitím RenderObject při změně Widgetu stejného typu.
| Strom | Účel | Immutable? | Životní cyklus |
|---|---|---|---|
| Widget | Konfigurace UI (recept) | Ano | Znovu vytvořen při každém build |
| Element | Spojení Widget ↔ RenderObject | Ne | Existuje, dokud je widget ve stromu |
| RenderObject | Vykreslení a layout | Ne | Těžký, znovu použit pokud možno |
Flutter dělí Widgety na dva základní typy: StatelessWidget a StatefulWidget. StatelessWidget je widget, který neobsahuje měnitelný stav. Vzhled StatelessWidget je zcela určen jeho konstruktorem a po vykreslení se nemůže změnit. Příklady: Text, Icon, Divider, Padding. Všechny vlastnosti StatelessWidget jsou deklarovány jako final v konstruktoru a jsou určeny pouze pro čtení.
StatefulWidget je widget s měnitelným stavem. Skládá se ze dvou tříd: samotného Widget (neměnná konfigurace, stejně jako StatelessWidget) a State (měnitelný stav). Oddělení Widget od State je klíčové architektonické rozhodnutí Flutter. Widget je při každém build znovu vytvořen, ale objekt State zůstává živý po celou dobu životního cyklu widgetu ve stromu a uchovává svůj stav.
Když je zavolána setState(), Flutter označí State jako „špinavý” a v dalším snímku zavolá metodu build() pro přestavění podstromu. Důležité: setState() znovu nevytváří samotný Widget — pouze spouští volání build() u existujícího State. To znamená, že StatefulWidget může aktualizovat UI bez ztráty stavu potomků Widget, pokud klíče (Key) potomků zůstávají stabilní.
// StatelessWidget — vzhled se nikdy nemění
class GreetingWidget extends StatelessWidget {
const GreetingWidget({super.key, required this.name});
final String name;
@override
Widget build(BuildContext context) {
return Text('Ahoj, $name');
}
}
// StatefulWidget — počítadlo s měnitelným stavem
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('Počet: $_count'),
ElevatedButton(
onPressed: () => setState(() => _count++),
child: const Text('Zvýšit'),
),
],
);
}
}
Všechny Widgety ve Flutter lze rozdělit do tří hlavních kategorií podle funkčního účelu. Layout Widget — jsou zodpovědné za umístění potomků na obrazovce. Row a Column umísťují potomky do řady, Stack pokládá jeden na druhý, Expanded a Flexible rozdělují dostupný prostor. Layout Widgety nemají vlastní vizuální reprezentaci — spravují pozici a velikost potomků.
Painting Widget — jsou zodpovědné za vizuální vzhled. Container kombinuje dekorace (barvu, gradient, stín, okraj) s vlastnostmi layout. Padding přidává mezery, DecoratedBox kreslí pozadí, Transform aplikuje transformace (otáčení, měřítko). Painting Widgety jsou stavebními bloky vizuálního stylu a často se používají společně s layout Widgety k dosažení požadovaného vzhledu.
Interactive Widget — zpracovávají vstup uživatele. GestureDetector detekuje gesta (tap, swipe, pinch), InkWell přidává efekt ripple Material, TextField přijímá textový vstup, Slider a Switch poskytují standardní ovládací prvky. Interactive Widgety vyvolávají události prostřednictvím callback funkcí, které jsou předány konstruktoru nebo zpracovávány prostřednictvím poskytovatelů stavu.
| Kategorie | Příklady Widget | Účel |
|---|---|---|
| Layout | Row, Column, Stack, Expanded, Flexible, Align | Umístění a velikost potomků |
| Painting | Container, Padding, DecoratedBox, RotatedBox | Barva, pozadí, okraje, stíny, transformace |
| Interactive | GestureDetector, InkWell, TextField, Slider | Zpracování dotyku, vstupu, gest |
| Platform | MaterialApp, CupertinoApp, Theme, MediaQuery | Integrace s platformou, témata, adaptace |
| Async | FutureBuilder, StreamBuilder, ValueListenableBuilder | Reaktivní aktualizace z asynchronních dat |
Podle údajů z Flutter Widget of the Week (Google, 2025) komunita Flutter aktivně používá kombinaci layout + painting + interactive Widget pro vytváření téměř jakéhokoli rozhraní. Například tlačítko: InkWell (interactive) + Container (painting) + Text (static) + Padding (layout). Taková modularita umožňuje opětovné použití standardních bloků v různých kontextech bez duplikace kódu.
Kompozice Widget je proces vytváření UI vnořováním Widgetů do sebe. Na rozdíl od klasického dědění (extends), kde potomek dědí chování rodiče, Flutter používá agregaci: každý Widget obsahuje další Widgety prostřednictvím parametru child (pro jeden) nebo children (pro více). Tento přístup poskytuje větší flexibilitu a znovupoužitelnost.
BuildContext je druhý nejdůležitější koncept po Widget. BuildContext je deskriptor pozice Widgetu ve stromu prvků. Prostřednictvím BuildContext může Widget přistupovat k předkům (Theme.of(context), MediaQuery.of(context), Navigator.of(context)). BuildContext je předán metodě build() a používá se pro interakci s rodičovskými a potomky. Každý Widget má přesně jeden BuildContext, který jednoznačně identifikuje jeho pozici ve stromu.
Podle Flutter Architectural Overview (Google, 2025) je BuildContext základem InheritedWidget — mechanismu, který umožňuje předávat data dolů stromem bez explicitního předávání přes konstruktory. Theme, MediaQuery, Navigator a Provider používají InheritedWidget pod kapotou. Jakýkoli hluboce vnořený Widget může přistupovat k datům předka prostřednictvím BuildContext.dependOnInheritedWidgetOfExactType, což činí BuildContext klíčem k reaktivní architektuře Flutter.
// Kompozice Widget vnořováním
Scaffold(
appBar: AppBar(title: const Text('Moje Aplikace')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('Vítejte ve Flutter',
style: TextStyle(fontSize: 24)),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () { /* nav */ },
child: const Text('Začít'),
),
],
),
),
)
// Přístup k tématům přes BuildContext
Text(
'Stylizovaný Text',
style: Theme.of(context).textTheme.headlineMedium,
)
První a nejčastější chyba — použití StatefulWidget tam, kde stačí StatelessWidget. Mnoho začínajících Flutter vývojářů vytváří StatefulWidget pro všechny widgety, i když je stav uložen v externím poskytovateli (Provider, Riverpod, BLoC). To je nadbytečné a zhoršuje výkon. Pravidlo: použijte StatelessWidget, pokud je stav spravován zvenčí nebo pokud widget nemá vlastní měnitelný stav.
Druhá chyba — vytváření Widget uvnitř metody build bez const konstruktoru. Každý Widget vytvořený bez const je při každém build znovu alokován. Pokud jsou uvnitř build() Widgety vytvářeny s const konstruktorem, Flutter může použít stejnou instanci, čímž se snižuje zatížení garbage collectoru. Přidávejte const všude, kde je to možné — zejména pro Text, Icon, SizedBox, Padding a další StatelessWidgety.
Třetí chyba — nesprávná práce s klíči (Key). Flutter používá Key k identifikaci Widgetů při přestavbě stromu. Pokud je seznam Widgetů přestavěn bez Key, Flutter může zamíchat pořadí prvků, což vede k nesprávné animaci nebo ztrátě stavu. Vždy přidávejte Key (např. ValueKey nebo ObjectKey) pro prvky v seznamech, zejména při použití ListView.builder s dynamickými daty.
Často kladené otázky
StatelessWidget — widget bez měnitelného stavu, jeho vzhled je zcela určen konstruktorem. StatefulWidget — widget s měnitelným stavem, který je uložen v samostatném objektu State a může být aktualizován pomocí setState() bez znovuvytvoření samotného widgetu. Používejte StatelessWidget všude, kde je to možné, StatefulWidget — když je vyžadován lokální stav.
Widget immutable — je architektonické rozhodnutí Flutter pro výkon. Pokud by Widgety byly mutable, Flutter by nemohl bezpečně porovnávat starou a novou konfiguraci při každém build. Neměnnost umožňuje Flutter rychle určit, zda se Widget změnil (pomocí operátoru ==), a znovu použít stávající RenderObject, čímž se minimalizují nákladné operace vykreslování.
BuildContext je deskriptor pozice Widgetu ve stromu prvků. Prostřednictvím něj Widget přistupuje k předkům (Theme, MediaQuery, Navigator) a InheritedWidget. BuildContext se také používá pro navigaci (Navigator.of(context)), zobrazení SnackBar a interakci s Provider. Každý Widget získává BuildContext prostřednictvím metody build() a předává jej svým následovníkům.
Použijte Row pro horizontální uspořádání prvků, Column — pro vertikální, Stack — pro překrývání prvků. Row a Column fungují na principu flexbox: children zabírají místo podle mainAxisSize, mainAxisAlignment a crossAxisAlignment. Stack používá positioned-children pro přesné umístění vzhledem k okrajům nebo středu.
Flutter dosahuje vysokého výkonu pomocí tří mechanismů: (1) Widget are cheap — lehké immutable objekty (40–80 bajtů), jejich vytváření nezatěžuje GC. (2) RenderObject reuse — při změně Widgetu stejného typu je RenderObject znovu použit, čímž se vyhne nákladnému znovuvytvoření. (3) Skia/Impeller engine — vykreslování na úrovni C++ s minimalizací volání kreslení pomocí repaint boundaries.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také