基础入门 Flutter for OpenHarmony:Switch 开关组件详解

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
🎯 欢迎来到 Flutter for OpenHarmony 社区!本文将深入讲解 Flutter 中 Switch 开关组件的使用方法,带你从基础到精通,掌握这一常见的开关控件。


一、Switch 组件概述

在 Flutter for OpenHarmony 应用开发中,Switch(开关)是一种用于二元状态切换的常见控件。用户可以通过点击或滑动来切换开关的状态,适用于表示"开/关"、"是/否"、"启用/禁用"等二元选择的场景。

📋 Switch 组件特点

特点 说明
二元状态 只有两个状态:开启(true)和关闭(false)
Material Design 遵循 Material Design 规范的开关样式
动画效果 开关切换时有平滑的动画过渡
触摸友好 提供较大的触摸区域,易于操作
可定制 支持自定义颜色、大小、圆角等样式

💡 使用场景:开关组件常用于设置页面中的功能启用/禁用,如推送通知开关、飞行模式开关、深色模式开关等。


二、Switch 基础用法

学习任何组件,我们都应该从最简单的用法开始。Switch 组件的核心功能非常简单:控制一个二元状态的切换。让我们一步步来理解它的基本使用方式。

2.1 最简单的 Switch

最基础的 Switch 只需要两个参数:value(当前状态)和 onChanged(状态改变时的回调)。

dart 复制代码
Switch(
  value: _isSwitched,
  onChanged: (bool value) {
    setState(() {
      _isSwitched = value;
    });
  },
)

代码解析:

  • value:这是一个布尔值,决定了开关的当前状态。true 表示开关处于开启状态,false 表示关闭状态。
  • onChanged:这是一个回调函数,当用户点击或滑动开关时会触发。回调函数会接收一个布尔参数,代表开关的新状态。
  • setState:这是 Flutter 中更新 UI 的关键方法。当我们修改状态后,必须调用 setState 来告诉 Flutter 重新构建界面。

⚠️ 注意 :如果忘记调用 setState,UI 将不会更新,用户会看到开关"卡住"的状态。

2.2 完整示例

下面是一个完整的可运行示例,展示了如何在实际应用中使用 Switch 组件:

2.2 完整示例

dart 复制代码
class SwitchExample extends StatefulWidget {
  const SwitchExample({super.key});

  @override
  State<SwitchExample> createState() => _SwitchExampleState();
}

class _SwitchExampleState extends State<SwitchExample> {
  bool _isSwitched = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Switch 示例')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Switch(
              value: _isSwitched,
              onChanged: (bool value) {
                setState(() {
                  _isSwitched = value;
                });
              },
            ),
            const SizedBox(height: 16),
            Text(
              '当前状态: ${_isSwitched ? "开启" : "关闭"}',
              style: const TextStyle(fontSize: 18),
            ),
          ],
        ),
      ),
    );
  }
}

三、Switch 常用属性

掌握了基础用法后,我们来深入了解 Switch 的各种属性。这些属性可以帮助我们自定义开关的外观和行为,使其更好地融入我们的应用设计。

3.1 value - 当前状态

控制开关的当前状态,true 表示开启,false 表示关闭。这是 Switch 组件的必填参数。

dart 复制代码
Switch(
  value: true,  // 开启状态
  onChanged: (value) {},
)

使用技巧:

  • 通常我们会将 value 绑定到一个状态变量上,这样开关的状态就可以动态更新。
  • 在实际开发中,value 的值可能来自应用的全局状态管理,或者是从后端 API 获取的配置。

3.2 onChanged - 状态改变回调

当用户点击开关时触发,接收新状态作为参数。设为 null 时开关禁用。

dart 复制代码
Switch(
  value: _isSwitched,
  onChanged: (bool value) {
    print('开关状态变为: $value');
    setState(() {
      _isSwitched = value;
    });
  },
)

深入理解:

  • onChanged 不仅仅是更新 UI 的地方,更是处理业务逻辑的关键节点。
  • 在这个回调中,你可以:
    • 更新本地状态(如上面的示例)
    • 调用 API 保存用户设置
    • 触发其他相关的业务逻辑
    • 发送事件通知其他组件

3.3 activeColor - 激活状态颜色

设置开关处于开启状态时的颜色。这个属性帮助你将开关的视觉风格与应用的主题色保持一致。

dart 复制代码
Switch(
  value: _isSwitched,
  activeColor: Colors.green,
  onChanged: (value) {},
)

设计建议:

  • 使用应用的主色调作为激活颜色,可以增强品牌识别度。
  • 不同的开关可以使用不同的颜色来区分其功能(例如:绿色表示"启用",红色表示"危险操作")。

3.4 activeTrackColor - 激活状态轨道颜色

设置开关开启时轨道的颜色。轨道是指开关滑块下方的背景条。

dart 复制代码
Switch(
  value: _isSwitched,
  activeTrackColor: Colors.green.withOpacity(0.5),
  onChanged: (value) {},
)

视觉效果技巧:

  • 通常将轨道颜色设置为激活颜色的半透明版本(withOpacity(0.5)),这样可以创造出层次感。
  • 轨道颜色和滑块颜色的搭配会影响开关的整体质感,建议多尝试不同的组合。

3.5 inactiveThumbColor - 关闭状态滑块颜色

设置开关关闭时滑块的颜色。

dart 复制代码
Switch(
  value: _isSwitched,
  inactiveThumbColor: Colors.grey,
  onChanged: (value) {},
)

最佳实践:

  • 关闭状态的颜色通常使用中性色(如灰色),以减少视觉干扰。
  • 保持关闭状态颜色的一致性,让用户能够快速识别哪些开关是关闭的。

3.6 inactiveTrackColor - 关闭状态轨道颜色

设置开关关闭时轨道的颜色。

3.2 onChanged - 状态改变回调

当用户点击开关时触发,接收新状态作为参数。设为 null 时开关禁用。

dart 复制代码
Switch(
  value: _isSwitched,
  onChanged: (bool value) {
    print('开关状态变为: $value');
    setState(() {
      _isSwitched = value;
    });
  },
)

3.3 activeColor - 激活状态颜色

设置开关处于开启状态时的颜色。

dart 复制代码
Switch(
  value: _isSwitched,
  activeColor: Colors.green,
  onChanged: (value) {},
)

3.4 activeTrackColor - 激活状态轨道颜色

设置开关开启时轨道的颜色。

dart 复制代码
Switch(
  value: _isSwitched,
  activeTrackColor: Colors.green.withOpacity(0.5),
  onChanged: (value) {},
)

3.5 inactiveThumbColor - 关闭状态滑块颜色

设置开关关闭时滑块的颜色。

dart 复制代码
Switch(
  value: _isSwitched,
  inactiveThumbColor: Colors.grey,
  onChanged: (value) {},
)

3.6 inactiveTrackColor - 关闭状态轨道颜色

设置开关关闭时轨道的颜色。

dart 复制代码
Switch(
  value: _isSwitched,
  inactiveTrackColor: Colors.grey.withOpacity(0.3),
  onChanged: (value) {},
)

3.7 materialTapTargetSize - 触摸目标大小

控制触摸区域的大小,影响可点击区域的范围。

dart 复制代码
Switch(
  value: _isSwitched,
  materialTapTargetSize: MaterialTapTargetSize.padded,
  onChanged: (value) {},
)
说明
padded 扩展触摸区域以适应 Material Design 规范(默认)
shrinkWrap 收缩触摸区域到组件实际大小

📊 Switch 属性速查表

属性 类型 默认值 说明
value bool - 当前开关状态(必填)
onChanged ValueChanged <bool>? - 状态改变回调
activeColor Color? - 激活状态滑块颜色
activeTrackColor Color? - 激活状态轨道颜色
inactiveThumbColor Color? - 关闭状态滑块颜色
inactiveTrackColor Color? - 关闭状态轨道颜色
materialTapTargetSize MaterialTapTargetSize? - 触摸目标大小

四、Switch 禁用状态

在某些情况下,你可能需要暂时禁用某个开关选项。例如,当某个功能依赖其他功能时,或者当用户权限不足时,就需要将开关设置为禁用状态。

onChanged 设为 null 即可禁用开关,禁用后用户无法点击切换。

dart 复制代码
Column(
  children: [
    Switch(
      value: true,
      onChanged: (value) {},  // 可用状态
    ),
    const SizedBox(height: 16),
    Switch(
      value: false,
      onChanged: null,  // 禁用状态
    ),
  ],
)

禁用状态的适用场景:

  1. 功能依赖:当某个功能需要先启用另一个功能时
  2. 权限限制:当用户没有足够的权限操作某个设置时
  3. 临时维护:当某个功能暂时不可用时
  4. 条件限制:当某些条件不满足时(例如网络断开时禁用在线同步开关)

💡 小贴士:禁用状态的开关通常显示为灰色,视觉上明确告知用户该选项当前不可用。建议在禁用开关时,通过文字说明或工具提示(Tooltip)告知用户禁用的原因,提升用户体验。


五、Switch 配合 ListTile 使用

在实际的应用开发中,单独使用 Switch 的情况并不多见。更常见的做法是将 Switch 与 ListTile 组件配合使用,形成标准的设置选项布局。这种组合可以提供更好的视觉层次和信息展示效果。

为什么要配合 ListTile 使用?

  1. 信息完整:ListTile 提供了标题、副标题、图标等多个信息展示区域,让用户清楚地了解每个开关的作用。
  2. 交互友好:整个 ListTile 都可以作为点击区域,增加了可操作的范围,提升用户体验。
  3. 视觉统一:ListTile 是 Material Design 规范中推荐的列表项组件,使用它可以保证界面风格的一致性。
  4. 易于维护:通过封装,可以轻松复用设置项的结构。

基础用法示例

dart 复制代码
ListTile(
  title: const Text('推送通知'),
  subtitle: const Text('接收应用推送消息'),
  leading: const Icon(Icons.notifications),
  trailing: Switch(
    value: _notificationsEnabled,
    onChanged: (bool value) {
      setState(() {
        _notificationsEnabled = value;
      });
    },
  ),
  onTap: () {
    setState(() {
      _notificationsEnabled = !_notificationsEnabled;
    });
  },
)

代码解析:

  • title:设置项的主标题,清晰说明这是什么功能
  • subtitle:可选的副标题,提供额外的说明信息
  • leading:左侧的图标,增强视觉识别度
  • trailing:右侧的 Switch 组件
  • onTap:点击整个 ListTile 时的回调,这里我们实现点击也能切换开关状态

💡 体验优化:让整个 ListTile 都可以点击切换开关,是一种很好的用户体验设计。用户不需要精确点击小小的开关,点击任何位置都可以完成操作。

5.1 完整设置页面示例

dart 复制代码
class SettingsPage extends StatefulWidget {
  const SettingsPage({super.key});

  @override
  State<SettingsPage> createState() => _SettingsPageState();
}

class _SettingsPageState extends State<SettingsPage> {
  bool _notificationsEnabled = true;
  bool _darkModeEnabled = false;
  bool _autoUpdateEnabled = true;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('设置')),
      body: ListView(
        children: [
          _buildSwitchTile(
            title: '推送通知',
            subtitle: '接收应用推送消息',
            icon: Icons.notifications,
            value: _notificationsEnabled,
            onChanged: (value) {
              setState(() {
                _notificationsEnabled = value;
              });
            },
          ),
          _buildSwitchTile(
            title: '深色模式',
            subtitle: '使用深色主题',
            icon: Icons.dark_mode,
            value: _darkModeEnabled,
            onChanged: (value) {
              setState(() {
                _darkModeEnabled = value;
              });
            },
          ),
          _buildSwitchTile(
            title: '自动更新',
            subtitle: '自动更新应用',
            icon: Icons.system_update,
            value: _autoUpdateEnabled,
            onChanged: (value) {
              setState(() {
                _autoUpdateEnabled = value;
              });
            },
          ),
        ],
      ),
    );
  }

  Widget _buildSwitchTile({
    required String title,
    required String subtitle,
    required IconData icon,
    required bool value,
    required ValueChanged<bool> onChanged,
  }) {
    return ListTile(
      title: Text(title),
      subtitle: Text(subtitle),
      leading: Icon(icon),
      trailing: Switch(
        value: value,
        onChanged: onChanged,
      ),
      onTap: () => onChanged(!value),
    );
  }
}

六、自定义 Switch 样式

虽然 Switch 的默认样式已经符合 Material Design 规范,但在实际开发中,我们经常需要根据应用的品牌风格来定制开关的外观。Flutter 提供了丰富的属性让我们能够轻松实现各种个性化设计。

6.1 主题色 Switch

将开关的颜色与应用的主题色保持一致,是提升应用整体视觉统一性的重要手段。

dart 复制代码
Switch(
  value: _isSwitched,
  activeColor: Colors.blue,
  activeTrackColor: Colors.blue.withOpacity(0.5),
  inactiveThumbColor: Colors.grey,
  inactiveTrackColor: Colors.grey.withOpacity(0.3),
  onChanged: (value) {},
)

颜色搭配原则:

  1. 激活状态:使用鲜明的主题色,让开启状态一目了然
  2. 轨道颜色:使用激活色的半透明版本,营造层次感
  3. 关闭状态:使用中性灰色,避免引起视觉注意
  4. 保持一致:所有开关的颜色方案应该保持一致

6.2 自定义颜色组合

不同的应用场景可能需要不同的颜色方案。例如,表示"启用"的功能可以用绿色,表示"危险操作"的功能可以用红色。

dart 复制代码
Switch(
  value: _isSwitched,
  activeColor: const Color(0xFF10B981),  // 绿色
  activeTrackColor: const Color(0xFFD1FAE5),
  inactiveThumbColor: const Color(0xFF9CA3AF),
  inactiveTrackColor: const Color(0xFFF3F4F6),
  onChanged: (value) {},
)

色彩心理学应用:

  • 🟢 绿色:表示安全、成功、启用
  • 🔴 红色:表示危险、警告、删除
  • 🔵 蓝色:表示信息、中性、标准
  • 🟡 黄色:表示注意、警告
  • 🟣 紫色:表示高级、特殊功能

6.2 自定义颜色组合

dart 复制代码
Switch(
  value: _isSwitched,
  activeColor: const Color(0xFF10B981),  // 绿色
  activeTrackColor: const Color(0xFFD1FAE5),
  inactiveThumbColor: const Color(0xFF9CA3AF),
  inactiveTrackColor: const Color(0xFFF3F4F6),
  onChanged: (value) {},
)

6.3 不同尺寸的 Switch

虽然 Switch 组件本身没有提供直接的尺寸属性,但我们可以通过 Transform.scale 来调整开关的大小。这在需要突出重要功能或节省空间时非常有用。

dart 复制代码
Row(
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: [
    Transform.scale(
      scale: 0.8,
      child: Switch(
        value: _isSwitched,
        onChanged: (value) {},
      ),
    ),
    Switch(
      value: _isSwitched,
      onChanged: (value) {},
    ),
    Transform.scale(
      scale: 1.2,
      child: Switch(
        value: _isSwitched,
        onChanged: (value) {},
      ),
    ),
  ],
)

尺寸选择建议:

场景 推荐尺寸 说明
密集列表 scale: 0.8 节省空间,适合信息密集的界面
标准设置 scale: 1.0 遵循 Material Design 规范
重要开关 scale: 1.2 突出显示,增强视觉重要性
无障碍优化 scale: 1.3+ 为视力障碍用户提供更大的操作目标

⚠️ 注意:虽然可以随意调整尺寸,但建议不要使用过大的缩放比例,以免破坏界面的整体协调性。


七、Switch 与 Checkbox 的对比

在 Flutter 中,Switch 和 Checkbox 都是用于表示选择的组件,初学者经常会混淆它们的用途。理解它们的区别,选择合适的组件,对于创建符合用户预期的界面非常重要。

📊 Switch vs Checkbox

特性 Switch Checkbox
状态含义 开启/关闭 选中/未选中
适用场景 设置选项、功能开关 多选列表、协议确认
视觉形式 滑动开关 勾选框
空间占用 横向占用较小 纵向占用较小
Material 3 使用自适应开关 使用自定义形状
用户心理 状态切换 选项选择
交互方式 点击或滑动 点击

如何选择?

使用 Switch 的场景:

  1. ✅ 功能开关:如"推送通知"、"深色模式"
  2. ✅ 状态切换:如"飞行模式"、"蓝牙"
  3. ✅ 设置选项:如"自动更新"、"省电模式"
  4. ✅ 独立控制:每个开关独立控制一个功能

使用 Checkbox 的场景:

  1. ✅ 多选列表:如选择多个联系人、选择多个文件
  2. ✅ 协议确认:如"同意服务条款"、"同意隐私政策"
  3. ✅ 选项选择:如选择兴趣爱好、选择技能标签
  4. ✅ 组合选择:多个选项的组合选择

💡 核心区别:Switch 表示"状态"的切换,Checkbox 表示"选项"的选择。如果是一个功能是开启还是关闭,用 Switch;如果是多个选项中哪些被选中,用 Checkbox。


八、实际应用场景

8.1 通知设置

dart 复制代码
Card(
  margin: const EdgeInsets.all(16),
  child: Column(
    children: [
      _buildSwitchSetting(
        title: '推送通知',
        subtitle: '接收推送消息',
        icon: Icons.notifications,
        value: _pushEnabled,
        onChanged: (value) => setState(() => _pushEnabled = value),
      ),
      _buildSwitchSetting(
        title: '声音提醒',
        subtitle: '新消息时播放声音',
        icon: Icons.volume_up,
        value: _soundEnabled,
        onChanged: (value) => setState(() => _soundEnabled = value),
      ),
      _buildSwitchSetting(
        title: '震动反馈',
        subtitle: '触感震动',
        icon: Icons.vibration,
        value: _vibrationEnabled,
        onChanged: (value) => setState(() => _vibrationEnabled = value),
      ),
    ],
  ),
)

Widget _buildSwitchSetting({
  required String title,
  required String subtitle,
  required IconData icon,
  required bool value,
  required ValueChanged<bool> onChanged,
}) {
  return ListTile(
    title: Text(title),
    subtitle: Text(subtitle),
    leading: Icon(icon),
    trailing: Switch(
      value: value,
      onChanged: onChanged,
    ),
    onTap: () => onChanged(!value),
  );
}

8.2 隐私设置

dart 复制代码
Card(
  margin: const EdgeInsets.all(16),
  child: Column(
    children: [
      _buildSwitchSetting(
        title: '位置信息',
        subtitle: '允许应用访问位置',
        icon: Icons.location_on,
        value: _locationEnabled,
        onChanged: (value) => setState(() => _locationEnabled = value),
      ),
      _buildSwitchSetting(
        title: '数据分析',
        subtitle: '帮助改进应用体验',
        icon: Icons.analytics,
        value: _analyticsEnabled,
        onChanged: (value) => setState(() => _analyticsEnabled = value),
      ),
      _buildSwitchSetting(
        title: '个性化推荐',
        subtitle: '根据喜好推荐内容',
        icon: Icons.recommend,
        value: _personalizationEnabled,
        onChanged: (value) => setState(() => _personalizationEnabled = value),
      ),
    ],
  ),
)

8.3 功能模块开关

dart 复制代码
Card(
  margin: const EdgeInsets.all(16),
  child: Column(
    children: [
      _buildSwitchSetting(
        title: '深色模式',
        subtitle: '使用深色主题',
        icon: Icons.dark_mode,
        value: _darkMode,
        onChanged: (value) {
          setState(() => _darkMode = value);
          // 切换主题逻辑
        },
      ),
      _buildSwitchSetting(
        title: '省电模式',
        subtitle: '降低功耗',
        icon: Icons.battery_saver,
        value: _powerSaving,
        onChanged: (value) => setState(() => _powerSaving = value),
      ),
      _buildSwitchSetting(
        title: '自动同步',
        subtitle: '自动同步数据',
        icon: Icons.sync,
        value: _autoSync,
        onChanged: (value) => setState(() => _autoSync = value),
      ),
    ],
  ),
)

九、完整示例代码

下面是一个完整的 Flutter 应用示例,展示 Switch 组件的各种用法。

dart 复制代码
import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Switch 组件演示',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.light(
          primary: const Color(0xFF6366F1),
          secondary: const Color(0xFF8B5CF6),
          surface: const Color(0xFFE8EAF6),
          background: const Color(0xFFF8F9FF),
          brightness: Brightness.light,
        ),
        useMaterial3: true,
      ),
      home: const SwitchPage(),
    );
  }
}

class SwitchPage extends StatefulWidget {
  const SwitchPage({super.key});

  @override
  State<SwitchPage> createState() => _SwitchPageState();
}

class _SwitchPageState extends State<SwitchPage> {
  bool _isSwitched = false;
  bool _notificationsEnabled = true;
  bool _darkModeEnabled = false;
  bool _locationEnabled = true;
  bool _analyticsEnabled = false;
  bool _greenSwitch = false;
  bool _purpleSwitch = false;
  bool _orangeSwitch = false;
  bool _smallSwitch = false;
  bool _normalSwitch = false;
  bool _largeSwitch = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        decoration: const BoxDecoration(
          gradient: LinearGradient(
            begin: Alignment.topCenter,
            end: Alignment.bottomCenter,
            colors: [
              Color(0xFFE8F4FF),
              Color(0xFFF8F9FF),
              Color(0xFFE8F4FF),
            ],
          ),
        ),
        child: SafeArea(
          child: SingleChildScrollView(
            padding: const EdgeInsets.all(20),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
              // 标题区域
              Container(
                padding: const EdgeInsets.all(24),
                decoration: BoxDecoration(
                  gradient: const LinearGradient(
                    begin: Alignment.topLeft,
                    end: Alignment.bottomRight,
                    colors: [
                      Color(0xFF6366F1),
                      Color(0xFF8B5CF6),
                      Color(0xFFEC4899),
                    ],
                  ),
                  borderRadius: BorderRadius.circular(24),
                  boxShadow: [
                    BoxShadow(
                      color: const Color(0xFF6366F1).withOpacity(0.3),
                      blurRadius: 20,
                      offset: const Offset(0, 8),
                    ),
                  ],
                ),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Row(
                      children: [
                        Container(
                          padding: const EdgeInsets.all(12),
                          decoration: BoxDecoration(
                            color: Colors.white.withOpacity(0.2),
                            borderRadius: BorderRadius.circular(12),
                          ),
                          child: const Icon(
                            Icons.toggle_on,
                            color: Colors.white,
                            size: 28,
                          ),
                        ),
                        const SizedBox(width: 16),
                        const Text(
                          'Switch',
                          style: TextStyle(
                            fontSize: 32,
                            fontWeight: FontWeight.bold,
                            color: Colors.white,
                            letterSpacing: 0.5,
                          ),
                        ),
                      ],
                    ),
                    const SizedBox(height: 12),
                    Text(
                      '探索 Flutter 中开关组件的各种用法',
                      style: TextStyle(
                        fontSize: 16,
                        color: Colors.white.withOpacity(0.9),
                        height: 1.5,
                      ),
                    ),
                  ],
                ),
              ),

              const SizedBox(height: 32),

              // 基础开关
              _buildSection(
                title: '基础开关',
                icon: Icons.toggle_on,
                color: Colors.blue,
                child: _buildCard([
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: [
                      const Text(
                        '基础开关',
                        style: TextStyle(
                          fontSize: 16,
                          fontWeight: FontWeight.w500,
                          color: Color(0xFF1E293B),
                        ),
                      ),
                      Switch(
                        value: _isSwitched,
                        onChanged: (value) {
                          setState(() {
                            _isSwitched = value;
                          });
                        },
                      ),
                    ],
                  ),
                  const SizedBox(height: 16),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: [
                      const Text(
                        '禁用状态',
                        style: TextStyle(
                          fontSize: 16,
                          fontWeight: FontWeight.w500,
                          color: Color(0xFF94A3B8),
                        ),
                      ),
                      Switch(
                        value: false,
                        onChanged: null,
                      ),
                    ],
                  ),
                ]),
              ),

              const SizedBox(height: 24),

              // 自定义颜色
              _buildSection(
                title: '自定义颜色',
                icon: Icons.palette,
                color: Colors.green,
                child: _buildCard([
                  _buildCustomSwitch('绿色主题', Colors.green, _greenSwitch),
                  const SizedBox(height: 12),
                  _buildCustomSwitch('紫色主题', Colors.purple, _purpleSwitch),
                  const SizedBox(height: 12),
                  _buildCustomSwitch('橙色主题', Colors.orange, _orangeSwitch),
                ]),
              ),

              const SizedBox(height: 24),

              // 不同尺寸
              _buildSection(
                title: '不同尺寸',
                icon: Icons.straighten,
                color: Colors.pink,
                child: _buildCard([
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                    children: [
                      Column(
                        children: [
                          Transform.scale(
                            scale: 0.8,
                            child: Switch(
                              value: _smallSwitch,
                              onChanged: (value) {
                                setState(() {
                                  _smallSwitch = value;
                                });
                              },
                            ),
                          ),
                          const SizedBox(height: 8),
                          const Text(
                            '小',
                            style: TextStyle(
                              fontSize: 12,
                              color: Color(0xFF64748B),
                            ),
                          ),
                        ],
                      ),
                      Column(
                        children: [
                          Switch(
                            value: _normalSwitch,
                            onChanged: (value) {
                              setState(() {
                                _normalSwitch = value;
                              });
                            },
                          ),
                          const SizedBox(height: 8),
                          const Text(
                            '中',
                            style: TextStyle(
                              fontSize: 12,
                              color: Color(0xFF64748B),
                            ),
                          ),
                        ],
                      ),
                      Column(
                        children: [
                          Transform.scale(
                            scale: 1.2,
                            child: Switch(
                              value: _largeSwitch,
                              onChanged: (value) {
                                setState(() {
                                  _largeSwitch = value;
                                });
                              },
                            ),
                          ),
                          const SizedBox(height: 8),
                          const Text(
                            '大',
                            style: TextStyle(
                              fontSize: 12,
                              color: Color(0xFF64748B),
                            ),
                          ),
                        ],
                      ),
                    ],
                  ),
                ]),
              ),

              const SizedBox(height: 24),

              // 通知设置
              _buildSection(
                title: '通知设置',
                icon: Icons.notifications,
                color: Colors.cyan,
                child: _buildCard([
                  _buildSwitchTile(
                    title: '推送通知',
                    subtitle: '接收应用推送消息',
                    icon: Icons.notifications,
                    value: _notificationsEnabled,
                    onChanged: (value) {
                      setState(() {
                        _notificationsEnabled = value;
                      });
                    },
                  ),
                  const SizedBox(height: 16),
                  _buildSwitchTile(
                    title: '深色模式',
                    subtitle: '使用深色主题',
                    icon: Icons.dark_mode,
                    value: _darkModeEnabled,
                    onChanged: (value) {
                      setState(() {
                        _darkModeEnabled = value;
                      });
                    },
                  ),
                ]),
              ),

              const SizedBox(height: 24),

              // 隐私设置
              _buildSection(
                title: '隐私设置',
                icon: Icons.privacy_tip,
                color: Colors.amber,
                child: _buildCard([
                  _buildSwitchTile(
                    title: '位置信息',
                    subtitle: '允许应用访问位置',
                    icon: Icons.location_on,
                    value: _locationEnabled,
                    onChanged: (value) {
                      setState(() {
                        _locationEnabled = value;
                      });
                    },
                  ),
                  const SizedBox(height: 16),
                  _buildSwitchTile(
                    title: '数据分析',
                    subtitle: '帮助改进应用体验',
                    icon: Icons.analytics,
                    value: _analyticsEnabled,
                    onChanged: (value) {
                      setState(() {
                        _analyticsEnabled = value;
                      });
                    },
                  ),
                ]),
              ),

              const SizedBox(height: 80),
              ],
            ),
          ),
        ),
      ),
    );
  }

  Widget _buildSection({
    required String title,
    required IconData icon,
    required Color color,
    required Widget child,
  }) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Row(
          children: [
            Container(
              padding: const EdgeInsets.all(12),
              decoration: BoxDecoration(
                gradient: LinearGradient(
                  begin: Alignment.topLeft,
                  end: Alignment.bottomRight,
                  colors: [
                    color,
                    color.withOpacity(0.7),
                  ],
                ),
                borderRadius: BorderRadius.circular(14),
                boxShadow: [
                  BoxShadow(
                    color: color.withOpacity(0.3),
                    blurRadius: 8,
                    offset: const Offset(0, 2),
                  ),
                ],
              ),
              child: Icon(icon, color: Colors.white, size: 22),
            ),
            const SizedBox(width: 12),
            Text(
              title,
              style: const TextStyle(
                fontSize: 20,
                fontWeight: FontWeight.w700,
                color: Color(0xFF1E293B),
              ),
            ),
          ],
        ),
        const SizedBox(height: 16),
        child,
      ],
    );
  }

  Widget _buildCard(List<Widget> children) {
    return Container(
      width: double.infinity,
      padding: const EdgeInsets.all(24),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(20),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withOpacity(0.05),
            blurRadius: 20,
            offset: const Offset(0, 4),
          ),
        ],
      ),
      child: Column(
        children: children,
      ),
    );
  }

  Widget _buildCustomSwitch(String label, Color color, bool currentValue) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: [
        Row(
          children: [
            Container(
              width: 12,
              height: 12,
              decoration: BoxDecoration(
                color: color,
                borderRadius: BorderRadius.circular(3),
              ),
            ),
            const SizedBox(width: 12),
            Text(
              label,
              style: const TextStyle(
                fontSize: 16,
                fontWeight: FontWeight.w500,
                color: Color(0xFF1E293B),
              ),
            ),
          ],
        ),
        Switch(
          value: currentValue,
          activeColor: color,
          activeTrackColor: color.withOpacity(0.5),
          inactiveThumbColor: Colors.grey,
          inactiveTrackColor: Colors.grey.withOpacity(0.3),
          onChanged: (newValue) {
            setState(() {
              if (label == '绿色主题') {
                _greenSwitch = newValue;
              } else if (label == '紫色主题') {
                _purpleSwitch = newValue;
              } else if (label == '橙色主题') {
                _orangeSwitch = newValue;
              }
            });
          },
        ),
      ],
    );
  }

  Widget _buildSwitchTile({
    required String title,
    required String subtitle,
    required IconData icon,
    required bool value,
    required ValueChanged<bool> onChanged,
  }) {
    Color iconColor = Colors.cyan;
    if (title == '推送通知') iconColor = Colors.blue;
    if (title == '深色模式') iconColor = Colors.purple;
    if (title == '位置信息') iconColor = Colors.red;
    if (title == '数据分析') iconColor = Colors.amber;

    return InkWell(
      onTap: () => onChanged(!value),
      borderRadius: BorderRadius.circular(12),
      child: Padding(
        padding: const EdgeInsets.symmetric(vertical: 6),
        child: Row(
          children: [
            Container(
              padding: const EdgeInsets.all(12),
              decoration: BoxDecoration(
                gradient: LinearGradient(
                  begin: Alignment.topLeft,
                  end: Alignment.bottomRight,
                  colors: [
                    iconColor,
                    iconColor.withOpacity(0.7),
                  ],
                ),
                borderRadius: BorderRadius.circular(12),
                boxShadow: [
                  BoxShadow(
                    color: iconColor.withOpacity(0.2),
                    blurRadius: 8,
                    offset: const Offset(0, 2),
                  ),
                ],
              ),
              child: Icon(icon, color: Colors.white, size: 22),
            ),
            const SizedBox(width: 16),
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    title,
                    style: const TextStyle(
                      fontSize: 16,
                      fontWeight: FontWeight.w600,
                      color: Color(0xFF1E293B),
                    ),
                  ),
                  const SizedBox(height: 4),
                  Text(
                    subtitle,
                    style: const TextStyle(
                      fontSize: 13,
                      color: Color(0xFF64748B),
                    ),
                  ),
                ],
              ),
            ),
            Switch(
              value: value,
              onChanged: onChanged,
            ),
          ],
        ),
      ),
    );
  }
}

十、总结

恭喜你!通过这篇文章的学习,你已经掌握了 Flutter 中 Switch 开关组件的全面知识。让我们回顾一下本章的核心内容。

🎯 核心要点

  1. 基础用法value 控制状态,onChanged 响应变化,这是 Switch 的最基本用法
  2. 样式定制 :通过 activeColorinactiveThumbColor 等属性自定义开关外观
  3. 禁用状态 :将 onChanged 设为 null 即可禁用开关
  4. 配合 ListTile:创建标准的设置选项布局,提升用户体验
  5. 最佳实践:为开关添加清晰的标题和说明,使用图标增强识别度

📚 使用建议

场景 推荐方案
设置页面 配合 ListTile 使用,添加标题和说明
功能开关 使用明确的图标和文字描述
颜色定制 保持与应用主题色一致
禁用状态 视觉上明确区分,避免用户困惑
状态管理 每个开关使用独立的状态变量,避免互相影响
相关推荐
KKei163828 分钟前
Flutter for OpenHarmony 健身计划与运动打卡APP
flutter·华为·harmonyos
HwJack201 小时前
HarmonyOS APP开发中userAuthIcon 统一认证控件的原理与实战破局
华为·harmonyos
KKei16381 小时前
Flutter for OpenHarmony 在线考试与自测系统APP技术文章
flutter·华为·harmonyos
美狐美颜SDK开放平台2 小时前
美颜SDK接入流程详解:Android、iOS、鸿蒙兼容方案解析
android·人工智能·ios·华为·harmonyos·美颜sdk·视频美颜sdk
liulian09163 小时前
Flutter 依赖注入与设备信息库:get_it 与 device_info_plus 的 OpenHarmony 适配指南
flutter
richard_yuu3 小时前
鸿蒙路由架构设计|页面跳转、参数传递与全局路由封装最佳实践
华为·harmonyos
KKei16384 小时前
Flutter for OpenHarmony学习目标追踪应用技术文章
学习·flutter·华为·harmonyos
leon_teacher6 小时前
HarmonyOS 6 ArkUI V2 实战:水墨启动页的 3 段错峰入场动画与 setInterval 进度条
华为·harmonyos