Theme i ThemeData — centralne mechanizmy motywizacji we Flutter, pozwalające ustawiać kolory, czcionki, odstępy i style komponentów globalnie dla całej aplikacji. ThemeData — obiekt konfiguracyjny przekazywany do MaterialApp przez parametr theme. Według oficjalnej dokumentacji Flutter (2026), prawidłowa konfiguracja ThemeData redukuje ilość powielonego kodu stylizacji o 70–80% i zapewnia jednolity wygląd ekranów. Wbudowany system dziedziczenia pozwala nadpisywać motyw na poziomie poszczególnych widgetów przez Theme.of(context).
Najważniejsze
ThemeData — klasa zawierająca pełny opis wizualnego stylu aplikacji Material. Przekazywana do MaterialApp przez parametr theme lub darkTheme i dziedziczona przez wszystkie widgety potomne. Każdy widget Material Design używa ThemeData do pobierania swoich stylów: kolorów przycisków, rozmiarów AppBar, stylów tekstu, odstępów kart i ikon.
U podstaw ThemeData leży ColorScheme, który określa primary, secondary, surface, error i inne kolory. Z tych kolorów automatycznie obliczane są stany (hover, focus, pressed) dla wszystkich komponentów Material. Na przykład ElevatedButton używa koloru primary do tła i onPrimary do tekstu — bez dodatkowej konfiguracji.
Widget Theme — to InheritedWidget, który rozpowszechnia motyw w dół drzewa widgetów. Każdy widget może uzyskać bieżący motyw przez Theme.of(context), a także utworzyć nowy motyw na podstawie istniejącego z nadpisanymi wartościami przez Theme.copyWith lub ThemeData.copyWith.
Podsumowanie: ThemeData — jednolite źródło stylów aplikacji. Rozpocznij tworzenie od zdefiniowania ThemeData — to nadaje wizualny język całemu projektowi i zapobiega rozproszonym stylom w różnych ekranach.
ColorScheme — centralny element motywizacji w Material 3. Zastępuje przestarzałe primarySwatch i primaryColor, dostarczając pełny zestaw kolorów dla wszystkich stanów i komponentów.
ColorScheme zawiera 12 podstawowych kolorów: primary, onPrimary, secondary, onSecondary, surface, onSurface, error, onError, background, onBackground, outline i shadow. Dla każdego koloru automatycznie obliczane są warianty light i dark, a także kolory dla stanów hover, focus, pressed i dragged. Przy podaniu tylko primary, framework generuje pozostałe kolory automatycznie przez ColorScheme.fromSeed.
Metoda ColorScheme.fromSeed (Material 3) przyjmuje bazowy kolor seedColor i generuje pełną harmonijną paletę na podstawie algorytmu Material Design 3. To eliminuje ręczne dopasowywanie kolorów complementary i analogous. Wystarczy przekazać jeden główny kolor marki, a ColorScheme.fromSeed stworzy spójną paletę dla całego systemu Material.
Podsumowanie: w nowych projektach używaj ColorScheme.fromSeed do generowania palety. Dla zgodności z Material 2 — primarySwatch. ColorScheme — obowiązkowe minimum dla Material 3.
TextTheme — część ThemeData określająca style tekstu dla wszystkich poziomów nagłówków, podtytułów, body i etykiet. Material Design definiuje 15 stylów tekstowych: headlineLarge, headlineMedium, headlineSmall, titleLarge, titleMedium i inne.
Typografika jest ustawiana albo przez GoogleFonts do automatycznego ładowania czcionek, albo przez wbudowany Theme.of(context).textTheme z niestandardowymi TextStyle. GoogleFonts udostępnia setki czcionek przez pakiet google_fonts, który cachuje je lokalnie po pierwszym załadowaniu. Alternatywa — osadzać czcionki w assets projektu przez pubspec.yaml.
Dla adaptacyjnej typografiki TextTheme obsługuje MediaQuery.textScaleFactor — przy zwiększeniu systemowej czcionki wszystkie style tekstowe skalują się proporcjonalnie. Dodatkowo można ustawić różne rozmiary czcionki dla różnych form faktorów przez isTest w zależności od MediaQuery.size.width.
Podsumowanie: definiuj TextTheme w ThemeData i używaj Theme.of(context).textTheme dla wszystkich elementów tekstowych. To zapewnia jednolitość czcionek i upraszcza globalne zmiany typografiki.
Theme — InheritedWidget, który rozpowszechnia motyw od rodzica do dzieci. Każdy widget potomny może zarówno odczytać bieżący motyw przez Theme.of(context), jak i utworzyć własną kopię ze zmianami przez ThemeData.copyWith.
Mechanizm dziedziczenia: jeśli widget jest owinięty w Theme z nowymi danymi, wszyscy jego potomkowie widzą nadpisany motyw, a widgety poza tym owinięciem — oryginalny. Pozwala to na przykład izolować style dla konkretnego ekranu: nagłówek używa niestandardowego motywu ze zmienionym AppBarTheme, a reszta aplikacji — standardowego.
Zagnieżdżanie Theme może być nieograniczone. Flutter używa hierarchicznego wyszukiwania: przy wywołaniu Theme.of(context) framework podnosi się w górę drzewa do pierwszego Theme i zwraca jego motyw. Jeśli nie zostanie znaleziony żaden Theme — zgłaszany jest wyjątek. Dlatego MaterialApp zawsze owija aplikację w Theme.
Podsumowanie: dziedziczenie Theme — elastyczny mechanizm do izolacji stylów. Używaj nadpisywania motywu dla poszczególnych ekranów bez powielania ogólnej konfiguracji.
Dark theme — wbudowana możliwość Flutter do przełączania między jasnym a ciemnym motywem. ThemeData jest przechowywana w parametrach theme (jasny) i darkTheme (ciemny) MaterialApp. Parametr themeMode zarządza trybem: ThemeMode.light (zawsze jasny), ThemeMode.dark (zawsze ciemny) lub ThemeMode.system (zgodny z ustawieniami systemowymi).
Przy implementacji ciemnego motywu nie trzeba tworzyć wszystkich stylów od nowa. Wystarczy przekazać ColorScheme.fromSeed(seedColor, brightness: Brightness.dark) w darkTheme. Framework automatycznie skoryguje kolory dla ciemnego tła: surface stanie się ciemnoszary, tekst — jasny. Pozostałe podmotywy (AppBarTheme, CardTheme, BottomNavigationBarTheme) dziedziczą z jasnej wersji, jeśli nie zostały jawnie nadpisane.
Dla płynnego przejścia między motywami Flutter udostępnia animację przez AnimatedTheme. Przy zmianie themeMode kolory i style są animowane, tworząc wizualnie przyjemne przełączanie. AnimatedTheme używa Duration i Curve do ustawienia szybkości animacji.
Podsumowanie: dark theme jest implementowane przez darkTheme i ColorScheme.fromSeed z brightness: Brightness.dark. Używaj ThemeMode.system do zgodności z ustawieniami systemowymi użytkownika.
Przykład 1 — globalna konfiguracja motywu przez MaterialApp z użyciem ColorScheme.fromSeed.
final ThemeData appTheme = ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.indigo,
brightness: Brightness.light,
),
appBarTheme: const AppBarTheme(
centerTitle: true,
elevation: 0,
),
cardTheme: const CardTheme(
elevation: 2,
margin: EdgeInsets.all(8),
),
);
return MaterialApp(
theme: appTheme,
themeMode: ThemeMode.system,
home: const MyHomePage(),
);
W tym przykładzie tworzony jest ThemeData z Material 3, schematem kolorów opartym na indigo i niestandardowymi stylami AppBar i Card. useMaterial3 włącza nowy system motywów Material 3, a ColorScheme.fromSeed generuje pełną paletę.
Przykład 2 — dostęp do motywu i nadpisanie dla części aplikacji.
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Column(
children: [
Text(
'Standardowy nagłówek',
style: theme.textTheme.headlineMedium,
),
Theme(
data: theme.copyWith(
colorScheme: theme.colorScheme.copyWith(
primary: Colors.orange,
),
),
child: const SpecialSection(),
),
],
);
}
Drugi przykład pokazuje użycie Theme.of(context) do odczytu stylów oraz Theme() z copyWith do nadpisania motywu w części drzewa bez zmiany globalnej konfiguracji. SpecialSection wewnątrz Theme otrzyma pomarańczowy kolor primary, reszta aplikacji — oryginalny indigo.
Podsumowanie: globalny motyw jest ustawiany w MaterialApp, dostęp przez Theme.of(context), nadpisanie przez Theme z copyWith. Dla Material 3 używaj ColorScheme.fromSeed.
Błąd 1: używanie primarySwatch zamiast ColorScheme w Material 3. primarySwatch jest przestarzałe i nieobsługiwane w Material 3. Przy użyciu useMaterial3: true z primarySwatch Flutter wyświetla ostrzeżenie. Rozwiązanie: przejść na ColorScheme.fromSeed do generowania palety.
Błąd 2: sztywne ustawianie kolorów w widgetach zamiast używania Theme.of(context). Programiści piszą Colors.blue bezpośrednio w Container, zamiast koloru primary lub secondary z motywu. Przy zmianie motywu takie kolory pozostają niezmienione, naruszając jednolity styl. Rozwiązanie: zawsze używać Theme.of(context).colorScheme.primary lub innych kolorów z motywu.
Błąd 3: brak darkTheme. Jeśli aplikacja nie definiuje darkTheme, przy włączeniu ciemnego motywu na urządzeniu Flutter próbuje dostosować jasny motyw automatycznie, co często daje niskiej jakości rezultat. Rozwiązanie: zawsze przekazywać darkTheme w MaterialApp, nawet jeśli ciemny motyw nie jest priorytetem — to ważne UX dla użytkowników.
Błąd 4: tworzenie nowego motywu przy każdym build. Jeśli ThemeData jest tworzone wewnątrz metody build, każde przebudowanie widgetu tworzy nowy obiekt motywu, łamiąc optymalizację InheritedWidget. Rozwiązanie: wynieść ThemeData do pola statycznego lub konstruktora const poza build.
Podsumowanie: prawidłowa motywizacja wymaga jednolitego scentralizowanego podejścia. Wynoś ThemeData do stałych, używaj schematów kolorów z motywu i zawsze definiuj oba motywy — jasny i ciemny.
Często zadawane pytania
ThemeData — to dane konfiguracyjne (kolory, czcionki, style). Theme — InheritedWidget, który przekazuje ThemeData w dół drzewa widgetów. Theme.of(context) zwraca ThemeData, który najbliższy Theme rozpowszechnia w bieżącym kontekście.
Zarządzaj themeMode w MaterialApp przez stan aplikacji. Zmień stan z ThemeMode.light na ThemeMode.dark, a Flutter przebuduje całe drzewo z nowym motywem. Do płynnej animacji używaj AnimatedTheme.
ColorScheme.fromSeed — metoda Material 3, która przyjmuje jeden bazowy kolor (seedColor) i generuje pełną harmonijną paletę: primary, secondary, tertiary, surface, error i ich on-warianty. Pozwala uzyskać spójny schemat kolorów z jednego koloru marki.
Przez TextTheme wewnątrz ThemeData: wskaż TextStyle z fontFamily dla każdego poziomu. Dla Google Fonts używaj pakietu google_fonts, przekazując fontFamily bezpośrednio w TextTheme. Dla lokalnych czcionek — podłącz je przez pubspec.yaml w sekcji fonts.
Tak. Owiń ekran w Theme z nadpisanymi danymi przez copyWith. Wszystkie widgety potomne wewnątrz tego Theme otrzymają zmienione style, a reszta aplikacji zachowa oryginalny motyw. Nie tworzy to dodatkowych narzutów.
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ż