← Назад к вопросам

Как реализовать тему (Theme) в Flutter-приложении?

1.0 Junior🔥 132 комментариев
#Flutter виджеты

Комментарии (2)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Реализация темы (Theme) в Flutter

Встроенная система ThemeData

ThemeData — это встроенный класс Flutter для определения цветов, шрифтов и стилей приложения:

import "package:flutter/material.dart";

void main() {
  runApp(
    MaterialApp(
      title: "My App",
      theme: ThemeData(
        // Основной цвет
        primaryColor: Colors.blue,
        primarySwatch: Colors.blue,
        
        // Цветовая схема (Material 3)
        colorScheme: ColorScheme.fromSeed(
          seedColor: Colors.blue,
          brightness: Brightness.light,
        ),
        
        // Шрифты
        fontFamily: "Roboto",
        textTheme: TextTheme(
          displayLarge: TextStyle(
            fontSize: 32,
            fontWeight: FontWeight.bold,
          ),
          titleMedium: TextStyle(
            fontSize: 18,
            fontWeight: FontWeight.w500,
          ),
          bodyMedium: TextStyle(
            fontSize: 14,
            color: Colors.grey[700],
          ),
        ),
        
        // Кнопки
        elevatedButtonTheme: ElevatedButtonThemeData(
          style: ElevatedButton.styleFrom(
            backgroundColor: Colors.blue,
            foregroundColor: Colors.white,
            padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(8),
            ),
          ),
        ),
        
        // AppBar
        appBarTheme: AppBarTheme(
          backgroundColor: Colors.blue,
          foregroundColor: Colors.white,
          elevation: 0,
        ),
        
        // Поля ввода
        inputDecorationTheme: InputDecorationTheme(
          border: OutlineInputBorder(
            borderRadius: BorderRadius.circular(8),
          ),
          contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
        ),
      ),
      darkTheme: ThemeData(
        brightness: Brightness.dark,
        colorScheme: ColorScheme.fromSeed(
          seedColor: Colors.blue,
          brightness: Brightness.dark,
        ),
      ),
      themeMode: ThemeMode.system, // Следить за системными настройками
      home: MyHomePage(),
    ),
  );
}

Динамическое переключение темы

Используйте Provider для управления темой:

import "package:flutter/material.dart";
import "package:provider/provider.dart";

class ThemeProvider extends ChangeNotifier {
  ThemeMode _themeMode = ThemeMode.light;

  ThemeMode get themeMode => _themeMode;

  void setThemeMode(ThemeMode mode) {
    _themeMode = mode;
    notifyListeners();
  }

  void toggleTheme() {
    _themeMode = _themeMode == ThemeMode.light
        ? ThemeMode.dark
        : ThemeMode.light;
    notifyListeners();
  }
}

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (_) => ThemeProvider(),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer<ThemeProvider>(
      builder: (context, themeProvider, child) {
        return MaterialApp(
          title: "My App",
          theme: _getLightTheme(),
          darkTheme: _getDarkTheme(),
          themeMode: themeProvider.themeMode,
          home: MyHomePage(),
        );
      },
    );
  }

  ThemeData _getLightTheme() {
    return ThemeData(
      brightness: Brightness.light,
      primaryColor: Colors.blue,
      scaffoldBackgroundColor: Colors.white,
      colorScheme: ColorScheme.light(
        primary: Colors.blue,
        secondary: Colors.amber,
      ),
    );
  }

  ThemeData _getDarkTheme() {
    return ThemeData(
      brightness: Brightness.dark,
      primaryColor: Colors.blue,
      scaffoldBackgroundColor: Colors.grey[900],
      colorScheme: ColorScheme.dark(
        primary: Colors.blue[300],
        secondary: Colors.amber[300],
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("My App")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text("Current Theme"),
            SizedBox(height: 16),
            ElevatedButton(
              onPressed: () {
                context.read<ThemeProvider>().toggleTheme();
              },
              child: Text("Toggle Theme"),
            ),
          ],
        ),
      ),
    );
  }
}

Собственная цветовая палитра

Создайте AppColors класс для централизации всех цветов:

class AppColors {
  // Светлая тема
  static const Color lightPrimary = Color(0xFF2196F3);
  static const Color lightAccent = Color(0xFFFFC107);
  static const Color lightBackground = Color(0xFFFFFFFF);
  static const Color lightSurface = Color(0xFFF5F5F5);
  static const Color lightText = Color(0xFF212121);
  static const Color lightHint = Color(0xFF9E9E9E);

  // Тёмная тема
  static const Color darkPrimary = Color(0xFF1976D2);
  static const Color darkAccent = Color(0xFFFFCA28);
  static const Color darkBackground = Color(0xFF121212);
  static const Color darkSurface = Color(0xFF1E1E1E);
  static const Color darkText = Color(0xFFFFFFFF);
  static const Color darkHint = Color(0xFFBDBDBD);
}

class AppTextStyles {
  static const TextStyle heading1 = TextStyle(
    fontSize: 32,
    fontWeight: FontWeight.bold,
    letterSpacing: 0.5,
  );

  static const TextStyle heading2 = TextStyle(
    fontSize: 24,
    fontWeight: FontWeight.w600,
  );

  static const TextStyle bodyLarge = TextStyle(
    fontSize: 16,
    height: 1.5,
  );

  static const TextStyle bodySmall = TextStyle(
    fontSize: 12,
    color: AppColors.lightHint,
  );
}

// Использование
class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final isDark = Theme.of(context).brightness == Brightness.dark;
    final primaryColor = isDark ? AppColors.darkPrimary : AppColors.lightPrimary;

    return Container(
      color: isDark ? AppColors.darkBackground : AppColors.lightBackground,
      child: Text(
        "Hello",
        style: AppTextStyles.heading1.copyWith(color: primaryColor),
      ),
    );
  }
}

Использование Theme.of() для доступа к текущей теме

class ThemedButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // Получить текущую тему
    final theme = Theme.of(context);
    final isDark = theme.brightness == Brightness.dark;

    return ElevatedButton(
      style: ElevatedButton.styleFrom(
        backgroundColor: theme.colorScheme.primary,
        foregroundColor: isDark ? Colors.black : Colors.white,
      ),
      onPressed: () {},
      child: Text("Themed Button"),
    );
  }
}

class ThemedCard extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);

    return Card(
      color: theme.colorScheme.surface,
      child: Padding(
        padding: EdgeInsets.all(16),
        child: Text(
          "Card content",
          style: theme.textTheme.bodyMedium,
        ),
      ),
    );
  }
}

Material 3 ColorScheme

const colorScheme = ColorScheme(
  brightness: Brightness.light,
  primary: Color(0xFF2196F3),
  onPrimary: Color(0xFFFFFFFF),
  primaryContainer: Color(0xFFB3E5FC),
  onPrimaryContainer: Color(0xFF01579B),
  secondary: Color(0xFFFFC107),
  onSecondary: Color(0xFF000000),
  secondaryContainer: Color(0xFFFFE082),
  onSecondaryContainer: Color(0xFFF57F17),
  tertiary: Color(0xFF4CAF50),
  onTertiary: Color(0xFFFFFFFF),
  tertiaryContainer: Color(0xFFC8E6C9),
  onTertiaryContainer: Color(0xFF1B5E20),
  error: Color(0xFFB00020),
  onError: Color(0xFFFFFFFF),
  errorContainer: Color(0xFFF9DEDC),
  onErrorContainer: Color(0xFF410E0B),
  background: Color(0xFFFAFAFA),
  onBackground: Color(0xFF1C1B1F),
  surface: Color(0xFFFAFAFA),
  onSurface: Color(0xFF1C1B1F),
  surfaceVariant: Color(0xFFEDEDED),
  onSurfaceVariant: Color(0xFF49454E),
  outline: Color(0xFF79747E),
);

Сохранение выбора темы

import "package:shared_preferences/shared_preferences.dart";

class ThemeProvider extends ChangeNotifier {
  ThemeMode _themeMode = ThemeMode.light;

  ThemeMode get themeMode => _themeMode;

  Future<void> loadThemeMode() async {
    final prefs = await SharedPreferences.getInstance();
    final themeName = prefs.getString("themeMode") ?? "light";
    
    _themeMode = themeName == "dark" ? ThemeMode.dark : ThemeMode.light;
    notifyListeners();
  }

  Future<void> setThemeMode(ThemeMode mode) async {
    _themeMode = mode;
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString(
      "themeMode",
      mode == ThemeMode.dark ? "dark" : "light",
    );
    notifyListeners();
  }
}

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  final themeProvider = ThemeProvider();
  await themeProvider.loadThemeMode();
  
  runApp(
    ChangeNotifierProvider.value(
      value: themeProvider,
      child: MyApp(),
    ),
  );
}

Best Practices

  • Централизуйте цвета в AppColors классе
  • Используйте ColorScheme вместо отдельных цветов
  • Тестируйте обе темы (light и dark)
  • Следуйте Material Design гайдлайнам
  • Сохраняйте выбор пользователя в SharedPreferences
  • Используйте Theme.of() вместо хардкода цветов

Итог

Темирование в Flutter — это:

  1. Определение ThemeData с цветами и стилями
  2. Использование Provider для динамического переключения
  3. Создание AppColors и AppTextStyles классов
  4. Доступ к теме через Theme.of(context)
  5. Сохранение выбора в SharedPreferences
Как реализовать тему (Theme) в Flutter-приложении? | PrepBro