Skip to content
On this page

Flutter主题与样式

在Flutter中,主题(Theming)是统一管理应用视觉外观的重要机制。通过主题,开发者可以轻松地在整个应用中保持一致的设计风格,包括颜色、字体、组件样式等。本章将详细介绍Flutter中的主题系统和样式管理。

主题基础

MaterialApp主题配置

MaterialApp的theme属性是应用主题的入口点:

dart
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Theming Demo',
      theme: ThemeData(
        // 主题颜色
        primarySwatch: Colors.blue,
        // 亮度模式
        brightness: Brightness.light,
        // 应用栏主题
        appBarTheme: AppBarTheme(
          backgroundColor: Colors.blue,
          foregroundColor: Colors.white,
        ),
        // 按钮主题
        elevatedButtonTheme: ElevatedButtonThemeData(
          style: ElevatedButton.styleFrom(
            backgroundColor: Colors.blue, // 背景颜色
            foregroundColor: Colors.white, // 文字颜色
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(10),
            ),
          ),
        ),
      ),
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('主题演示'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              '当前主题',
              style: Theme.of(context).textTheme.headlineSmall,
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {},
              child: Text('主题按钮'),
            ),
          ],
        ),
      ),
    );
  }
}

颜色主题

使用预定义颜色

Flutter提供了丰富的预定义颜色系统:

dart
class ColorThemeDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('颜色主题')),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // 主要颜色
            _buildColorCard(
              'Primary Color',
              Theme.of(context).primaryColor,
            ),
            SizedBox(height: 10),
            // 强调色
            _buildColorCard(
              'Primary Variant',
              Theme.of(context).primaryColorDark,
            ),
            SizedBox(height: 10),
            // 背景色
            _buildColorCard(
              'Background',
              Theme.of(context).canvasColor,
            ),
            SizedBox(height: 10),
            // 片段背景色
            _buildColorCard(
              'Surface',
              Theme.of(context).colorScheme.surface,
            ),
            SizedBox(height: 10),
            // 错误色
            _buildColorCard(
              'Error',
              Theme.of(context).colorScheme.error,
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildColorCard(String title, Color color) {
    return Container(
      padding: EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: color,
        borderRadius: BorderRadius.circular(8),
      ),
      child: Text(
        title,
        style: TextStyle(
          color: ThemeData.estimateBrightnessForColor(color) == Brightness.dark 
              ? Colors.white 
              : Colors.black,
          fontWeight: FontWeight.bold,
        ),
      ),
    );
  }
}

自定义颜色主题

dart
class CustomColorTheme extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Custom Color Theme',
      theme: ThemeData(
        // 使用自定义颜色调色板
        primarySwatch: _createMaterialColor(Color(0xFF6200EE)),
        primaryColor: Color(0xFF6200EE),
        colorScheme: ColorScheme.fromSeed(
          seedColor: Color(0xFF6200EE),
          brightness: Brightness.light,
        ),
        // 自定义其他颜色
        scaffoldBackgroundColor: Color(0xFFF5F5F5),
        cardColor: Colors.white,
      ),
      home: CustomColorHomePage(),
    );
  }

  MaterialColor _createMaterialColor(Color color) {
    List strengths = <double>[.05];
    Map<int, Color> swatch = {};
    final int r = color.red, g = color.green, b = color.blue;

    for (int i = 1; i < 10; i++) {
      strengths.add(0.1 * i);
    }
    for (var strength in strengths) {
      final double ds = 0.5 - strength;
      swatch[(strength * 1000).round()] = Color.fromRGBO(
        r + ((ds < 0 ? r : (255 - r)) * ds).round(),
        g + ((ds < 0 ? g : (255 - g)) * ds).round(),
        b + ((ds < 0 ? b : (255 - b)) * ds).round(),
        1,
      );
    }
    return MaterialColor(color.value, swatch);
  }
}

class CustomColorHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('自定义颜色主题'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {},
          child: Text('自定义主题按钮'),
        ),
      ),
    );
  }
}

字体主题

全局字体配置

dart
import 'package:google_fonts/google_fonts.dart';

class TypographyThemeDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Typography Theme Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        // 使用Google Fonts
        textTheme: GoogleFonts.robotoTextTheme(
          Theme.of(context).textTheme,
        ),
        // 自定义字体族
        fontFamily: 'Roboto',
      ),
      home: TypographyHomePage(),
    );
  }
}

class TypographyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final textTheme = Theme.of(context).textTheme;
    
    return Scaffold(
      appBar: AppBar(title: Text('字体主题')),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text('Display Large', style: textTheme.displayLarge),
            SizedBox(height: 10),
            Text('Display Medium', style: textTheme.displayMedium),
            SizedBox(height: 10),
            Text('Display Small', style: textTheme.displaySmall),
            SizedBox(height: 10),
            Text('Headline Large', style: textTheme.headlineLarge),
            SizedBox(height: 10),
            Text('Headline Medium', style: textTheme.headlineMedium),
            SizedBox(height: 10),
            Text('Headline Small', style: textTheme.headlineSmall),
            SizedBox(height: 10),
            Text('Title Large', style: textTheme.titleLarge),
            SizedBox(height: 10),
            Text('Title Medium', style: textTheme.titleMedium),
            SizedBox(height: 10),
            Text('Title Small', style: textTheme.titleSmall),
            SizedBox(height: 10),
            Text('Body Large', style: textTheme.bodyLarge),
            SizedBox(height: 10),
            Text('Body Medium', style: textTheme.bodyMedium),
            SizedBox(height: 10),
            Text('Body Small', style: textTheme.bodySmall),
            SizedBox(height: 10),
            Text('Label Large', style: textTheme.labelLarge),
            SizedBox(height: 10),
            Text('Label Medium', style: textTheme.labelMedium),
            SizedBox(height: 10),
            Text('Label Small', style: textTheme.labelSmall),
          ],
        ),
      ),
    );
  }
}

自定义字体主题

dart
class CustomTypographyDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Custom Typography Demo',
      theme: ThemeData(
        primarySwatch: Colors.indigo,
        textTheme: TextTheme(
          // 自定义标题样式
          headlineSmall: TextStyle(
            fontSize: 24,
            fontWeight: FontWeight.bold,
            color: Colors.indigo[900],
            letterSpacing: 0.5,
          ),
          // 自定义正文样式
          bodyMedium: TextStyle(
            fontSize: 16,
            color: Colors.grey[700],
            height: 1.5,
          ),
          // 自定义按钮文字样式
          labelLarge: TextStyle(
            fontSize: 16,
            fontWeight: FontWeight.w600,
            letterSpacing: 0.5,
          ),
        ),
      ),
      home: CustomTypographyPage(),
    );
  }
}

class CustomTypographyPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('自定义字体主题')),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              '自定义标题样式',
              style: Theme.of(context).textTheme.headlineSmall,
            ),
            SizedBox(height: 20),
            Text(
              '这是使用自定义正文样式的文本,展示了如何通过主题来自定义字体属性。',
              style: Theme.of(context).textTheme.bodyMedium,
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {},
              child: Text('自定义按钮文字'),
            ),
          ],
        ),
      ),
    );
  }
}

组件主题

按钮主题定制

dart
class ButtonThemeDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Button Theme Demo',
      theme: ThemeData(
        primarySwatch: Colors.purple,
        // 自定义ElevatedButton主题
        elevatedButtonTheme: ElevatedButtonThemeData(
          style: ElevatedButton.styleFrom(
            backgroundColor: Colors.purple,
            foregroundColor: Colors.white,
            padding: EdgeInsets.symmetric(horizontal: 32, vertical: 16),
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(25),
            ),
            elevation: 4,
          ),
        ),
        // 自定义TextButton主题
        textButtonTheme: TextButtonThemeData(
          style: TextButton.styleFrom(
            foregroundColor: Colors.purple,
            padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(8),
              side: BorderSide(color: Colors.purple, width: 1),
            ),
          ),
        ),
        // 自定义OutlinedButton主题
        outlinedButtonTheme: OutlinedButtonThemeData(
          style: OutlinedButton.styleFrom(
            foregroundColor: Colors.purple,
            side: BorderSide(color: Colors.purple, width: 2),
            padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
          ),
        ),
      ),
      home: ButtonThemePage(),
    );
  }
}

class ButtonThemePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('按钮主题定制')),
      body: Center(
        child: Padding(
          padding: EdgeInsets.all(16.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: () {},
                child: Text('Elevated Button'),
              ),
              SizedBox(height: 16),
              TextButton(
                onPressed: () {},
                child: Text('Text Button'),
              ),
              SizedBox(height: 16),
              OutlinedButton(
                onPressed: () {},
                child: Text('Outlined Button'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

输入框主题定制

dart
class TextFieldThemeDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'TextField Theme Demo',
      theme: ThemeData(
        primarySwatch: Colors.teal,
        inputDecorationTheme: InputDecorationTheme(
          // 边框样式
          border: OutlineInputBorder(
            borderRadius: BorderRadius.circular(12),
          ),
          enabledBorder: OutlineInputBorder(
            borderRadius: BorderRadius.circular(12),
            borderSide: BorderSide(color: Colors.grey.shade300),
          ),
          focusedBorder: OutlineInputBorder(
            borderRadius: BorderRadius.circular(12),
            borderSide: BorderSide(color: Colors.teal, width: 2),
          ),
          // 标签样式
          labelStyle: TextStyle(
            color: Colors.teal,
            fontWeight: FontWeight.w600,
          ),
          // 提示文字样式
          hintStyle: TextStyle(
            color: Colors.grey.shade500,
            fontSize: 14,
          ),
        ),
      ),
      home: TextFieldThemePage(),
    );
  }
}

class TextFieldThemePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('输入框主题')),
      body: Padding(
        padding: EdgeInsets.all(24.0),
        child: Column(
          children: [
            TextField(
              decoration: InputDecoration(
                labelText: '用户名',
                hintText: '请输入用户名',
              ),
            ),
            SizedBox(height: 16),
            TextField(
              decoration: InputDecoration(
                labelText: '邮箱',
                hintText: '请输入邮箱地址',
              ),
            ),
            SizedBox(height: 16),
            TextField(
              decoration: InputDecoration(
                labelText: '密码',
                hintText: '请输入密码',
                suffixIcon: Icon(Icons.visibility),
              ),
              obscureText: true,
            ),
          ],
        ),
      ),
    );
  }
}

暗色主题

实现亮色/暗色主题切换

dart
import 'package:flutter/scheduler.dart';

class DarkThemeDemo extends StatefulWidget {
  @override
  _DarkThemeDemoState createState() => _DarkThemeDemoState();
}

class _DarkThemeDemoState extends State<DarkThemeDemo> {
  // 检测系统主题模式
  bool _isDarkTheme = SchedulerBinding.instance.window.platformBrightness == Brightness.dark;

  void _toggleTheme() {
    setState(() {
      _isDarkTheme = !_isDarkTheme;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Dark Theme Demo',
      theme: _buildLightTheme(),
      darkTheme: _buildDarkTheme(),
      themeMode: _isDarkTheme ? ThemeMode.dark : ThemeMode.light,
      home: DarkThemeHomePage(toggleTheme: _toggleTheme),
    );
  }

  ThemeData _buildLightTheme() {
    return ThemeData(
      primarySwatch: Colors.deepPurple,
      brightness: Brightness.light,
      appBarTheme: AppBarTheme(
        backgroundColor: Colors.deepPurple,
        foregroundColor: Colors.white,
      ),
      cardTheme: CardTheme(
        color: Colors.white,
        elevation: 4,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(12),
        ),
      ),
      scaffoldBackgroundColor: Colors.grey[100],
    );
  }

  ThemeData _buildDarkTheme() {
    return ThemeData(
      primarySwatch: Colors.orange,
      brightness: Brightness.dark,
      appBarTheme: AppBarTheme(
        backgroundColor: Colors.grey[900],
        foregroundColor: Colors.white,
      ),
      cardTheme: CardTheme(
        color: Colors.grey[850],
        elevation: 4,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(12),
        ),
      ),
      scaffoldBackgroundColor: Colors.grey[900],
      textTheme: TextTheme(
        bodyMedium: TextStyle(color: Colors.white),
      ),
    );
  }
}

class DarkThemeHomePage extends StatelessWidget {
  final VoidCallback toggleTheme;

  const DarkThemeHomePage({Key? key, required this.toggleTheme}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('亮色/暗色主题'),
        actions: [
          IconButton(
            icon: Icon(
              Theme.of(context).brightness == Brightness.dark
                  ? Icons.light_mode
                  : Icons.dark_mode,
            ),
            onPressed: toggleTheme,
          ),
        ],
      ),
      body: Center(
        child: Padding(
          padding: EdgeInsets.all(16.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Card(
                child: Padding(
                  padding: EdgeInsets.all(16.0),
                  child: Column(
                    children: [
                      Text(
                        '当前主题模式',
                        style: Theme.of(context).textTheme.headlineSmall,
                      ),
                      SizedBox(height: 10),
                      Text(
                        Theme.of(context).brightness == Brightness.dark
                            ? '暗色主题'
                            : '亮色主题',
                        style: TextStyle(fontSize: 18),
                      ),
                    ],
                  ),
                ),
              ),
              SizedBox(height: 20),
              ElevatedButton(
                onPressed: toggleTheme,
                child: Text('切换主题'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

动态主题切换

使用Provider管理主题状态

dart
import 'package:flutter/foundation.dart';
import 'package:provider/provider.dart';

// 主题状态管理器
class ThemeProvider extends ChangeNotifier {
  bool _isDarkTheme = false;

  bool get isDarkTheme => _isDarkTheme;

  ThemeData get theme => _isDarkTheme ? _darkTheme : _lightTheme;

  void toggleTheme() {
    _isDarkTheme = !_isDarkTheme;
    notifyListeners();
  }

  void setDarkTheme(bool isDark) {
    _isDarkTheme = isDark;
    notifyListeners();
  }

  static ThemeData _lightTheme = ThemeData(
    primarySwatch: Colors.blue,
    brightness: Brightness.light,
    appBarTheme: AppBarTheme(
      backgroundColor: Colors.blue,
      foregroundColor: Colors.white,
    ),
    elevatedButtonTheme: ElevatedButtonThemeData(
      style: ElevatedButton.styleFrom(
        backgroundColor: Colors.blue,
        foregroundColor: Colors.white,
      ),
    ),
  );

  static ThemeData _darkTheme = ThemeData(
    primarySwatch: Colors.orange,
    brightness: Brightness.dark,
    appBarTheme: AppBarTheme(
      backgroundColor: Colors.grey[900],
      foregroundColor: Colors.white,
    ),
    elevatedButtonTheme: ElevatedButtonThemeData(
      style: ElevatedButton.styleFrom(
        backgroundColor: Colors.orange,
        foregroundColor: Colors.black,
      ),
    ),
  );
}

class DynamicThemeDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => ThemeProvider(),
      child: Consumer<ThemeProvider>(
        builder: (context, themeProvider, child) {
          return MaterialApp(
            title: 'Dynamic Theme Demo',
            theme: themeProvider.theme,
            home: DynamicThemeHomePage(),
          );
        },
      ),
    );
  }
}

class DynamicThemeHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('动态主题切换'),
        actions: [
          IconButton(
            icon: Icon(
              Provider.of<ThemeProvider>(context).isDarkTheme
                  ? Icons.light_mode
                  : Icons.dark_mode,
            ),
            onPressed: () {
              Provider.of<ThemeProvider>(context, listen: false).toggleTheme();
            },
          ),
        ],
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              '动态主题演示',
              style: Theme.of(context).textTheme.headlineSmall,
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                Provider.of<ThemeProvider>(context, listen: false).toggleTheme();
              },
              child: Text('切换主题'),
            ),
            SizedBox(height: 20),
            Card(
              child: Padding(
                padding: EdgeInsets.all(16.0),
                child: Text(
                  '这是一个卡片组件,颜色会根据主题自动调整',
                  style: Theme.of(context).textTheme.bodyMedium,
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

主题继承与覆盖

使用Theme.of()访问主题

dart
class ThemeInheritanceDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('主题继承与覆盖')),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          children: [
            // 使用默认主题
            _buildThemedWidget(context, '默认主题'),
            
            SizedBox(height: 20),
            
            // 使用Theme覆盖
            Theme(
              data: Theme.of(context).copyWith(
                textTheme: Theme.of(context).textTheme.copyWith(
                  headlineSmall: TextStyle(
                    color: Colors.red,
                    fontSize: 20,
                    fontWeight: FontWeight.bold,
                  ),
                ),
                elevatedButtonTheme: ElevatedButtonThemeData(
                  style: ElevatedButton.styleFrom(
                    backgroundColor: Colors.red,
                    foregroundColor: Colors.white,
                  ),
                ),
              ),
              child: _buildThemedWidget(context, '红色主题覆盖'),
            ),
            
            SizedBox(height: 20),
            
            // 使用自定义主题
            _buildCustomThemedSection(context),
          ],
        ),
      ),
    );
  }

  Widget _buildThemedWidget(BuildContext context, String title) {
    return Card(
      child: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          children: [
            Text(
              title,
              style: Theme.of(context).textTheme.headlineSmall,
            ),
            SizedBox(height: 10),
            Text(
              '这是正文文本',
              style: Theme.of(context).textTheme.bodyMedium,
            ),
            SizedBox(height: 10),
            ElevatedButton(
              onPressed: () {},
              child: Text('按钮'),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildCustomThemedSection(BuildContext context) {
    final customTheme = Theme.of(context).copyWith(
      primaryColor: Colors.green,
      colorScheme: Theme.of(context).colorScheme.copyWith(
        primary: Colors.green,
        secondary: Colors.lightGreen,
      ),
    );

    return Theme(
      data: customTheme,
      child: Card(
        color: customTheme.primaryColor,
        child: Padding(
          padding: EdgeInsets.all(16.0),
          child: Column(
            children: [
              Text(
                '绿色主题区域',
                style: customTheme.textTheme.headlineSmall?.copyWith(
                  color: Colors.white,
                ),
              ),
              SizedBox(height: 10),
              Text(
                '这个区域使用了自定义主题',
                style: customTheme.textTheme.bodyMedium?.copyWith(
                  color: Colors.white70,
                ),
              ),
              SizedBox(height: 10),
              ElevatedButton(
                onPressed: () {},
                child: Text('绿色按钮'),
                style: ElevatedButton.styleFrom(
                  backgroundColor: Colors.white,
                  foregroundColor: Colors.green,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

响应式主题设计

根据屏幕尺寸调整主题

dart
class ResponsiveThemeDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final screenWidth = MediaQuery.of(context).size.width;
    
    // 根据屏幕宽度调整主题
    ThemeData responsiveTheme = screenWidth > 600 
        ? _buildTabletTheme() 
        : _buildMobileTheme();

    return MaterialApp(
      title: 'Responsive Theme Demo',
      theme: responsiveTheme,
      home: ResponsiveThemeHomePage(),
    );
  }

  ThemeData _buildMobileTheme() {
    return ThemeData(
      primarySwatch: Colors.blue,
      textTheme: TextTheme(
        headlineSmall: TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
        bodyMedium: TextStyle(fontSize: 16),
      ),
      elevatedButtonTheme: ElevatedButtonThemeData(
        style: ElevatedButton.styleFrom(
          padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
          textStyle: TextStyle(fontSize: 16),
        ),
      ),
    );
  }

  ThemeData _buildTabletTheme() {
    return ThemeData(
      primarySwatch: Colors.purple,
      textTheme: TextTheme(
        headlineSmall: TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
        bodyMedium: TextStyle(fontSize: 18),
      ),
      elevatedButtonTheme: ElevatedButtonThemeData(
        style: ElevatedButton.styleFrom(
          padding: EdgeInsets.symmetric(horizontal: 30, vertical: 16),
          textStyle: TextStyle(fontSize: 18),
        ),
      ),
    );
  }
}

class ResponsiveThemeHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final screenWidth = MediaQuery.of(context).size.width;
    
    return Scaffold(
      appBar: AppBar(
        title: Text(screenWidth > 600 ? '平板主题' : '手机主题'),
      ),
      body: Center(
        child: Padding(
          padding: EdgeInsets.all(screenWidth > 600 ? 32.0 : 16.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(
                '屏幕宽度: ${screenWidth.toStringAsFixed(0)}px',
                style: Theme.of(context).textTheme.headlineSmall,
              ),
              SizedBox(height: 20),
              Text(
                screenWidth > 600 
                    ? '检测到平板尺寸,使用大屏主题' 
                    : '检测到手机尺寸,使用小屏主题',
                textAlign: TextAlign.center,
                style: Theme.of(context).textTheme.bodyMedium,
              ),
              SizedBox(height: 20),
              ElevatedButton(
                onPressed: () {},
                child: Text('响应式按钮'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

主题持久化

保存主题设置到本地

dart
import 'package:shared_preferences/shared_preferences.dart';

class PersistentThemeDemo extends StatefulWidget {
  @override
  _PersistentThemeDemoState createState() => _PersistentThemeDemoState();
}

class _PersistentThemeDemoState extends State<PersistentThemeDemo> {
  bool _isDarkTheme = false;
  bool _isLoading = true;

  @override
  void initState() {
    super.initState();
    _loadTheme();
  }

  Future<void> _loadTheme() async {
    final prefs = await SharedPreferences.getInstance();
    setState(() {
      _isDarkTheme = prefs.getBool('is_dark_theme') ?? false;
      _isLoading = false;
    });
  }

  Future<void> _saveTheme(bool isDark) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setBool('is_dark_theme', isDark);
  }

  void _toggleTheme() async {
    setState(() {
      _isDarkTheme = !_isDarkTheme;
    });
    await _saveTheme(_isDarkTheme);
  }

  @override
  Widget build(BuildContext context) {
    if (_isLoading) {
      return MaterialApp(
        home: Scaffold(
          body: Center(child: CircularProgressIndicator()),
        ),
      );
    }

    return MaterialApp(
      title: 'Persistent Theme Demo',
      theme: _isDarkTheme ? _darkTheme() : _lightTheme(),
      darkTheme: _darkTheme(),
      themeMode: _isDarkTheme ? ThemeMode.dark : ThemeMode.light,
      home: PersistentThemeHomePage(toggleTheme: _toggleTheme),
    );
  }

  ThemeData _lightTheme() {
    return ThemeData(
      primarySwatch: Colors.blue,
      brightness: Brightness.light,
    );
  }

  ThemeData _darkTheme() {
    return ThemeData(
      primarySwatch: Colors.deepOrange,
      brightness: Brightness.dark,
    );
  }
}

class PersistentThemeHomePage extends StatelessWidget {
  final VoidCallback toggleTheme;

  const PersistentThemeHomePage({Key? key, required this.toggleTheme}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('主题持久化'),
        actions: [
          IconButton(
            icon: Icon(
              Theme.of(context).brightness == Brightness.dark
                  ? Icons.light_mode
                  : Icons.dark_mode,
            ),
            onPressed: toggleTheme,
          ),
        ],
      ),
      body: Center(
        child: Padding(
          padding: EdgeInsets.all(16.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Icon(
                Theme.of(context).brightness == Brightness.dark
                    ? Icons.dark_mode
                    : Icons.light_mode,
                size: 80,
                color: Theme.of(context).primaryColor,
              ),
              SizedBox(height: 20),
              Text(
                '主题设置会在应用重启后保持',
                textAlign: TextAlign.center,
                style: Theme.of(context).textTheme.headlineSmall,
              ),
              SizedBox(height: 20),
              ElevatedButton(
                onPressed: toggleTheme,
                child: Text('切换主题'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

主题最佳实践

1. 创建主题工具类

dart
class AppTheme {
  // 定义应用常量
  static const Color primaryColor = Color(0xFF6200EE);
  static const Color secondaryColor = Color(0xFFFF6B6B);
  static const Color backgroundColor = Color(0xFFF5F5F5);
  static const Color surfaceColor = Color(0xFFFFFFFF);
  static const Color errorColor = Color(0xFFB00020);
  
  // 定义字体大小
  static const TextTheme lightTextTheme = TextTheme(
    displayLarge: TextStyle(fontSize: 96, fontWeight: FontWeight.w300),
    displayMedium: TextStyle(fontSize: 60, fontWeight: FontWeight.w300),
    displaySmall: TextStyle(fontSize: 48, fontWeight: FontWeight.w400),
    headlineMedium: TextStyle(fontSize: 34, fontWeight: FontWeight.w400),
    headlineSmall: TextStyle(fontSize: 24, fontWeight: FontWeight.w400),
    titleLarge: TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
    titleMedium: TextStyle(fontSize: 16, fontWeight: FontWeight.w400),
    titleSmall: TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
    bodyLarge: TextStyle(fontSize: 16, fontWeight: FontWeight.w400),
    bodyMedium: TextStyle(fontSize: 14, fontWeight: FontWeight.w400),
    bodySmall: TextStyle(fontSize: 12, fontWeight: FontWeight.w400),
    labelLarge: TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
  );

  static ThemeData get lightTheme => ThemeData(
    primarySwatch: Colors.blue,
    brightness: Brightness.light,
    colorScheme: ColorScheme.fromSeed(
      seedColor: primaryColor,
      brightness: Brightness.light,
    ),
    textTheme: lightTextTheme,
    appBarTheme: AppBarTheme(
      backgroundColor: primaryColor,
      foregroundColor: Colors.white,
    ),
  );

  static ThemeData get darkTheme => ThemeData(
    primarySwatch: Colors.orange,
    brightness: Brightness.dark,
    colorScheme: ColorScheme.fromSeed(
      seedColor: Colors.orange,
      brightness: Brightness.dark,
    ),
    textTheme: lightTextTheme.apply(
      bodyColor: Colors.white,
      displayColor: Colors.white,
    ),
    appBarTheme: AppBarTheme(
      backgroundColor: Colors.grey[900],
      foregroundColor: Colors.white,
    ),
  );
}

2. 主题使用建议

dart
class ThemeBestPractices extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('主题最佳实践'),
        // 使用主题中的颜色
        backgroundColor: Theme.of(context).primaryColor,
        foregroundColor: Theme.of(context).colorScheme.onPrimary,
      ),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          children: [
            // 使用主题文本样式
            Text(
              '使用主题文本样式',
              style: Theme.of(context).textTheme.headlineSmall,
            ),
            SizedBox(height: 20),
            // 使用主题颜色
            Container(
              width: 100,
              height: 100,
              color: Theme.of(context).colorScheme.primary,
              child: Center(
                child: Text(
                  '主题色',
                  style: TextStyle(
                    color: Theme.of(context).colorScheme.onPrimary,
                  ),
                ),
              ),
            ),
            SizedBox(height: 20),
            // 使用主题按钮样式
            ElevatedButton(
              onPressed: () {},
              style: Theme.of(context).elevatedButtonTheme.style,
              child: Text('主题按钮'),
            ),
          ],
        ),
      ),
    );
  }
}

总结

Flutter的主题系统提供了强大而灵活的UI定制能力:

  1. 基础主题配置:通过MaterialApp的theme属性配置全局主题
  2. 颜色主题:使用预定义颜色或自定义颜色系统
  3. 字体主题:配置全局字体族和文本样式
  4. 组件主题:定制各种组件的外观和行为
  5. 暗色主题:实现亮色/暗色模式切换
  6. 动态主题:使用状态管理实现运行时主题切换
  7. 主题继承:通过Theme组件覆盖局部主题
  8. 主题持久化:将用户选择的主题设置保存到本地

合理使用主题系统可以显著提高开发效率,确保应用UI的一致性,并为用户提供更好的视觉体验.