Flutter Widgets – podstawowe bloki budulcowe interfejsu użytkownika w frameworku Google Flutter, wydanym w stabilnej wersji 1.0 w grudniu 2018 roku. We Flutter wszystko jest widgetem: od ekranu i przycisku po odstęp i wyrównanie. Widgety tworzą drzewo (widget tree), które Flutter renderuje na własnym silniku Skia/Impeller, z pominięciem natywnych komponentów platformy. Według Google I/O (2025), Flutter jest używany w ponad 500 000 aplikacji w Google Play i App Store. Architektura Flutter dzieli widgety na StatelessWidget (niezmienne) i StatefulWidget (ze zmiennym stanem poprzez obiekt State).
Najważniejsze
Flutter Widgets – wszystkie elementy UI we frameworku Flutter, zaimplementowane jako niezmienne (immutable) klasy Dart. Widget to konfiguracja elementu UI: jego rozmiar, kolor, położenie, obsługa zdarzeń. W przeciwieństwie do Androida (XML + View) czy iOS (Storyboard + UIView), Flutter nie używa natywnych komponentów – wszystko jest rysowane przez silnik Skia (Android/iOS/Linux) lub Impeller (iOS/macOS) na Canvas. Zapewnia to jednakowy wygląd na wszystkich platformach bez rozbieżności platformowych. Flutter jest dostarczany z dwoma zestawami widgetów: Material Widgets (projekt Google Material Design 3 dla Androida/Web) i Cupertino Widgets (projekt Apple HIG dla iOS/macOS). Deweloper może łączyć oba zestawy w jednej aplikacji. Każdy widget ma metodę build(), zwracającą widget potomny lub ich kompozycję – w ten sposób budowane jest drzewo widgetów.
Flutter operuje na trzech drzewach: Widget Tree (konfiguracja, tworzona przez programistę), Element Tree (dopasowanie widgeta do pozycji w drzewie, zarządzane przez framework), Render Tree (obiekty obliczające layout i wykonujące paint). Widget tworzy Element, który tworzy RenderObject. Element jest ponownie używany przy zmianie konfiguracji (rebuild) – to mechanizm optymalizacji Flutter: jeśli typ widgeta i key są zgodne, Element nie jest odtwarzany, a jedynie aktualizowana jest nowa konfiguracja. Zrozumienie trzech drzew to klucz do optymalizacji wydajności aplikacji Flutter.
StatelessWidget – widget, który nie przechowuje stanu wewnętrznego. Przyjmuje parametry przez konstruktor i wyświetla je bez możliwości zmiany po zbudowaniu. StatelessWidget idealnie nadaje się do elementów statycznych: nagłówków, ikon, awatarów, list ze stałymi danymi. Metoda build() jest wywoływana raz przy tworzeniu i przy zmianie widgeta nadrzędnego. Ponieważ StatelessWidget nie ma stanu, nie wymaga oddzielnego obiektu State – build() jest zdefiniowany bezpośrednio w klasie widgeta.
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 – StatelessWidget, przyjmujący name i avatarUrl przez konstruktor. CircleAvatar wyświetla awatar, Text – nazwę. Widget nie może zmienić tych wartości po utworzeniu. Użycie const konstruktora (const ProfileHeader) pozwala Flutter na ponowne wykorzystanie instancji widgeta przy rebuild rodzica – to mikrooptymalizacja zalecana dla wszystkich StatelessWidget.
StatefulWidget – widget ze zmiennym stanem, który jest przechowywany w oddzielnym obiekcie State. Sam StatefulWidget jest niezmienny (immutable), tak jak StatelessWidget. Zmienna część jest wyodrębniona w State: State – obiekt, który jest tworzony raz i przetrwa wielokrotne rebuildy widgeta. Gdy stan się zmienia (setState()), Flutter wywołuje build() State, aktualizując UI. StatefulWidget jest używany do elementów interaktywnych: liczników, pól tekstowych, checkboxów, animacji, timerów.
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 – StatefulWidget z klasą State _LikeButtonState. Pola liked i count to stan przechowywany w State i nie resetowany przy rebuild. setState() powiadamia Flutter o konieczności przebudowania UI. Flutter nie odtwarza State przy każdym rebuildzie rodzica – tylko przy zmianie konfiguracji (key lub typ widgeta). W IT Sectr używamy StatelessWidget do wyświetlania i StatefulWidget do komponentów interaktywnych, stosując zasadę minimalizacji StatefulWidget do granic mutacji.
Widget Tree – hierarchiczna struktura widgetów opisująca UI aplikacji. Flutter przekształca Widget Tree w Element Tree: każdy Widget tworzy Element, który śledzi pozycję w drzewie i połączenie z RenderObject. Render Tree wykonuje layout (określanie rozmiarów i pozycji) oraz paint (renderowanie). Element to kluczowa warstwa optymalizacyjna: jeśli typ widgeta i key nie zmieniły się przy rebuild, Element jest ponownie używany, aktualizując konfigurację bez odtwarzania RenderObject. Zapewnia to wydajność Flutter przy wysokiej częstotliwości klatek (60/120 FPS).
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 Widgety')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.flutter_dash, size: 80),
const SizedBox(height: 16),
const Text(
'Witaj, Flutter!',
style: TextStyle(fontSize: 24),
),
],
),
),
);
}
}Drzewo widgetów HomeScreen: Scaffold → AppBar + Center → Column → [Icon, SizedBox, Text]. Scaffold – główny widget layoutu Material Design. Center centruje potomny Column. Column rozkłada dzieci pionowo z wyrównaniem do środka (mainAxisAlignment.center). Flutter oblicza layout od góry do dołu (constraints przekazywane od rodzica), a rozmiary od dołu do góry (sizedByParent lub sizing from children).
BuildContext – deskryptor pozycji widgeta w Element Tree. Każda metoda build() otrzymuje BuildContext, przez który widget może uzyskać dostęp do:MediaQuery (rozmiar ekranu, orientacja, gęstość pikseli, safe area), Theme (MaterialThemeData lub CupertinoThemeData), Navigator (stos nawigacyjny), ScaffoldMessenger (SnackBar), InheritedWidget (przekazywanie danych w dół drzewa). BuildContext nie powinien być przechowywany po zakończeniu build() ani używany poza kontekstem synchronicznym – prowadzi to do błędów «Scaffold.of() called with a context that does not contain a Scaffold». Zamiast przechowywania context używaj widgeta Builder lub wzorca InheritedWidget do dostępu do danych.
Key – opcjonalny parametr konstruktora każdego widgeta (super.key). Key pomaga Flutter identyfikować widget przy zmianie kolejności/liczby elementów na liście. Bez key Flutter porównuje widgety według typu i pozycji; przy dodawaniu elementu do środka listy bez key Flutter ponownie używa Element dla niewłaściwego widgeta, powodując błędy stanu (StatefulWidget) i animacji. Używaj ValueKey (unikalny identyfikator) dla list, ObjectKey (na podstawie obiektu danych), UniqueKey (gwarantowana unikalność przy każdym rebuild – do wymuszonego odtworzenia).
Flutter SDK (stabilna wersja 3.3+, Dart 3+) zawiera 200+ wbudowanych widgetów, podzielonych na kategorie: 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) i Accessibility (Semantics, MergeSemantics, ExcludeSemantics). Layout-widgety to podstawa każdego Flutter UI: Row dla sekwencji poziomych, Column dla pionowych, Stack dla nakładających się elementów, Container z BoxDecoration do dekorowania.
| Kategoria | Kluczowe widgety | Przeznaczenie |
|---|---|---|
| Layout | Row, Column, Stack, Container, Expanded, Flexible | Pozycjonowanie i rozmiary elementów potomnych |
| Material | Scaffold, AppBar, Card, Drawer, FAB, TabBar | Komponenty UI Material Design 3 |
| Cupertino | CupertinoPageScaffold, CupertinoNavigationBar, CupertinoButton | Komponenty stylizowane na iOS |
| Painting | Opacity, ClipRect, Transform, DecoratedBox, BackdropFilter | Efekty wizualne i przycinanie |
| Async | FutureBuilder, StreamBuilder | Asynchroniczne ładowanie danych ze stanami UI |
| Accessibility | Semantics, MergeSemantics | Semantyczne oznaczanie dla czytników ekranu |
Często zadawane pytania
StatelessWidget nie ma stanu wewnętrznego – wszystkie dane są przekazywane przez konstruktor i nie zmieniają się. StatefulWidget przechowuje zmienny stan w oddzielnym obiekcie State, który przetrwa rebuildy. Zmiana stanu wywołuje setState(), a Flutter przebudowuje UI. Dla elementów niezmiennych używaj StatelessWidget; dla interaktywnych – StatefulWidget.
BuildContext – deskryptor pozycji widgeta w Element Tree, przekazywany do metody build(). Przez context widget uzyskuje dostęp do MediaQuery, Theme, Navigator, ScaffoldMessenger i InheritedWidget. BuildContext nie powinien być przechowywany po zakończeniu build(). Do dostępu do danych używaj widgeta Builder lub InheritedWidget.
Tak, Flutter używa Dart jako jedynego języka programowania. Dart jest statycznie typowanym językiem z null-safety, dźwiękowymi typami (sound null safety), extension methods i pattern matching. Na początek wystarczą podstawy Darta: klasy, funkcje, async/await, kolekcje. Dart jest kompilowany do kodu natywnego (AOT) przez Dart Native i do JavaScript (dla web) przez Dart2JS.
Nie, Flutter obsługuje sześć platform: iOS, Android, Web, macOS, Windows, Linux. Ponad 90% kodu jest wspólne dla wszystkich platform. Adaptacje platformowe (kamera, system plików) są realizowane przez wtyczki lub platform channels. Dla desktopu Flutter używa powłoki okienkowej Flutter Desktop, dla web – silnika CanvasKit (WebGL) lub renderera HTML.
Flutter osiąga 60/120 FPS dzięki: własnemu silnikowi 2D (Skia/Impeller), braku natywnego bridge (w przeciwieństwie do React Native), trzem drzewom (Widget → Element → Render) z ponownym wykorzystaniem Element przy niezmiennym typie/key, algorytmowi layoutu z jednokierunkowym przebiegiem (constraints down, sizes up) oraz RepaintBoundary do izolacji przerysowywania poszczególnych stref ekranu.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również