在这里插入图片描述

设置页面是每个App的标配,它让用户能够根据自己的喜好定制App的行为。今天我们来实现衣橱管家App的设置功能,包括通知设置、显示设置、数据管理等模块。

设置功能的设计原则

好的设置页面应该简洁明了,让用户能够快速找到想要调整的选项。我们按功能分组,每组有明确的标题,选项使用开关或列表项的形式呈现。

设置项不宜过多,只保留用户真正需要的选项。过多的设置会让用户感到困惑,反而降低使用体验。

页面整体结构

设置页面使用ListView展示各个设置分组,每个分组包含标题和若干设置项。

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

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

  
  State<SettingsScreen> createState() => _SettingsScreenState();
}

class _SettingsScreenState extends State<SettingsScreen> {
  bool _notificationEnabled = true;
  bool _darkMode = false;
  bool _autoBackup = true;
  String _language = '简体中文';

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('设置')),
      body: ListView(
        children: [
          _buildSectionTitle('通知设置'),
          _buildSwitchTile('穿搭提醒', '每日推送穿搭建议', _notificationEnabled, (v) => setState(() => _notificationEnabled = v)),
          // 更多设置项
        ],
      ),
    );
  }
}

StatefulWidget用于管理设置项的状态,每个开关对应一个bool变量。ListView直接使用children,因为设置项数量固定。
状态变量在类顶部声明,方便管理和查看。setState触发界面刷新,更新开关状态。

分组标题组件

每个设置分组都有一个标题,用于说明该组设置的用途。

Widget _buildSectionTitle(String title) {
  return Padding(
    padding: EdgeInsets.fromLTRB(16.w, 16.h, 16.w, 8.h),
    child: Text(title, style: TextStyle(fontSize: 14.sp, color: Colors.grey, fontWeight: FontWeight.bold)),
  );
}

分组标题使用灰色小字,与设置项的黑色文字形成区分。上边距16,下边距8,让标题与上一组有足够间隔,与本组设置项靠近。
fontWeight设为bold,虽然字号小但依然醒目。这种设计在iOS和Android的系统设置中都很常见。

开关设置项

开关设置项用于控制是否启用某个功能,使用SwitchListTile组件实现。

Widget _buildSwitchTile(String title, String subtitle, bool value, ValueChanged<bool> onChanged) {
  return SwitchListTile(
    title: Text(title),
    subtitle: Text(subtitle),
    value: value,
    activeColor: const Color(0xFFE91E63),
    onChanged: onChanged,
  );
}

SwitchListTile是Material Design提供的开关列表项组件,集成了标题、副标题和开关。activeColor设置开关打开时的颜色为品牌色。
onChanged回调在开关状态改变时触发,传入新的bool值。这个方法可以复用于所有开关设置项。

通知设置分组

通知设置让用户控制App的推送行为。

_buildSectionTitle('通知设置'),
_buildSwitchTile('穿搭提醒', '每日推送穿搭建议', _notificationEnabled, (v) => setState(() => _notificationEnabled = v)),
_buildSwitchTile('洗衣提醒', '待洗衣物超过5件时提醒', true, (v) {}),
_buildSwitchTile('预算提醒', '预算使用超过80%时提醒', true, (v) {}),

三个通知选项覆盖了App的主要提醒场景:穿搭建议、洗衣提醒、预算提醒。每个选项都有清晰的说明文字。
副标题说明触发条件,让用户知道什么情况下会收到通知。

显示设置分组

显示设置让用户调整App的外观。

_buildSectionTitle('显示设置'),
_buildSwitchTile('深色模式', '切换深色/浅色主题', _darkMode, (v) => setState(() => _darkMode = v)),
ListTile(
  title: const Text('语言'),
  subtitle: Text(_language),
  trailing: const Icon(Icons.chevron_right),
  onTap: () => _showLanguageDialog(),
),

深色模式使用开关控制,语言选择使用列表项点击后弹出选择对话框。trailing的右箭头图标暗示用户这个选项可以点击进入。
subtitle显示当前选择的语言,让用户不用点击就能看到当前设置。

语言选择对话框

点击语言选项后弹出对话框,让用户选择语言。

void _showLanguageDialog() {
  showDialog(
    context: context,
    builder: (context) => AlertDialog(
      title: const Text('选择语言'),
      content: Column(
        mainAxisSize: MainAxisSize.min,
        children: ['简体中文', 'English', '日本語'].map((lang) {
          return RadioListTile<String>(
            title: Text(lang),
            value: lang,
            groupValue: _language,
            onChanged: (v) {
              setState(() => _language = v!);
              Navigator.pop(context);
            },
          );
        }).toList(),
      ),
    ),
  );
}

RadioListTile实现单选列表,groupValue是当前选中的值,value是该选项的值。选中后更新状态并关闭对话框。
mainAxisSize设为min让Column只占用必要的高度。三种语言覆盖了主要的用户群体。

数据管理分组

数据管理让用户控制数据的备份和恢复。

_buildSectionTitle('数据管理'),
_buildSwitchTile('自动备份', '自动备份衣橱数据', _autoBackup, (v) => setState(() => _autoBackup = v)),
ListTile(
  title: const Text('导出数据'),
  subtitle: const Text('导出衣橱数据到本地'),
  trailing: const Icon(Icons.chevron_right),
  onTap: () => ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('功能开发中'))),
),
ListTile(
  title: const Text('导入数据'),
  subtitle: const Text('从备份文件恢复数据'),
  trailing: const Icon(Icons.chevron_right),
  onTap: () => ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('功能开发中'))),
),
ListTile(
  title: const Text('清除缓存'),
  subtitle: const Text('清除应用缓存数据'),
  trailing: const Icon(Icons.chevron_right),
  onTap: () => _showClearCacheDialog(),
),

数据管理包含自动备份开关、导出数据、导入数据、清除缓存四个选项。导出导入功能暂时显示开发中提示。
清除缓存需要确认,避免用户误操作。

清除缓存对话框

清除缓存前需要用户确认。

void _showClearCacheDialog() {
  showDialog(
    context: context,
    builder: (context) => AlertDialog(
      title: const Text('清除缓存'),
      content: const Text('确定要清除应用缓存吗?'),
      actions: [
        TextButton(onPressed: () => Navigator.pop(context), child: const Text('取消')),
        TextButton(
          onPressed: () {
            Navigator.pop(context);
            ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('缓存已清除')));
          },
          child: const Text('确定'),
        ),
      ],
    ),
  );
}

确认对话框包含取消和确定两个按钮,符合用户习惯。确定后显示SnackBar提示操作成功。
实际项目中需要调用清除缓存的逻辑,这里只是展示UI交互。

账号设置分组

账号设置包含重置数据等危险操作。

_buildSectionTitle('账号'),
ListTile(
  title: const Text('重置数据'),
  subtitle: const Text('清除所有衣橱数据'),
  trailing: const Icon(Icons.chevron_right, color: Colors.red),
  onTap: () => _showResetDialog(),
),

重置数据是危险操作,trailing图标使用红色警示。点击后弹出确认对话框,需要用户明确确认。
这种设计防止用户误操作导致数据丢失。

重置数据对话框

重置数据需要更强的确认,提示用户操作不可恢复。

void _showResetDialog() {
  showDialog(
    context: context,
    builder: (context) => AlertDialog(
      title: const Text('重置数据'),
      content: const Text('此操作将清除所有衣橱数据,且无法恢复。确定要继续吗?'),
      actions: [
        TextButton(onPressed: () => Navigator.pop(context), child: const Text('取消')),
        TextButton(
          onPressed: () {
            Navigator.pop(context);
            ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('数据已重置')));
          },
          child: const Text('重置', style: TextStyle(color: Colors.red)),
        ),
      ],
    ),
  );
}

对话框内容明确说明操作不可恢复,让用户充分了解后果。确定按钮使用红色文字,强调危险性。
实际项目中需要调用清除数据的逻辑,并可能需要退出登录或重启App。

版本信息展示

在设置页面底部显示App版本信息。

Widget _buildVersionInfo() {
  return Padding(
    padding: EdgeInsets.all(16.w),
    child: Center(
      child: Column(
        children: [
          Text('衣橱管家', style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold)),
          SizedBox(height: 4.h),
          Text('版本 1.0.0', style: TextStyle(fontSize: 12.sp, color: Colors.grey)),
          SizedBox(height: 4.h),
          Text('© 2024 OpenHarmony', style: TextStyle(fontSize: 12.sp, color: Colors.grey)),
        ],
      ),
    ),
  );
}

版本信息居中显示,包含App名称、版本号和版权信息。使用灰色小字,不抢眼但能被注意到。
版本号在用户反馈问题时很有用,方便开发者定位问题。

设置持久化

设置项的值需要持久化存储,下次打开App时恢复。


void initState() {
  super.initState();
  _loadSettings();
}

Future<void> _loadSettings() async {
  final prefs = await SharedPreferences.getInstance();
  setState(() {
    _notificationEnabled = prefs.getBool('notification_enabled') ?? true;
    _darkMode = prefs.getBool('dark_mode') ?? false;
    _autoBackup = prefs.getBool('auto_backup') ?? true;
    _language = prefs.getString('language') ?? '简体中文';
  });
}

Future<void> _saveSetting(String key, dynamic value) async {
  final prefs = await SharedPreferences.getInstance();
  if (value is bool) {
    await prefs.setBool(key, value);
  } else if (value is String) {
    await prefs.setString(key, value);
  }
}

SharedPreferences是Flutter常用的本地存储方案,适合存储简单的键值对。initState中加载设置,每次修改时保存。
_saveSetting方法根据值的类型调用不同的set方法,支持bool和String两种类型。

深色模式实现

深色模式需要在App级别切换主题。

// 在main.dart中
class MyApp extends StatefulWidget {
  
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  ThemeMode _themeMode = ThemeMode.light;

  void setThemeMode(ThemeMode mode) {
    setState(() => _themeMode = mode);
  }

  
  Widget build(BuildContext context) {
    return MaterialApp(
      themeMode: _themeMode,
      theme: ThemeData.light(),
      darkTheme: ThemeData.dark(),
      // ...
    );
  }
}

MaterialApp的themeMode属性控制使用哪个主题,theme是浅色主题,darkTheme是深色主题。
设置页面修改深色模式开关时,需要调用App级别的setThemeMode方法。可以使用Provider或其他状态管理方案实现。

关于页面入口

设置页面通常包含关于页面的入口。

ListTile(
  title: const Text('关于我们'),
  trailing: const Icon(Icons.chevron_right),
  onTap: () => Navigator.push(
    context,
    MaterialPageRoute(builder: (_) => const AboutScreen()),
  ),
),
ListTile(
  title: const Text('使用帮助'),
  trailing: const Icon(Icons.chevron_right),
  onTap: () => Navigator.push(
    context,
    MaterialPageRoute(builder: (_) => const HelpScreen()),
  ),
),
ListTile(
  title: const Text('意见反馈'),
  trailing: const Icon(Icons.chevron_right),
  onTap: () => Navigator.push(
    context,
    MaterialPageRoute(builder: (_) => const FeedbackScreen()),
  ),
),

关于我们、使用帮助、意见反馈是常见的设置项,点击后跳转到对应页面。
这些入口让用户能够了解App信息、获取帮助、提交反馈。

完整代码整合

把所有功能整合在一起,形成完整的设置页面。

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

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

  
  State<SettingsScreen> createState() => _SettingsScreenState();
}

class _SettingsScreenState extends State<SettingsScreen> {
  bool _notificationEnabled = true;
  bool _darkMode = false;
  bool _autoBackup = true;
  String _language = '简体中文';

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('设置')),
      body: ListView(
        children: [
          _buildSectionTitle('通知设置'),
          _buildSwitchTile('穿搭提醒', '每日推送穿搭建议', _notificationEnabled, (v) => setState(() => _notificationEnabled = v)),
          _buildSwitchTile('洗衣提醒', '待洗衣物超过5件时提醒', true, (v) {}),
          _buildSwitchTile('预算提醒', '预算使用超过80%时提醒', true, (v) {}),
          
          _buildSectionTitle('显示设置'),
          _buildSwitchTile('深色模式', '切换深色/浅色主题', _darkMode, (v) => setState(() => _darkMode = v)),
          ListTile(
            title: const Text('语言'),
            subtitle: Text(_language),
            trailing: const Icon(Icons.chevron_right),
            onTap: () => _showLanguageDialog(),
          ),
          
          _buildSectionTitle('数据管理'),
          _buildSwitchTile('自动备份', '自动备份衣橱数据', _autoBackup, (v) => setState(() => _autoBackup = v)),
          ListTile(
            title: const Text('导出数据'),
            subtitle: const Text('导出衣橱数据到本地'),
            trailing: const Icon(Icons.chevron_right),
            onTap: () => ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('功能开发中'))),
          ),
          ListTile(
            title: const Text('导入数据'),
            subtitle: const Text('从备份文件恢复数据'),
            trailing: const Icon(Icons.chevron_right),
            onTap: () => ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('功能开发中'))),
          ),
          ListTile(
            title: const Text('清除缓存'),
            subtitle: const Text('清除应用缓存数据'),
            trailing: const Icon(Icons.chevron_right),
            onTap: () => _showClearCacheDialog(),
          ),
          
          _buildSectionTitle('账号'),
          ListTile(
            title: const Text('重置数据'),
            subtitle: const Text('清除所有衣橱数据'),
            trailing: const Icon(Icons.chevron_right, color: Colors.red),
            onTap: () => _showResetDialog(),
          ),
        ],
      ),
    );
  }

  Widget _buildSectionTitle(String title) {
    return Padding(
      padding: EdgeInsets.fromLTRB(16.w, 16.h, 16.w, 8.h),
      child: Text(title, style: TextStyle(fontSize: 14.sp, color: Colors.grey, fontWeight: FontWeight.bold)),
    );
  }

  Widget _buildSwitchTile(String title, String subtitle, bool value, ValueChanged<bool> onChanged) {
    return SwitchListTile(
      title: Text(title),
      subtitle: Text(subtitle),
      value: value,
      activeColor: const Color(0xFFE91E63),
      onChanged: onChanged,
    );
  }

  void _showLanguageDialog() {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('选择语言'),
        content: Column(
          mainAxisSize: MainAxisSize.min,
          children: ['简体中文', 'English', '日本語'].map((lang) {
            return RadioListTile<String>(
              title: Text(lang),
              value: lang,
              groupValue: _language,
              onChanged: (v) {
                setState(() => _language = v!);
                Navigator.pop(context);
              },
            );
          }).toList(),
        ),
      ),
    );
  }

  void _showClearCacheDialog() {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('清除缓存'),
        content: const Text('确定要清除应用缓存吗?'),
        actions: [
          TextButton(onPressed: () => Navigator.pop(context), child: const Text('取消')),
          TextButton(
            onPressed: () {
              Navigator.pop(context);
              ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('缓存已清除')));
            },
            child: const Text('确定'),
          ),
        ],
      ),
    );
  }

  void _showResetDialog() {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('重置数据'),
        content: const Text('此操作将清除所有衣橱数据,且无法恢复。确定要继续吗?'),
        actions: [
          TextButton(onPressed: () => Navigator.pop(context), child: const Text('取消')),
          TextButton(
            onPressed: () {
              Navigator.pop(context);
              ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('数据已重置')));
            },
            child: const Text('重置', style: TextStyle(color: Colors.red)),
          ),
        ],
      ),
    );
  }
}

代码结构清晰,设置项按分组排列,每个分组有明确的标题。_buildSectionTitle和_buildSwitchTile方法实现代码复用。
对话框方法独立封装,便于维护和测试。危险操作使用红色警示,防止误操作。

写在最后

设置页面虽然不是App的核心功能,但它直接影响用户的使用体验。一个好的设置页面应该简洁明了,让用户能够快速找到想要调整的选项。

希望这篇文章能帮助你实现一个完善的设置功能,让用户能够根据自己的喜好定制App。

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐