第十二讲 风格与主题统一

0 阅读13分钟

前言:

通过 ThemeData(全局主题)和工具类,实现颜色、字体、组件样式的统一管理,减少重复代码,便于后期维护;通过平台判断,实现 Android/iOS/Web 端的差异化适配,兼顾原生体验。

关键要点:

  • Material 风格对应 Android,Cupertino 风格对应 iOS,根据需求选择入口;
  • ThemeData 是全局主题核心,子组件通过 Theme.of(context) 获取样式;
  • 颜色、字体、样式需封装工具类,禁止硬编码;
  • 平台适配优先“自动适配+手动差异化”,避免两套完全独立的 UI。

一、总览

本讲核心目标是帮助开发者掌握 Flutter 中“风格与主题统一”的实现方法,解决多页面、多组件的视觉一致性问题,同时兼顾不同平台(Android/iOS)的原生视觉体验。

通过学习 Material 风格、Cupertino iOS 风格的差异与应用,掌握 ThemeData 全局主题的配置技巧,实现颜色、字体、组件样式的统一管理,并理解平台适配的核心逻辑与差异化处理方案,最终能独立开发出视觉统一、平台适配的 Flutter 应用。

简单来说,本章要解决的核心问题:如何让 App 所有页面“看起来像一个整体”,同时在 Android 和 iOS 上都能符合用户的使用习惯(比如 Android 用 Material 按钮,iOS 用 Cupertino 按钮)。

Flutter 风格与主题统一的底层逻辑,核心是“主题全局管理+组件风格适配”,底层结构分为 4 层,自上而下层层依赖,具体结构如下:

image.png

结构说明:

  • 顶层:应用入口决定整体风格基调(MaterialApp 对应 Android 风格,CupertinoApp 对应 iOS 风格,也可混合使用);
  • 核心层:全局主题(ThemeData/CupertinoThemeData)是风格统一的核心,存储全局共享的颜色、字体、组件样式;
  • 中间层:主题属性通过“继承+覆盖”的方式,传递给所有子组件,确保组件样式统一;
  • 底层:组件根据全局主题和平台判断,渲染对应风格的 UI,实现“统一风格+平台差异化”。

二、核心知识点

2.1 Material 风格与 Cupertino iOS 风格

Flutter 提供两种主流 UI 风格,分别对应 Android 和 iOS 原生视觉,开发者可根据需求选择单一风格或混合适配。

2.1.1 Material 风格(Android 原生风格)

基于 Google 的 Material Design 设计规范,特点是立体感、阴影、圆角、波纹效果,适合 Android 平台。

核心属性

  • MaterialApp:Material 风格入口,包含 theme(全局主题)、home(首页)、routes(路由)等核心属性;
  • Scaffold:Material 风格页面容器,包含 appBar(导航栏)、body(内容区)、floatingActionButton(悬浮按钮)等;
  • 常用组件:ElevatedButton(悬浮按钮)、TextButton(文本按钮)、Card(卡片)、ListTile(列表项),均自带 Material 风格。

核心案例:MaterialApp 入口+基础组件

import 'package:flutter/material.dart';

void main() {
  runApp(const MyMaterialApp());
}

class MyMaterialApp extends StatelessWidget {
  const MyMaterialApp({super.key});

  @override
  Widget build(BuildContext context) {
    // Material风格入口
    return MaterialApp(
      title: 'Material风格示例',
      home: Scaffold(
        // Material专属导航栏
        appBar: AppBar(title: const Text('Material App')),
        body: Center(
          // Material专属按钮(带波纹效果)
          child: ElevatedButton(
            onPressed: () {},
            child: const Text('点击按钮'),
          ),
        ),
      ),
    );
  }
}

注意事项

  • Material 组件必须包裹在 MaterialApp 或 Material 组件内部,否则会报错;
  • 波纹效果默认开启,可通过 splashColor 关闭或修改。
2.1.2 Cupertino iOS 风格

基于 Apple 的 iOS 设计规范,特点是扁平化、无阴影(或浅阴影)、圆角柔和,适合 iOS 平台,需导入 cupertino_icons 依赖。

核心属性

  • CupertinoApp:iOS 风格入口,包含 theme(CupertinoThemeData)、home、routes 等;
  • CupertinoPageScaffold:iOS 风格页面容器,包含 navigationBar(导航栏)、child(内容区);
  • 常用组件:CupertinoButton(按钮)、CupertinoTextField(输入框)、CupertinoListTile(列表项)、CupertinoAlertDialog(弹窗)。

核心案例:CupertinoApp 入口+基础组件

import 'package:flutter/cupertino.dart';

void main() {
  runApp(const MyCupertinoApp());
}

class MyCupertinoApp extends StatelessWidget {
  const MyCupertinoApp({super.key});

  @override
  Widget build(BuildContext context) {
    // iOS风格入口
    return CupertinoApp(
      title: 'Cupertino风格示例',
      home: CupertinoPageScaffold(
        // iOS专属导航栏
        navigationBar: const CupertinoNavigationBar(
          middle: Text('Cupertino App'),
        ),
        child: Center(
          // iOS专属按钮(无波纹,点击有高亮效果)
          child: CupertinoButton(
            color: CupertinoColors.activeBlue,
            onPressed: () {},
            child: const Text('点击按钮'),
          ),
        ),
      ),
    );
  }
}

注意事项

  • 需在 pubspec.yaml 中添加 cupertino_icons: ^1.0.6 依赖,否则图标无法正常显示;
  • Cupertino 组件不支持 Material 风格的波纹效果,点击反馈为高亮效果;
  • 导航栏默认无返回按钮,需手动添加。

3.2 ThemeData 全局主题(核心)

ThemeData 是 Material 风格的全局主题配置类,可统一管理 App 所有组件的颜色、字体、样式,实现“一处修改,全局生效”;Cupertino 风格对应 CupertinoThemeData,用法类似。

核心属性(ThemeData)

  • 颜色相关:primaryColor(主色调)、primarySwatch(主色调系列)、accentColor(强调色)、backgroundColor(背景色)、errorColor(错误色);
  • 字体相关:fontFamily(全局字体)、textTheme(文本样式集合,包含标题、正文、提示文字等);
  • 组件样式相关:elevatedButtonTheme(悬浮按钮样式)、textButtonTheme(文本按钮样式)、cardTheme(卡片样式)、appBarTheme(导航栏样式);
  • 其他:brightness(亮度,light/dark)、scaffoldBackgroundColor(页面背景色)。

核心案例:配置全局主题(颜色+字体+按钮样式)

import 'package:flutter/material.dart';

void main() {
  runApp(const MyThemedApp());
}

class MyThemedApp extends StatelessWidget {
  const MyThemedApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '全局主题示例',
      // 全局主题配置
      theme: ThemeData(
        // 1. 颜色主题(全局主色调、次要色调、错误色)
        primaryColor: Colors.blue, // 主色调(导航栏、按钮等)
        primarySwatch: Colors.blue, // 主色调系列(用于生成不同深浅的颜色)
        shadowColor: Colors.orange,// 阴影颜色(用于按钮、卡片等)
        // 2. 字体配置(全局字体)
        fontFamily: 'PingFang SC', // 全局字体(需导入字体资源)
        textTheme: const TextTheme(
          // 标题字体
          titleLarge: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
          // 正文字体
          bodyLarge: TextStyle(fontSize: 16, color: Colors.grey),
          // 提示文字字体
          bodySmall: TextStyle(fontSize: 14, color: Colors.grey),
        ),
        // 3. 按钮样式(全局统一按钮)
        elevatedButtonTheme: ElevatedButtonThemeData(
          style: ElevatedButton.styleFrom(
            padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
            textStyle: const TextStyle(fontSize: 16),
          ),
        ),
      ),
      home: const HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    // 从全局主题中获取样式(无需重复配置)
    final textTheme = Theme.of(context).textTheme;
    return Scaffold(
      appBar: AppBar(title: Text('全局主题演示', style: textTheme.titleLarge)),
      body: Padding(
        padding: const EdgeInsets.all(20),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text('这是正文内容,字体和颜色全局统一', style: textTheme.bodyLarge),
            const SizedBox(height: 20),
            Text('这是提示文字', style: textTheme.bodySmall),
            const SizedBox(height: 20),
            // 按钮样式全局统一
            ElevatedButton(
              onPressed: () {},
              child: const Text('全局样式按钮'),
            ),
            const SizedBox(height: 20),
            // 手动覆盖全局样式(特殊需求)
            ElevatedButton(
              style: ElevatedButton.styleFrom(foregroundColor: Colors.green),
              onPressed: () {},
              child: const Text('覆盖全局样式按钮'),
            ),
          ],
        ),
      ),
    );
  }
}

注意事项

  • 全局主题需在 MaterialApp 的 theme 属性中配置,子组件通过 Theme.of(context) 获取主题样式;
  • 可通过“局部主题”(Theme 组件)覆盖全局主题,满足特殊页面/组件的样式需求;
  • 导入自定义字体时,需在 pubspec.yaml 中配置 fonts 路径,否则 fontFamily 不生效;
  • Cupertino 风格的全局主题用 CupertinoThemeData,属性类似(如 primaryColor、textTheme 对应 textTheme)。

3.3 颜色、字体、样式统一

在全局主题的基础上,进一步规范颜色、字体、组件样式的使用,避免混乱,核心是“统一命名、统一引用、禁止硬编码”。

核心案例:规范颜色和字体(封装工具类)

import 'package:flutter/material.dart';

// 1. 统一颜色管理(封装工具类,避免硬编码)
class AppColors {
  static const primary = Color(0xFF2196F3); // 主色调
  static const secondary = Color(0xFFFF9800); // 强调色
  static const success = Color(0xFF4CAF50); // 成功色
  static const error = Color(0xFFF44336); // 错误色
  static const textPrimary = Color(0xFF333333); // 正文主色
  static const textSecondary = Color(0xFF666666); // 正文次要色
  static const background = Color(0xFFF5F5F5); // 页面背景色
}

// 2. 统一字体管理
class AppFonts {
  static const fontFamily = 'PingFang SC';
  // 标题字体
  static const titleLarge = TextStyle(
    fontFamily: fontFamily,
    fontSize: 20,
    fontWeight: FontWeight.bold,
    color: AppColors.textPrimary,
  );
  // 正文字体
  static const bodyLarge = TextStyle(
    fontFamily: fontFamily,
    fontSize: 16,
    color: AppColors.textPrimary,
  );
  // 提示文字字体
  static const bodySmall = TextStyle(
    fontFamily: fontFamily,
    fontSize: 14,
    color: AppColors.textSecondary,
  );
}

// 3. 统一组件样式(按钮、卡片等)
class AppStyles {
  // 统一按钮样式
  static final elevatedButtonStyle = ElevatedButton.styleFrom(
    backgroundColor: AppColors.primary,
    padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
    borderRadius: BorderRadius.circular(8),
    textStyle: AppFonts.bodyLarge,
  );
  // 统一卡片样式
  static final cardStyle = CardTheme(
    elevation: 2,
    borderRadius: BorderRadius.circular(12),
    margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
  );
}

// 应用入口(使用统一样式)
void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '样式统一示例',
      theme: ThemeData(
        primaryColor: AppColors.primary,
        scaffoldBackgroundColor: AppColors.background,
        fontFamily: AppFonts.fontFamily,
        textTheme: TextTheme(
          titleLarge: AppFonts.titleLarge,
          bodyLarge: AppFonts.bodyLarge,
          bodySmall: AppFonts.bodySmall,
        ),
        elevatedButtonTheme: ElevatedButtonThemeData(style: AppStyles.elevatedButtonStyle),
        cardTheme: AppStyles.cardStyle,
      ),
      home: const StyleUnificationPage(),
    );
  }
}

class StyleUnificationPage extends StatelessWidget {
  const StyleUnificationPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('样式统一演示')),
      body: ListView(
        children: [
          // 统一卡片样式
          Card(
            child: Padding(
              padding: const EdgeInsets.all(16),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text('卡片标题', style: AppFonts.titleLarge),
                  const SizedBox(height: 8),
                  Text('这是卡片正文,颜色、字体、间距都统一配置,无需重复编写。', style: AppFonts.bodyLarge),
                ],
              ),
            ),
          ),
          // 统一按钮样式
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
            child: ElevatedButton(
              onPressed: () {},
              child: const Text('统一样式按钮'),
            ),
          ),
          // 引用统一颜色
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
            child: Text('这是提示文字,颜色统一', style: AppFonts.bodySmall),
          ),
        ],
      ),
    );
  }
}

注意事项

  • 颜色、字体、样式需封装成工具类(如 AppColors、AppFonts),禁止在组件中直接写色值(如 Color(0xFF2196F3))、字体大小,便于后期统一修改;
  • 所有组件优先使用全局主题或工具类中的样式,特殊情况可局部覆盖,但需注明原因;
  • 字体需统一(如全用 PingFang SC 或 Roboto),避免同一页面出现多种字体。

3.4 平台适配与差异化

平台适配的核心是“统一风格基础上,兼顾平台原生体验”,Flutter 提供两种适配方式:自动适配(根据运行平台自动切换组件)、手动适配(根据平台判断渲染不同组件)。

核心属性与方法

  • Platform.isAndroid / Platform.isIOS:判断当前运行平台(需导入 dart:io 包);
  • 自动适配:根据平台选择 MaterialApp 或 CupertinoApp 作为入口;
  • 手动适配:通过 if-else 判断平台,渲染不同组件(如按钮、弹窗、导航栏);
  • 平台专属方法:showDialog(Android 弹窗)、showCupertinoDialog(iOS 弹窗)。

核心案例:平台差异化适配(自动+手动)

import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'dart:io'; // 用于判断平台

void main() {
  runApp(const PlatformAdaptationApp());
}

class PlatformAdaptationApp extends StatelessWidget {
  const PlatformAdaptationApp({super.key});

  @override
  Widget build(BuildContext context) {
    // 方式1:自动适配(根据平台选择入口)
    return Platform.isAndroid
        ? MaterialApp(
            title: 'Android 适配',
            theme: ThemeData(primarySwatch: Colors.blue),
            home: const PlatformAdaptationPage(),
          )
        : CupertinoApp(
            title: 'iOS 适配',
            theme: CupertinoThemeData(primaryColor: CupertinoColors.activeBlue),
            home: const PlatformAdaptationPage(),
          );
  }
}

class PlatformAdaptationPage extends StatelessWidget {
  const PlatformAdaptationPage({super.key});

  // 封装:根据平台返回不同按钮
  Widget _buildPlatformButton() {
    if (Platform.isAndroid) {
      // Android:Material 按钮
      return ElevatedButton(
        onPressed: () => _showPlatformDialog(),
        child: const Text('点击弹窗'),
      );
    } else {
      // iOS:Cupertino 按钮
      return CupertinoButton(
        color: CupertinoColors.activeBlue,
        onPressed: () => _showPlatformDialog(),
        child: const Text('点击弹窗'),
      );
    }
  }

  // 封装:根据平台返回不同弹窗
  void _showPlatformDialog() {
    if (Platform.isAndroid) {
      // Android:Material 弹窗
      showDialog(
        context: navigatorKey.currentContext!,
        builder: (context) => AlertDialog(
          title: const Text('提示'),
          content: const Text('这是Android平台弹窗'),
          actions: [
            TextButton(
              onPressed: () => Navigator.pop(context),
              child: const Text('取消'),
            ),
            TextButton(
              onPressed: () => Navigator.pop(context),
              child: const Text('确定'),
            ),
          ],
        ),
      );
    } else {
      // iOS:Cupertino 弹窗
      showCupertinoDialog(
        context: navigatorKey.currentContext!,
        builder: (context) => CupertinoAlertDialog(
          title: const Text('提示'),
          content: const Text('这是iOS平台弹窗'),
          actions: [
            CupertinoDialogAction(
              onPressed: () => Navigator.pop(context),
              child: const Text('取消'),
            ),
            CupertinoDialogAction(
              onPressed: () => Navigator.pop(context),
              child: const Text('确定'),
            ),
          ],
        ),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    // 方式2:手动适配(同一页面中,根据平台渲染不同组件)
    return Scaffold(
      // 导航栏适配
      appBar: Platform.isAndroid
          ? AppBar(title: const Text('平台适配演示'))
          : CupertinoNavigationBar(middle: const Text('平台适配演示')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // 平台差异化按钮
            _buildPlatformButton(),
            const SizedBox(height: 20),
            // 平台差异化文本(字体、颜色)
            Text(
              Platform.isAndroid ? '当前是Android平台' : '当前是iOS平台',
              style: Platform.isAndroid
                  ? Theme.of(context).textTheme.titleLarge
                  : CupertinoTheme.of(context).textTheme.navTitleTextStyle,
            ),
          ],
        ),
      ),
    );
  }
}

// 全局导航键(用于弹窗获取上下文)
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

注意事项

  • 平台适配不是“两套完全独立的 UI”,而是“统一核心样式,差异化细节”,避免用户体验割裂;
  • 使用 Platform 类需注意:Web 端不支持 dart:io 包,若需适配 Web,需用 kIsWeb(from flutter/foundation.dart)判断;
  • 可使用第三方插件(如 flutter_platform_widgets)简化平台适配代码,无需手动写 if-else。

四、综合应用案例

需求:开发一个“个人中心”页面,要求:

① 风格统一(颜色、字体、组件样式)

② 平台适配(Android 用 Material 风格,iOS 用 Cupertino 风格)

③ 全局主题控制

④ 包含导航栏、头像、列表、按钮等组件。

完整代码

import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'dart:io';
import 'package:flutter/foundation.dart' show kIsWeb;

// 1. 统一颜色管理
class AppColors {
  static const primary = Color(0xFF2196F3);
  static const secondary = Color(0xFFFF9800);
  static const textPrimary = Color(0xFF333333);
  static const textSecondary = Color(0xFF666666);
  static const background = Color(0xFFF5F5F5);
  static const cardBackground = Color(0xFFFFFFFF);
}

// 2. 统一字体管理
class AppFonts {
  static const fontFamily = kIsWeb ? 'Arial' : 'PingFang SC'; // Web端适配字体
  static const titleLarge = TextStyle(
    fontFamily: fontFamily,
    fontSize: 20,
    fontWeight: FontWeight.bold,
    color: AppColors.textPrimary,
  );
  static const bodyLarge = TextStyle(
    fontFamily: fontFamily,
    fontSize: 16,
    color: AppColors.textPrimary,
  );
  static const bodySmall = TextStyle(
    fontFamily: fontFamily,
    fontSize: 14,
    color: AppColors.textSecondary,
  );
}

// 3. 统一组件样式
class AppStyles {
  // 按钮样式
  static final elevatedButtonStyle = ElevatedButton.styleFrom(
    backgroundColor: AppColors.primary,
    padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
    borderRadius: BorderRadius.circular(8),
    textStyle: AppFonts.bodyLarge,
  );
  static final cupertinoButtonStyle = CupertinoButtonData(
    color: AppColors.primary,
  );
  // 卡片样式
  static final cardStyle = CardTheme(
    elevation: 2,
    borderRadius: BorderRadius.circular(12),
    margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
    color: AppColors.cardBackground,
  );
  // 列表项样式
  static final listTileStyle = ListTileThemeData(
    textColor: AppColors.textPrimary,
    iconColor: AppColors.primary,
  );
}

// 4. 全局主题配置
ThemeData get androidTheme => ThemeData(
      primaryColor: AppColors.primary,
      scaffoldBackgroundColor: AppColors.background,
      fontFamily: AppFonts.fontFamily,
      textTheme: TextTheme(
        titleLarge: AppFonts.titleLarge,
        bodyLarge: AppFonts.bodyLarge,
        bodySmall: AppFonts.bodySmall,
      ),
      elevatedButtonTheme: ElevatedButtonThemeData(style: AppStyles.elevatedButtonStyle),
      cardTheme: AppStyles.cardStyle,
      listTileTheme: AppStyles.listTileStyle,
    );

CupertinoThemeData get iosTheme => CupertinoThemeData(
      primaryColor: AppColors.primary,
      scaffoldBackgroundColor: AppColors.background,
      textTheme: CupertinoTextThemeData(
        navTitleTextStyle: AppFonts.titleLarge,
        bodyTextStyle: AppFonts.bodyLarge,
        captionTextStyle: AppFonts.bodySmall,
      ),
    );

// 5. 主入口(平台自动适配)
void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    if (kIsWeb) {
      // Web端默认使用Material风格
      return MaterialApp(
        title: '个人中心(Web)',
        theme: androidTheme,
        home: const ProfilePage(),
        navigatorKey: navigatorKey,
      );
    } else if (Platform.isAndroid) {
      // Android端使用Material风格
      return MaterialApp(
        title: '个人中心(Android)',
        theme: androidTheme,
        home: const ProfilePage(),
        navigatorKey: navigatorKey,
      );
    } else {
      // iOS端使用Cupertino风格
      return CupertinoApp(
        title: '个人中心(iOS)',
        theme: iosTheme,
        home: const ProfilePage(),
        navigatorKey: navigatorKey,
      );
    }
  }
}

// 全局导航键
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

// 6. 个人中心页面(核心页面,整合所有技术)
class ProfilePage extends StatelessWidget {
  const ProfilePage({super.key});

  // 封装:平台适配按钮
  Widget _buildLogoutButton() {
    if (kIsWeb || Platform.isAndroid) {
      return ElevatedButton(
        style: AppStyles.elevatedButtonStyle,
        onPressed: () => _showLogoutDialog(),
        child: const Text('退出登录'),
      );
    } else {
      return CupertinoButton(
        color: AppColors.primary,
        onPressed: () => _showLogoutDialog(),
        child: const Text('退出登录'),
      );
    }
  }

  // 封装:平台适配弹窗
  void _showLogoutDialog() {
    if (kIsWeb || Platform.isAndroid) {
      showDialog(
        context: navigatorKey.currentContext!,
        builder: (context) => AlertDialog(
          title: const Text('确认退出'),
          content: const Text('确定要退出当前账号吗?'),
          actions: [
            TextButton(
              onPressed: () => Navigator.pop(context),
              child: const Text('取消'),
            ),
            TextButton(
              onPressed: () => Navigator.pop(context),
              child: const Text('确定'),
            ),
          ],
        ),
      );
    } else {
      showCupertinoDialog(
        context: navigatorKey.currentContext!,
        builder: (context) => CupertinoAlertDialog(
          title: const Text('确认退出'),
          content: const Text('确定要退出当前账号吗?'),
          actions: [
            CupertinoDialogAction(
              onPressed: () => Navigator.pop(context),
              child: const Text('取消'),
            ),
            CupertinoDialogAction(
              onPressed: () => Navigator.pop(context),
              child: const Text('确定'),
            ),
          ],
        ),
      );
    }
  }

  // 封装:平台适配导航栏
  Widget _buildAppBar() {
    if (kIsWeb || Platform.isAndroid) {
      return AppBar(
        title: const Text('个人中心'),
        centerTitle: true,
        backgroundColor: AppColors.primary,
      );
    } else {
      return CupertinoNavigationBar(
        middle: const Text('个人中心'),
        backgroundColor: AppColors.primary,
        textStyle: const TextStyle(color: Colors.white),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: _buildAppBar(),
      body: ListView(
        children: [
          // 头像区域
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 24),
            child: Center(
              child: Column(
                children: [
                  // 头像
                  Container(
                    width: 100,
                    height: 100,
                    decoration: BoxDecoration(
                      color: AppColors.secondary,
                      borderRadius: BorderRadius.circular(50),
                      image: const DecorationImage(
                        image: NetworkImage('https://via.placeholder.com/100'),
                        fit: BoxFit.cover,
                      ),
                    ),
                  ),
                  const SizedBox(height: 12),
                  // 用户名(全局字体)
                  Text('Flutter 开发者', style: AppFonts.titleLarge),
                  const SizedBox(height: 4),
                  // 简介(全局字体)
                  Text('专注 Flutter 学习与开发', style: AppFonts.bodySmall),
                ],
              ),
            ),
          ),

          // 功能列表(全局列表样式)
          Card(
            child: Column(
              children: [
                ListTile(
                  leading: const Icon(Icons.person),
                  title: const Text('个人资料'),
                  trailing: const Icon(Icons.arrow_forward_ios, size: 16),
                  onTap: () {},
                ),
                const Divider(height: 1),
                ListTile(
                  leading: const Icon(Icons.settings),
                  title: const Text('设置'),
                  trailing: const Icon(Icons.arrow_forward_ios, size: 16),
                  onTap: () {},
                ),
                const Divider(height: 1),
                ListTile(
                  leading: const Icon(Icons.help),
                  title: const Text('帮助与反馈'),
                  trailing: const Icon(Icons.arrow_forward_ios, size: 16),
                  onTap: () {},
                ),
              ],
            ),
          ),

          // 退出登录按钮(平台适配)
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
            child: _buildLogoutButton(),
          ),
        ],
      ),
    );
  }
}

案例说明

  • 整合点:统一颜色/字体/组件样式(工具类封装)、全局主题配置(androidTheme/iosTheme)、平台适配(自动选择入口、手动适配组件);
  • 效果:在 Android 上显示 Material 风格(导航栏、按钮、弹窗),在 iOS 上显示 Cupertino 风格,Web 端默认使用 Material 风格,且所有页面颜色、字体、样式统一;
  • 可扩展性:如需修改主色调,只需修改 AppColors.primary;如需修改按钮样式,只需修改 AppStyles.elevatedButtonStyle,全局生效。