Theme и ThemeData — какво е това, принципи и настройка на стилове

Автор: IT Sectr Публикувано: 2026-07-03 Време за четене: 8 мин

Theme и ThemeData — централните механизми за тематизация във Flutter, които позволяват глобално задаване на цветове, шрифтове, разстояния и стилове на компоненти за цялото приложение. ThemeData е конфигурационен обект, който се предава на MaterialApp чрез параметъра theme. Според официалната документация на Flutter (2026), правилната настройка на ThemeData намалява количеството дублиран код за стилизиране със 70–80% и осигурява единообразен външен вид на екраните. Вградената система за наследяване позволява предефиниране на темата на ниво отделни уиджети чрез Theme.of(context).

Основни точки

  • ThemeData — конфигурационен обект с цветова палитра, типография и стилове на компоненти
  • Theme.of(context) — достъп до текущата тема от всеки уиджет чрез BuildContext
  • Светла/Тъмна — вградена поддръжка за светла и тъмна тема чрез theme и darkTheme
  • Theme — уиджет, който наследява темата на дъщерните елементи и позволява нейното предефиниране
  • ColorScheme — набор от хармонични цветове, генериращ стилове за цялата Material система

Какво са Theme и ThemeData

ThemeData — клас, съдържащ пълно описание на визуалния стил на Material приложение. Предава се на MaterialApp чрез параметъра theme или darkTheme и се наследява от всички дъщерни уиджети. Всеки Material Design уиджет използва ThemeData за получаване на своите стилове: цветове на бутони, размери на AppBar, стилове на текст, разстояния на карти и икони.

В основата на ThemeData стои ColorScheme, който дефинира primary, secondary, surface, error и други цветове. От тези цветове автоматично се изчисляват състоянията (hover, focus, pressed) за всички Material компоненти. Например ElevatedButton използва primary цвят за фон и onPrimary за текст — без допълнителна конфигурация.

Уиджетът Theme — е InheritedWidget, който разпространява темата надолу по дървото от уиджети. Всеки уиджет може да получи текущата тема чрез Theme.of(context), както и да създаде нова тема на базата на съществуваща с предефинирани стойности чрез Theme.copyWith или ThemeData.copyWith.

Извод: ThemeData — единният източник на стилове за приложението. Започнете разработката с дефиниране на ThemeData — това задава визуалния език на целия проект и предотвратява разпръснати стилове на различни екрани.

ColorScheme: основа на цветовата палитра

ColorScheme — централният елемент на тематизация в Material 3. Той заменя остарелите primarySwatch и primaryColor, предоставяйки пълен набор от цветове за всички състояния и компоненти.

ColorScheme съдържа 12 основни цвята: primary, onPrimary, secondary, onSecondary, surface, onSurface, error, onError, background, onBackground, outline и shadow. За всеки цвят автоматично се изчисляват light и dark варианти, както и цветове за състоянията hover, focus, pressed и dragged. При задаване само на primary, framework автоматично генерира останалите цветове чрез ColorScheme.fromSeed.

Методът ColorScheme.fromSeed (Material 3) приема базов цвят seedColor и генерира пълна хармонична палитра на базата на алгоритъма Material Design 3. Това елиминира ръчния подбор на complementary и analogous цветове. Достатъчно е да предадете един основен цвят на марката и ColorScheme.fromSeed ще създаде съгласувана палитра за цялата Material система.

Извод: в нови проекти използвайте ColorScheme.fromSeed за генериране на палитра. За съвместимост с Material 2 — primarySwatch. ColorScheme е задължителният минимум за Material 3.

Typography и TextTheme: шрифтове на приложението

TextTheme — част от ThemeData, която дефинира стиловете на текста за всички нива на заглавия, подзаглавия, body и етикети. Material Design дефинира 15 текстови стила: headlineLarge, headlineMedium, headlineSmall, titleLarge, titleMedium и други.

Типографията се задава или чрез GoogleFonts за автоматично зареждане на шрифтове, или чрез вградения Theme.of(context).textTheme с персонализирани TextStyle. GoogleFonts предоставя стотици шрифтове чрез пакета google_fonts, който ги кешира локално след първото зареждане. Алтернатива — вграждане на шрифтове в assets на проекта чрез pubspec.yaml.

За адаптивна типография TextTheme поддържа MediaQuery.textScaleFactor — при увеличаване на системния шрифт всички текстови стилове се мащабират пропорционално. Допълнително могат да се зададат различни размери на шрифта за различни форм-фактори чрез isTest в зависимост от MediaQuery.size.width.

Извод: дефинирайте TextTheme в ThemeData и използвайте Theme.of(context).textTheme за всички текстови елементи. Това осигурява единообразие на шрифтовете и опростява глобалните промени в типографията.

Наследяване и предефиниране на тема

Theme — InheritedWidget, който разпространява темата от родител към деца. Всеки дъщерен уиджет може както да чете текущата тема чрез Theme.of(context), така и да създаде свое копие с промени чрез ThemeData.copyWith.

Механизъм на наследяване: ако уиджет е обвит в Theme с нови данни, всички негови потомци виждат предефинираната тема, а уиджетите извън тази обвивка — оригиналната. Това позволява например изолиране на стилове за конкретен екран: заглавието използва персонализирана тема с променен AppBarTheme, а останалата част от приложението — стандартна.

Влагаността на Theme може да бъде неограничена. Flutter използва йерархично търсене: при извикване на Theme.of(context) framework се изкачва нагоре по дървото до първия Theme и връща неговата тема. Ако не бъде намерен Theme — се хвърля изключение. Затова MaterialApp винаги обвива приложението в Theme.

Извод: наследяването на Theme — гъвкав механизъм за изолиране на стилове. Използвайте предефиниране на тема за отделни екрани без дублиране на общата конфигурация.

Dark theme: тъмна тема на приложението

Dark theme — вградената възможност на Flutter за превключване между светла и тъмна тема. ThemeData се съхранява в параметрите theme (светла) и darkTheme (тъмна) на MaterialApp. Параметърът themeMode управлява режима: ThemeMode.light (винаги светла), ThemeMode.dark (винаги тъмна) или ThemeMode.system (следва системните настройки).

При имплементиране на тъмна тема не е необходимо да създавате всички стилове наново. Достатъчно е да предадете ColorScheme.fromSeed(seedColor, brightness: Brightness.dark) в darkTheme. Framework автоматично коригира цветовете за тъмен фон: surface става тъмносиво, текстът — светъл. Останалите подтеми (AppBarTheme, CardTheme, BottomNavigationBarTheme) се наследяват от светлата версия, ако не са изрично предефинирани.

За плавен преход между темите Flutter предоставя анимация чрез AnimatedTheme. При смяна на themeMode цветовете и стиловете се анимират, създавайки визуално приятно превключване. AnimatedTheme използва Duration и Curve за настройка на скоростта на анимация.

Извод: тъмната тема се имплементира чрез darkTheme и ColorScheme.fromSeed с brightness: Brightness.dark. Използвайте ThemeMode.system за следване на системните настройки на потребителя.

Примери за код с Theme и ThemeData

Пример 1 — глобална настройка на тема чрез MaterialApp с използване на ColorScheme.fromSeed.

dart
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(),
);

В този пример се създава ThemeData с Material 3, цветова схема на базата на indigo и персонализирани стилове на AppBar и Card. useMaterial3 активира новата система за теми Material 3, а ColorScheme.fromSeed генерира пълната палитра.

Пример 2 — достъп до темата и предефиниране за част от приложението.

dart
Widget build(BuildContext context) {
  final theme = Theme.of(context);

  return Column(
    children: [
      Text(
        'Стандартно заглавие',
        style: theme.textTheme.headlineMedium,
      ),
      Theme(
        data: theme.copyWith(
          colorScheme: theme.colorScheme.copyWith(
            primary: Colors.orange,
          ),
        ),
        child: const SpecialSection(),
      ),
    ],
  );
}

Вторият пример показва използването на Theme.of(context) за четене на стилове и Theme() с copyWith за предефиниране на темата в част от дървото без промяна на глобалната конфигурация. SpecialSection вътре в Theme ще получи оранжев primary цвят, останалата част от приложението запазва оригиналния indigo.

Извод: глобалната тема се задава в MaterialApp, достъп чрез Theme.of(context), предефиниране чрез Theme с copyWith. За Material 3 използвайте ColorScheme.fromSeed.

Типични грешки при настройка на тема

Грешка 1: използване на primarySwatch вместо ColorScheme в Material 3. primarySwatch е остарял и не се поддържа в Material 3. При използване на useMaterial3: true с primarySwatch Flutter издава предупреждение. Решение: преминете на ColorScheme.fromSeed за генериране на палитра.

Грешка 2: твърдо задаване на цветове в уиджети вместо използване на Theme.of(context). Разработчиците пишат Colors.blue директно в Container, вместо primary или secondary цвят от темата. При смяна на темата такива цветове остават непроменени, нарушавайки единния стил. Решение: винаги използвайте Theme.of(context).colorScheme.primary или други цветове от темата.

Грешка 3: липса на darkTheme. Ако приложението не дефинира darkTheme, при включване на тъмна тема на устройството Flutter се опитва автоматично да адаптира светлата тема, което често дава некачествен резултат. Решение: винаги предавайте darkTheme в MaterialApp, дори ако тъмната тема не е приоритет — това е важен UX за потребителите.

Грешка 4: създаване на нова тема при всяко build. Ако ThemeData се създава вътре в build метода, всяко преизграждане на уиджета създава нов обект на тема, нарушавайки оптимизацията на InheritedWidget. Решение: изнесете ThemeData в статично поле или const конструктор извън build.

Извод: правилната тематизация изисква единен централизиран подход. Изнесете ThemeData в константи, използвайте цветови схеми от темата и винаги дефинирайте и двете теми — светла и тъмна.

Често задавани въпроси

Каква е разликата между Theme и ThemeData?

ThemeData — конфигурационни данни (цветове, шрифтове, стилове). Theme — InheritedWidget, който предава ThemeData надолу по дървото от уиджети. Theme.of(context) връща ThemeData, който най-близкият Theme разпространява в текущия контекст.

Как да превключвам динамично темата на приложението?

Управлявайте themeMode в MaterialApp чрез състоянието на приложението. Променете състоянието от ThemeMode.light на ThemeMode.dark и Flutter ще преизгради цялото дърво с новата тема. За плавна анимация използвайте AnimatedTheme.

Какво е ColorScheme.fromSeed?

ColorScheme.fromSeed — метод на Material 3, който приема един базов цвят (seedColor) и генерира пълна хармонична палитра: primary, secondary, tertiary, surface, error и техните on-варианти. Позволява получаване на съгласувана цветова схема от един цвят на марката.

Как да използвам персонализирани шрифтове с Theme?

Чрез TextTheme вътре в ThemeData: посочете TextStyle с fontFamily за всяко ниво. За Google Fonts използвайте пакета google_fonts, предавайки fontFamily директно в TextTheme. За локални шрифтове — свържете ги чрез pubspec.yaml в секцията fonts.

Може ли да се предефинира темата за един екран?

Да. Обвийте екрана в Theme с предефинирани данни чрез copyWith. Всички дъщерни уиджети вътре в този Theme ще получат променените стилове, а останалата част от приложението ще запази оригиналната тема. Това не създава допълнително натоварване.

Обобщение

  • ThemeData — конфигурационен обект с цветова палитра, типография и стилове на всички Material компоненти
  • ColorScheme.fromSeed — основният начин за генериране на цветова палитра в Material 3 от един цвят на марката
  • Theme.of(context) — достъп до текущата тема за четене на стилове без дублиране на код
  • Theme — InheritedWidget за наследяване и предефиниране на тема в поддървото от уиджети
  • Тъмна тема — отделен ThemeData с brightness: Brightness.dark, управляван чрез themeMode
  • Препоръка: изнесете ThemeData в static или const, винаги задавайте theme и darkTheme в MaterialApp

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също