Skip to content

第11章:Flutter 样式与主题

11.1 Flutter 样式基础

文本样式(TextStyle)

TextStyle 用于定义文本的样式,包括字体、颜色、粗细、间距等。

常用属性

属性类型描述
colorColor文本颜色
fontSizedouble字体大小
fontWeightFontWeight字体粗细(bold、normal等)
fontStyleFontStyle字体样式(normal、italic)
letterSpacingdouble字母间距
wordSpacingdouble单词间距
lineHeightdouble行高
decorationTextDecoration文本装饰(underline、lineThrough等)
fontFamilyString字体系列

使用示例

dart
Text(
  'Hello Flutter!',
  style: TextStyle(
    color: Colors.blue,
    fontSize: 24,
    fontWeight: FontWeight.bold,
    fontStyle: FontStyle.italic,
    letterSpacing: 2.0,
    wordSpacing: 4.0,
    decoration: TextDecoration.underline,
    decorationColor: Colors.red,
    decorationStyle: TextDecorationStyle.wavy,
  ),
);

容器样式(BoxDecoration)

BoxDecoration 用于定义容器的样式,包括背景色、边框、圆角、阴影、渐变等。

常用属性

属性类型描述
colorColor背景颜色
borderBoxBorder边框
borderRadiusBorderRadius圆角
boxShadowList<BoxShadow>阴影
gradientGradient渐变
imageDecorationImage背景图片

使用示例

dart
Container(
  width: 200,
  height: 200,
  decoration: BoxDecoration(
    color: Colors.white,
    border: Border.all(
      color: Colors.blue,
      width: 2,
    ),
    borderRadius: BorderRadius.circular(10),
    boxShadow: [
      BoxShadow(
        color: Colors.grey.withOpacity(0.5),
        spreadRadius: 5,
        blurRadius: 7,
        offset: Offset(0, 3), // 阴影偏移
      ),
    ],
    gradient: LinearGradient(
      begin: Alignment.topLeft,
      end: Alignment.bottomRight,
      colors: [Colors.blue, Colors.purple],
    ),
  ),
  child: Center(
    child: Text('Styled Container'),
  ),
);

11.2 主题(Theme)配置

全局主题

通过 MaterialApptheme 属性配置全局主题,应用于整个应用。

基本配置

dart
MaterialApp(
  theme: ThemeData(
    // 主色调
    primaryColor: Colors.blue,
    primarySwatch: Colors.blue,
    // 次要色调
    accentColor: Colors.orange,
    // 背景色
    backgroundColor: Colors.grey[100],
    // 卡片颜色
    cardColor: Colors.white,
    // 文本主题
    textTheme: TextTheme(
      headline1: TextStyle(
        fontSize: 32,
        fontWeight: FontWeight.bold,
        color: Colors.black,
      ),
      bodyText1: TextStyle(
        fontSize: 16,
        color: Colors.grey[800],
      ),
      bodyText2: TextStyle(
        fontSize: 14,
        color: Colors.grey[600],
      ),
    ),
    // 按钮主题
    buttonTheme: ButtonThemeData(
      buttonColor: Colors.blue,
      textTheme: ButtonTextTheme.primary,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(8),
      ),
    ),
    // 输入框主题
    inputDecorationTheme: InputDecorationTheme(
      border: OutlineInputBorder(
        borderRadius: BorderRadius.circular(8),
      ),
      focusedBorder: OutlineInputBorder(
        borderSide: BorderSide(color: Colors.blue, width: 2),
        borderRadius: BorderRadius.circular(8),
      ),
    ),
  ),
  home: HomePage(),
);

局部主题

使用 Theme 组件覆盖全局主题,实现局部样式差异。

使用示例

dart
Theme(
  data: ThemeData(
    primaryColor: Colors.green,
    accentColor: Colors.yellow,
    textTheme: TextTheme(
      bodyText1: TextStyle(color: Colors.green),
    ),
  ),
  child: Container(
    child: Text('Local Theme Text'),
  ),
);

主题数据获取

使用 Theme.of(context) 获取当前主题数据:

dart
Container(
  color: Theme.of(context).primaryColor,
  child: Text(
    'Themed Text',
    style: Theme.of(context).textTheme.bodyText1,
  ),
);

11.3 字体与图标自定义

自定义字体导入

步骤 1:添加字体文件

将字体文件(.ttf 或 .otf)添加到项目的 assets/fonts 目录中。

步骤 2:配置 pubspec.yaml

yaml
flutter:
  fonts:
    - family: CustomFont
      fonts:
        - asset: assets/fonts/CustomFont-Regular.ttf
        - asset: assets/fonts/CustomFont-Bold.ttf
          weight: 700
        - asset: assets/fonts/CustomFont-Italic.ttf
          style: italic

步骤 3:使用自定义字体

dart
Text(
  'Custom Font Text',
  style: TextStyle(
    fontFamily: 'CustomFont',
    fontSize: 20,
  ),
);

自定义图标导入

步骤 1:生成图标字体

使用 FlutterIconIconMoon 等工具生成图标字体。

步骤 2:添加图标文件

将生成的字体文件和 Dart 类文件添加到项目中。

步骤 3:使用自定义图标

dart
import 'package:your_app/icons/custom_icons.dart';

Icon(
  CustomIcons.heart,
  size: 24,
  color: Colors.red,
);

11.4 实操案例:配置全局主题,自定义页面样式,实现主题切换

目标

创建一个应用,配置全局主题,自定义页面样式,并实现亮色/暗色主题切换功能。

步骤 1:创建主题管理类

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

class ThemeModel extends ChangeNotifier {
  bool _isDarkMode = false;

  bool get isDarkMode => _isDarkMode;

  // 初始化:从本地存储加载主题设置
  Future<void> init() async {
    final prefs = await SharedPreferences.getInstance();
    _isDarkMode = prefs.getBool('isDarkMode') ?? false;
    notifyListeners();
  }

  // 切换主题
  Future<void> toggleTheme() async {
    _isDarkMode = !_isDarkMode;
    
    // 保存到本地存储
    final prefs = await SharedPreferences.getInstance();
    await prefs.setBool('isDarkMode', _isDarkMode);
    
    notifyListeners();
  }

  // 获取当前主题
  ThemeData get themeData => _isDarkMode ? darkTheme : lightTheme;

  // 亮色主题
  static final lightTheme = ThemeData(
    brightness: Brightness.light,
    primaryColor: Colors.blue,
    primarySwatch: Colors.blue,
    accentColor: Colors.orange,
    backgroundColor: Colors.grey[100],
    cardColor: Colors.white,
    textTheme: TextTheme(
      headline1: TextStyle(
        fontSize: 32,
        fontWeight: FontWeight.bold,
        color: Colors.black,
      ),
      bodyText1: TextStyle(
        fontSize: 16,
        color: Colors.grey[800],
      ),
      bodyText2: TextStyle(
        fontSize: 14,
        color: Colors.grey[600],
      ),
    ),
    buttonTheme: ButtonThemeData(
      buttonColor: Colors.blue,
      textTheme: ButtonTextTheme.primary,
    ),
  );

  // 暗色主题
  static final darkTheme = ThemeData(
    brightness: Brightness.dark,
    primaryColor: Colors.blue[700],
    primarySwatch: Colors.blue,
    accentColor: Colors.orange[700],
    backgroundColor: Colors.grey[900],
    cardColor: Colors.grey[800],
    textTheme: TextTheme(
      headline1: TextStyle(
        fontSize: 32,
        fontWeight: FontWeight.bold,
        color: Colors.white,
      ),
      bodyText1: TextStyle(
        fontSize: 16,
        color: Colors.grey[300],
      ),
      bodyText2: TextStyle(
        fontSize: 14,
        color: Colors.grey[400],
      ),
    ),
    buttonTheme: ButtonThemeData(
      buttonColor: Colors.blue[700],
      textTheme: ButtonTextTheme.primary,
    ),
  );
}

步骤 2:创建主应用

dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'theme_model.dart';
import 'home_page.dart';

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => ThemeModel()..init(),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer<ThemeModel>(
      builder: (context, themeModel, child) {
        return MaterialApp(
          title: 'Theme Demo',
          theme: themeModel.themeData,
          home: HomePage(),
        );
      },
    );
  }
}

步骤 3:创建首页

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

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final themeModel = Provider.of<ThemeModel>(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('Theme Demo'),
        actions: [
          IconButton(
            icon: Icon(
              themeModel.isDarkMode ? Icons.wb_sunny : Icons.nightlight_round,
            ),
            onPressed: () {
              themeModel.toggleTheme();
            },
          ),
        ],
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Text(
              'Welcome to Flutter Theme Demo',
              style: Theme.of(context).textTheme.headline1,
              textAlign: TextAlign.center,
            ),
            SizedBox(height: 40),
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16.0),
                child: Column(
                  children: [
                    Text(
                      'Card Title',
                      style: Theme.of(context).textTheme.headline6,
                    ),
                    SizedBox(height: 10),
                    Text(
                      'This is a sample card with themed content. The colors will change based on the selected theme.',
                      style: Theme.of(context).textTheme.bodyText1,
                      textAlign: TextAlign.center,
                    ),
                  ],
                ),
              ),
            ),
            SizedBox(height: 40),
            ElevatedButton(
              onPressed: () {
                ScaffoldMessenger.of(context).showSnackBar(
                  SnackBar(
                    content: Text('Button clicked!'),
                  ),
                );
              },
              child: Text('Click Me'),
            ),
            SizedBox(height: 40),
            Text(
              'Current Theme: ${themeModel.isDarkMode ? 'Dark' : 'Light'}',
              style: Theme.of(context).textTheme.bodyText2,
            ),
          ],
        ),
      ),
    );
  }
}

步骤 4:运行应用

  1. 启动模拟器或连接真机
  2. 运行项目
  3. 点击右上角的图标切换主题
  4. 观察主题变化效果
  5. 重启应用,验证主题设置是否保存

11.5 样式与主题最佳实践

1. 全局主题统一

  • 集中管理:在 MaterialApp 中配置全局主题
  • 一致性:确保应用整体风格一致
  • 可维护性:便于后续统一修改样式

2. 主题层次结构

  • 全局主题:设置应用的基本风格
  • 局部主题:针对特定页面或组件进行样式调整
  • 组件样式:对单个组件进行样式定制

3. 响应式设计

  • 适配不同屏幕:考虑不同屏幕尺寸的样式表现
  • 动态字体:使用 MediaQuery 调整字体大小
  • 布局适配:确保样式在不同设备上都能正常显示

4. 性能优化

  • 避免重复创建:复用样式对象
  • 使用 const:对于固定样式使用 const 构造函数
  • 主题数据缓存:避免频繁获取主题数据

5. 可访问性

  • 对比度:确保文本与背景的对比度符合可访问性标准
  • 字体大小:支持系统字体大小设置
  • 色彩方案:考虑色盲用户的体验

11.6 小结

本章介绍了 Flutter 的样式与主题,包括文本样式、容器样式、主题配置、字体与图标自定义等内容。样式与主题是 Flutter 应用的重要组成部分,它们直接影响用户体验和应用的视觉效果。

我们学习了:

  • TextStyle:定义文本的样式,包括字体、颜色、粗细等
  • BoxDecoration:定义容器的样式,包括背景色、边框、圆角、阴影等
  • ThemeData:配置全局主题,统一应用的整体风格
  • Theme 组件:实现局部主题覆盖,实现样式差异
  • 自定义字体:导入和使用自定义字体
  • 自定义图标:导入和使用自定义图标
  • 主题切换:实现亮色/暗色主题切换功能

通过实操案例,我们创建了一个应用,配置了全局主题,实现了主题切换功能,并保存了主题设置。在实际开发中,你应该根据应用的需求和品牌风格,设计合适的样式和主题,提升应用的视觉效果和用户体验。

在接下来的章节中,我们将学习 Flutter 的常用组件进阶,掌握更多实用的组件和功能。

© 2026 编程马·菜鸟教程 版权所有