← Назад к вопросам
Как реализовать тему (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 — это:
- Определение ThemeData с цветами и стилями
- Использование Provider для динамического переключения
- Создание AppColors и AppTextStyles классов
- Доступ к теме через Theme.of(context)
- Сохранение выбора в SharedPreferences