在这里插入图片描述

成就系统在训练类 App 里承担两个核心角色:

  • 正向激励:让用户看到自己的成长里程碑
  • 目标引导:给用户明确的努力方向

当前实现是纯展示型,用 ListView 展示若干成就项,并用颜色和图标区分“已解锁/未解锁”。

本文涉及文件

  • lib/feature_pages.dart
  • lib/app.dart
  • lib/main.dart

1. 入口在哪里:从“进度统计”进入

成就系统属于 ProgressStatsPage(进度统计)下的一个入口项。
入口页负责 push 到 AchievementSystemPage,核心跳转代码示例如下:

// 进度统计页面的入口按钮点击事件
onTap: () {
  Navigator.push(
    context,
    MaterialPageRoute(
      builder: (context) => const AchievementSystemPage(),
    ),
  );
},
  • 跳转逻辑采用 Flutter 标准的 Navigator.push 方式,保证路由跳转的稳定性
  • 使用 MaterialPageRoute 适配 Material 设计规范,过渡动画更符合用户习惯
  • 传入 AchievementSystemPage 实例时添加 const 关键字,减少不必要的对象重建

2. AchievementSystemPage 的真实代

下面这段实现来自你项目 lib/feature_pages.dart
为了保证“代码真实可运行”,我分模块拆解并补充说明:

2.1 页面基础结构定义

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

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('成就系统')),
  • 采用 StatelessWidget 符合当前静态展示的业务场景,无状态管理成本
  • 构造方法添加 const 修饰,提升组件复用效率,减少内存占用
  • AppBar 配置明确的标题文本,保证页面导航的可读性
  • super.key 传递父类 key,避免组件重建时的标识混乱

2.2 页面主体布局容器

      body: Padding(
        padding: EdgeInsets.all(16.w),
        child: ListView(
          children: [
            Text('成就徽章', style: TextStyle(
              fontSize: 20.sp, 
              fontWeight: FontWeight.bold
            )),
  • Padding 采用 16.w 响应式间距,适配不同屏幕尺寸的边距需求
  • 外层使用 ListView 而非 Column,核心优势如下:
    1. 自动处理内容溢出,小屏设备下支持滚动浏览
    2. 懒加载渲染(默认构造器虽非懒加载,但基础版已满足当前需求)
    3. 适配不同数量的成就项扩展,无需修改布局结构
  • 标题文本设置 20.sp 响应式字号 + 加粗样式,层级区分更清晰

2.3 成就列表项占位与间距控制

            SizedBox(height: 24.h),
            _buildAchievement(
              '逆向新手', 
              '完成第一个逆向思维题', 
              Icons.star, 
              Colors.brown, 
              true
            ),
  • SizedBox(height: 24.h) 采用响应式高度,保证不同屏幕下的间距一致性
  • _buildAchievement 方法调用时参数清晰,按“标题-描述-图标-颜色-状态”顺序传递,可读性强
  • 第一个成就项“逆向新手”作为入门级成就,引导用户完成基础任务
  • 图标选择 Icons.star 贴合“新手徽章”的视觉认知,颜色选用 Colors.brown 区分不同成就类别

2.4 更多成就项配置

            _buildAchievement(
              '逻辑大师', 
              '完成50道逻辑题', 
              Icons.psychology, 
              Colors.blue, 
              true
            ),
            _buildAchievement(
              '模式专家', 
              '完成30道模式题', 
              Icons.grid_on, 
              Colors.purple, 
              false
            ),
  • “逻辑大师”成就绑定 Icons.psychology 图标,视觉上贴合“逻辑思维”的业务属性
  • “模式专家”选用 Icons.grid_on 图标,匹配“模式题”的网格/规律类题型特征
  • 不同成就项配置不同的主题色(蓝色/紫色),视觉区分度更高
  • 解锁状态通过布尔值直接传递,逻辑简单直观

2.5 剩余成就项补充

            _buildAchievement(
              '文字高手', 
              '完成40道文字题', 
              Icons.text_fields, 
              Colors.green, 
              false
            ),
            _buildAchievement(
              '全能冠军', 
              '完成所有题目', 
              Icons.emoji_events, 
              Colors.yellow, 
              false
            ),
          ],
        ),
      ),
    );
  }
  • “文字高手”使用 Icons.text_fields 图标,精准匹配“文字题”的业务场景
  • “全能冠军”选用 Icons.emoji_events 奖杯图标,强化“最高成就”的视觉感知
  • 黄色主题色(Colors.yellow)符合“冠军”的视觉认知,提升成就的荣誉感
  • 所有成就项包裹在 ListViewchildren 列表中,保证布局的统一性

2.6 成就项构建方法定义

  Widget _buildAchievement(
    String title, 
    String description, 
    IconData icon, 
    Color color, 
    bool unlocked
  ) {
    return Card(
      margin: EdgeInsets.only(bottom: 12.h),
      child: ListTile(
  • 方法参数做了清晰的类型标注,提升代码的可维护性
  • Card 组件提供基础的卡片样式,包含圆角、阴影效果,提升视觉质感
  • margin: EdgeInsets.only(bottom: 12.h) 仅设置底部间距,避免上下间距重复叠加
  • 响应式单位 h 保证不同屏幕下的间距比例一致

2.7 成就项左侧图标区域

        leading: CircleAvatar(
          backgroundColor: unlocked ? color : Colors.grey,
          child: Icon(icon, color: Colors.white),
        ),
  • CircleAvatar 实现圆形图标容器,符合成就徽章的视觉形态
  • 背景色根据解锁状态动态切换:
    1. 已解锁:使用成就主题色,强化视觉识别
    2. 未解锁:统一使用 Colors.grey,弱化显示,引导用户完成任务
  • 图标固定使用 Colors.white,保证在不同背景色下的可读性

2.8 成就项标题与副标题

        title: Text(title, style: TextStyle(
          fontSize: 16.sp,
          color: unlocked ? Colors.black : Colors.grey,
        )),
        subtitle: Text(description),
  • 标题字号设置为 16.sp,比页面标题小一级,形成合理的视觉层级
  • 标题颜色随解锁状态动态变化,与左侧图标背景色形成视觉呼应
  • 副标题直接展示成就描述文本,无额外样式修饰,保证信息的简洁性
  • 未解锁状态下标题置灰,进一步强化“未完成”的视觉提示

2.9 成就项右侧状态图标

        trailing: unlocked 
            ? const Icon(Icons.check, color: Colors.green) 
            : const Icon(Icons.lock),
      ),
    );
  }
}
  • 三元运算符简洁区分两种状态图标,代码可读性高
  • 已解锁使用 Icons.check + 绿色,符合“完成/成功”的视觉认知
  • 未解锁使用 Icons.lock 原生样式,直观表达“锁定”状态
  • 图标添加 const 修饰,减少组件重建开销
  • 方法结尾闭合 CardListTile 等组件,保证布局结构的完整性

3. 为什么用 StatelessWidget:当前成就数据是静态写死

你把成就系统写成 StatelessWidget,核心原因和扩展建议如下:

3.1 核心原因

// 静态写死的成就数据示例
_buildAchievement('逆向新手', '完成第一个逆向思维题', Icons.star, Colors.brown, true),
  • 成就列表直接写在 UI 代码中,无动态数据来源
  • unlocked 状态为固定布尔值,无运行时状态变更逻辑
  • 页面无交互操作(如点击解锁、下拉刷新),无需状态管理
  • StatelessWidget 代码量更少,维护成本更低

3.2 扩展方向1:改为 StatefulWidget 动态拉取数据

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

  
  State<AchievementSystemPage> createState() => _AchievementSystemPageState();
}
  • 若需从本地/服务端拉取成就数据,需改为 StatefulWidget
  • 通过 createState 方法创建状态类,管理动态数据
  • 可在 initState 中初始化数据请求,保证页面加载时获取最新状态

3.3 扩展方向2:保留 StatelessWidget 接收外部数据

// 改造后的构造方法,接收外部成就列表数据
class AchievementSystemPage extends StatelessWidget {
  final List<AchievementModel> achievements;
  const AchievementSystemPage({
    super.key,
    required this.achievements,
  });
  • 定义 AchievementModel 数据模型,统一成就数据结构
  • 通过构造方法接收外部传入的成就列表,保持组件无状态特性
  • 父组件负责数据管理和更新,子组件仅负责展示,符合单一职责原则

3.4 成就数据模型定义

// 成就数据模型示例
class AchievementModel {
  final String title;
  final String description;
  final IconData icon;
  final Color color;
  final bool unlocked;

  const AchievementModel({
    required this.title,
    required this.description,
    required this.icon,
    required this.color,
    required this.unlocked,
  });
}
  • 使用不可变模型(所有字段 final),保证数据安全性
  • 构造方法添加 required 修饰,强制传入所有必要字段
  • 支持 const 构造,提升实例创建效率
  • 统一管理成就数据,便于后续扩展和维护

4. ListView:成就列表可滚动

你用 ListView 包裹所有成就项,补充优化点和对比代码:

4.1 基础 ListView 实现

ListView(
  children: [
    // 成就标题和成就项
  ],
)
  • 核心优势:
    1. 自动处理内容溢出,小屏设备下支持垂直滚动
    2. 无需手动计算高度,适配不同数量的成就项
    3. 继承 ScrollView 特性,支持滚动物理效果(如回弹)

4.2 优化版 ListView.builder

ListView.builder(
  itemCount: achievements.length,
  itemBuilder: (context, index) {
    final achievement = achievements[index];
    return _buildAchievement(
      achievement.title,
      achievement.description,
      achievement.icon,
      achievement.color,
      achievement.unlocked,
    );
  },
)
  • 使用 ListView.builder 实现懒加载,仅渲染可视区域内的成就项
  • 通过 itemCount 控制列表长度,避免空列表异常
  • 索引遍历获取成就数据,适配动态列表场景
  • 减少内存占用,提升大量成就项时的渲染性能

4.3 与 Column 的对比

// 不推荐的 Column 写法(易溢出)
Column(
  children: [
    // 成就项列表
  ],
)
  • 缺点分析:
    1. 成就项数量增加时,内容会超出屏幕高度导致溢出
    2. 无滚动能力,用户无法查看超出可视区域的内容
    3. 需要手动嵌套 SingleChildScrollView,代码冗余

5. _buildAchievement:把重复 UI 抽成方法

你将重复的成就项布局抽成独立方法,补充方法设计细节和扩展优化:

5.1 方法定义核心价值

Widget _buildAchievement(
  String title, 
  String description, 
  IconData icon, 
  Color color, 
  bool unlocked
) {
  return Card(
    margin: EdgeInsets.only(bottom: 12.h),
    child: ListTile(
  • 方法参数覆盖成就项的所有视觉属性,扩展性强
  • 统一渲染逻辑,后续修改样式仅需调整该方法,符合“单一修改点”原则
  • 参数命名语义化(title/description/icon 等),代码可读性高
  • 方法返回值为 Widget,可直接嵌入 ListView 列表,集成性好

5.2 方法扩展:添加点击事件

// 扩展:为成就项添加点击事件
Widget _buildAchievement(
  String title, 
  String description, 
  IconData icon, 
  Color color, 
  bool unlocked, {
  VoidCallback? onTap,
}) {
  return Card(
    margin: EdgeInsets.only(bottom: 12.h),
    child: ListTile(
      onTap: unlocked ? onTap : null,
  • 新增可选参数 onTap,支持为已解锁成就添加点击事件
  • 未解锁成就禁用点击(onTap: null),符合交互逻辑
  • 使用命名参数({} 包裹),避免参数传递顺序混乱
  • 点击事件仅对已解锁成就生效,引导用户先完成任务解锁

5.3 方法扩展:添加成就进度

// 扩展:显示成就完成进度
subtitle: Column(
  crossAxisAlignment: CrossAxisAlignment.start,
  children: [
    Text(description),
    if (!unlocked)
      Padding(
        padding: EdgeInsets.only(top: 4.h),
        child: LinearProgressIndicator(
          value: 0.3, // 示例进度值,实际从数据获取
          backgroundColor: Colors.grey[200],
          valueColor: AlwaysStoppedAnimation<Color>(color),
        ),
      ),
  ],
),
  • 未解锁成就显示进度条,直观展示完成进度
  • 进度条使用成就主题色,保持视觉一致性
  • 进度值 value 可从用户做题数据中动态计算
  • 进度条添加顶部间距,避免与描述文本重叠

6. Card + ListTile:成就项的标准布局

每条成就用 Card 包裹 ListTile,补充布局细节和视觉设计思路:

6.1 Card 组件的视觉优化

Card(
  elevation: 2, // 阴影高度
  shape: RoundedRectangleBorder(
    borderRadius: BorderRadius.circular(8.r),
  ),
  margin: EdgeInsets.only(bottom: 12.h),
  child: ListTile(
  • elevation: 2 设置轻微阴影,提升卡片层次感,不突兀
  • shape 自定义圆角半径(8.r 响应式),适配不同屏幕的圆角比例
  • Card 组件默认有内边距,与 ListTile 配合无需额外调整间距
  • 仅设置底部 margin,保证列表项之间的间距均匀

6.2 ListTile 组件的布局优势

ListTile(
  leading: CircleAvatar(...),
  title: Text(...),
  subtitle: Text(...),
  trailing: Icon(...),
)
  • ListTile 是 Flutter 内置的标准化列表项布局,包含四个核心区域:
    1. leading:左侧前置组件(成就图标)
    2. title:主标题(成就名称)
    3. subtitle:副标题(成就描述)
    4. trailing:右侧后置组件(状态图标)
  • 无需手动调整各区域的间距和对齐方式,减少布局代码量
  • 适配不同屏幕尺寸的自动布局,兼容性强

6.3 视觉层次设计

  • 已解锁成就:
    1. 圆形图标背景为主题色,视觉突出
    2. 标题为黑色,对比度高
    3. 右侧对勾图标为绿色,强化完成状态
  • 未解锁成就:
    1. 圆形图标背景为灰色,视觉弱化
    2. 标题为灰色,降低关注度
    3. 右侧锁图标为默认色,表达锁定状态
  • 整体视觉层次清晰,用户可快速区分成就状态

7. leading:用 CircleAvatar + Icon 做徽章

你用 CircleAvatar 包裹 Icon 实现成就徽章,补充设计细节和优化点:

7.1 核心实现代码与解析

leading: CircleAvatar(
  radius: 24.r, // 自定义半径
  backgroundColor: unlocked ? color : Colors.grey,
  child: Icon(
    icon,
    size: 18.sp,
    color: Colors.white,
  ),
),
  • radius: 24.r 自定义圆形头像半径,适配成就徽章的视觉大小
  • 图标尺寸 18.sp 与半径匹配,避免图标过大/过小
  • Colors.white 图标颜色在彩色/灰色背景下均清晰可见
  • 响应式单位 r/sp 保证不同屏幕下的比例一致

7.2 扩展:添加成就等级标识

// 扩展:在徽章中添加等级标识
CircleAvatar(
  backgroundColor: unlocked ? color : Colors.grey,
  child: Stack(
    alignment: Alignment.center,
    children: [
      Icon(icon, color: Colors.white),
      Positioned(
        bottom: 0,
        right: 0,
        child: Container(
          padding: EdgeInsets.all(2.r),
          decoration: BoxDecoration(
            color: Colors.red,
            borderRadius: BorderRadius.circular(8.r),
          ),
          child: Text('1', style: TextStyle(
            fontSize: 8.sp,
            color: Colors.white,
          )),
        ),
      ),
    ],
  ),
),
  • 使用 Stack 叠加图标和等级标识,丰富徽章信息
  • 等级标识使用小尺寸文本 + 红色背景,视觉突出但不喧宾夺主
  • 圆角容器包裹等级文本,符合整体圆润的设计风格
  • 仅在已解锁成就中显示等级,未解锁状态隐藏

8. title 的颜色:解锁/未解锁的视觉区分

你根据解锁状态设置标题颜色,补充视觉设计和扩展优化:

8.1 核心实现与设计思路

title: Text(title, style: TextStyle(
  fontSize: 16.sp,
  fontWeight: FontWeight.w500, // 补充字重
  color: unlocked ? Colors.black : Colors.grey[600],
)),
  • 新增 FontWeight.w500,比普通文本稍粗,突出成就名称
  • 未解锁状态使用 Colors.grey[600](中灰色),比纯灰色更柔和
  • 字号 16.sp 介于页面标题(20.sp)和副标题之间,层级清晰
  • 颜色对比:已解锁(黑色)vs 未解锁(中灰色),区分度适中

8.2 扩展:添加成就解锁时间

// 扩展:已解锁成就显示解锁时间
title: Column(
  crossAxisAlignment: CrossAxisAlignment.start,
  children: [
    Text(title, style: TextStyle(
      fontSize: 16.sp,
      color: unlocked ? Colors.black : Colors.grey[600],
    )),
    if (unlocked)
      Text(
        '解锁于: 2024-05-20', // 实际从数据获取
        style: TextStyle(fontSize: 10.sp, color: Colors.grey[500]),
      ),
  ],
),
  • 已解锁成就下方显示解锁时间,增强成就感和记录感
  • 时间文本使用小号字体(10.sp)+ 浅灰色,不抢主标题风头
  • 仅在已解锁状态下显示,避免未解锁成就的信息冗余
  • 时间格式统一为“年-月-日”,符合用户阅读习惯

9. trailing:右侧用 check/lock 表达状态

你用对勾/锁图标表达解锁状态,补充交互设计和扩展优化:

9.1 核心实现与交互逻辑

trailing: unlocked 
    ? Icon(Icons.check_circle, color: Colors.green[500], size: 20.sp)
    : Icon(Icons.lock_outline, size: 20.sp),
  • 替换为 Icons.check_circle(圆形对勾),视觉更饱满
  • 绿色使用 Colors.green[500],比纯绿色更自然
  • 锁图标使用 Icons.lock_outline(轮廓版),与对勾图标视觉风格统一
  • 统一设置图标尺寸 20.sp,保证视觉一致性

9.2 扩展:添加成就点击反馈

// 扩展:已解锁成就添加点击波纹效果
trailing: unlocked
    ? InkWell(
        onTap: () {
          // 展示成就详情弹窗
          showAchievementDetail(context, title);
        },
        child: Icon(Icons.check_circle, color: Colors.green[500]),
      )
    : Icon(Icons.lock_outline),
  • 为已解锁成就的右侧图标添加点击事件,支持查看成就详情
  • 使用 InkWell 实现水波纹点击反馈,符合 Material 设计规范
  • 未解锁成就仍为普通图标,无点击反馈,交互逻辑清晰
  • 点击后调用 showAchievementDetail 方法展示详情弹窗

9.3 成就详情弹窗实现

// 成就详情弹窗示例
void showAchievementDetail(BuildContext context, String title) {
  showDialog(
    context: context,
    builder: (context) => AlertDialog(
      title: Text('成就详情'),
      content: Text('你已解锁「$title」成就,继续加油!'),
      actions: [
        TextButton(
          onPressed: () => Navigator.pop(context),
          child: const Text('确定'),
        ),
      ],
    ),
  );
}
  • 使用 showDialog 弹出标准对话框,交互体验统一
  • 弹窗标题和内容清晰,强化成就解锁的正向反馈
  • 提供“确定”按钮关闭弹窗,操作简单明确
  • 适配不同屏幕尺寸,弹窗自动居中显示

10. 成就项的语义设计:从“新手”到“冠军”

你设计的 5 个成就形成了一个递进路径,补充语义设计细节和扩展建议:

10.1 成就层级设计

成就名称 解锁条件 难度层级 设计意图
逆向新手 完成1道逆向思维题 入门 降低首次解锁门槛,提升用户初期参与感
逻辑大师 完成50道逻辑题 进阶 引导用户深耕逻辑题型,提升专项能力
模式专家 完成30道模式题 进阶 覆盖另一核心题型,丰富训练维度
文字高手 完成40道文字题 进阶 覆盖文字类题型,保证题型全覆盖
全能冠军 完成所有题目 终极 设定最高目标,激励用户完成全部训练

10.2 扩展成就体系

// 扩展成就项示例
_buildAchievement(
  '每日打卡', 
  '连续7天完成每日训练', 
  Icons.calendar_today, 
  Colors.orange, 
  false
),
_buildAchievement(
  '速通达人', 
  '10分钟内完成10道题', 
  Icons.speed, 
  Colors.red, 
  false
),
  • 新增“每日打卡”成就,鼓励用户持续参与,提升留存率
  • 新增“速通达人”成就,激励用户提升解题速度,增加挑战性
  • 不同成就类别使用差异化图标和颜色,视觉区分度更高
  • 解锁条件覆盖“持续参与”和“解题效率”,丰富成就维度

10.3 成就排序逻辑

// 成就列表排序示例(按解锁状态+难度)
final sortedAchievements = [
  ...achievements.where((a) => a.unlocked).toList()
    ..sort((a, b) => a.title.compareTo(b.title)),
  ...achievements.where((a) => !a.unlocked).toList()
    ..sort((a, b) => getDifficultyLevel(a).compareTo(getDifficultyLevel(b))),
];
  • 已解锁成就按名称排序,保证展示顺序稳定
  • 未解锁成就按难度层级排序,引导用户从易到难完成
  • 使用扩展运算符(...)合并两个列表,代码简洁
  • 提取 getDifficultyLevel 方法获取成就难度,逻辑复用

11. 如何接入真实解锁逻辑:从做题记录统计

你当前 unlocked 是写死的,补充真实解锁逻辑的实现方案:

11.1 本地数据存储

// 引入 Hive 存储用户做题记录
import 'package:hive/hive.dart';

// 初始化 Hive 盒子
final box = Hive.box('user_data');

// 存储做题记录
void saveProblemRecord(String type, int count) {
  box.put('${type}_count', count);
}
  • 使用 Hive 轻量级本地存储,无需复杂配置,性能高
  • 按题型分类存储完成数量,便于后续统计
  • 键名格式统一为“题型_count”,便于数据管理
  • 支持跨会话持久化存储,用户重启 App 数据不丢失

11.2 成就解锁逻辑计算

// 计算成就解锁状态
bool checkAchievementUnlocked(String achievementType) {
  switch (achievementType) {
    case 'reverse_newbie':
      return box.get('reverse_count', defaultValue: 0) >= 1;
    case 'logic_master':
      return box.get('logic_count', defaultValue: 0) >= 50;
    case 'pattern_expert':
      return box.get('pattern_count', defaultValue: 0) >= 30;
    case 'text_master':
      return box.get('text_count', defaultValue: 0) >= 40;
    case 'all_champion':
      return checkAllProblemsCompleted();
    default:
      return false;
  }
}
  • 使用 switch 语句区分不同成就的解锁条件,逻辑清晰
  • 从 Hive 中获取对应题型的完成数量,默认值为 0,避免空值异常
  • 封装 checkAllProblemsCompleted 方法判断是否完成所有题目
  • 返回布尔值直接映射到成就的 unlocked 状态,集成简单

11.3 全题完成判断方法

// 判断是否完成所有题目
bool checkAllProblemsCompleted() {
  final reverseCount = box.get('reverse_count', defaultValue: 0);
  final logicCount = box.get('logic_count', defaultValue: 0);
  final patternCount = box.get('pattern_count', defaultValue: 0);
  final textCount = box.get('text_count', defaultValue: 0);
  
  // 假设各题型总题数
  const totalReverse = 10;
  const totalLogic = 50;
  const totalPattern = 30;
  const totalText = 40;
  
  return reverseCount >= totalReverse &&
         logicCount >= totalLogic &&
         patternCount >= totalPattern &&
         textCount >= totalText;
}
  • 分别获取各题型的完成数量,与总题数对比
  • 总题数使用 const 常量定义,便于后续修改
  • 所有题型都完成时返回 true,否则返回 false
  • 逻辑清晰,便于后续扩展更多题型的判断

11.4 成就状态更新时机

// 完成题目后更新成就状态
void updateAchievementStatusAfterSolve() {
  // 保存本次做题记录
  saveProblemRecord(currentProblemType, currentCount + 1);
  
  // 重新计算所有成就解锁状态
  setState(() {
    achievements = achievements.map((a) {
      return AchievementModel(
        title: a.title,
        description: a.description,
        icon: a.icon,
        color: a.color,
        unlocked: checkAchievementUnlocked(a.type),
      );
    }).toList();
  });
}
  • 在用户完成题目后立即更新成就状态,保证数据实时性
  • 先保存做题记录,再重新计算成就状态,数据顺序正确
  • 使用 setState 更新状态(StatefulWidget 场景),触发 UI 刷新
  • 遍历成就列表重新计算解锁状态,保证所有成就同步更新

12. 小结:成就系统实现的关键点

  • 展示清晰:Card + ListTile 组合,符合 Material 设计规范,视觉层次清晰
  • 状态直观:颜色 + 图标区分解锁/未解锁,用户可快速识别成就状态
  • 复用到位:_buildAchievement 抽取重复逻辑,后续修改仅需调整一处
  • 语义递进:从新手到冠军的成就路径,形成完整的激励体系
  • 扩展灵活:支持静态展示/动态数据两种模式,适配不同业务阶段
  • 性能优化:采用响应式单位、懒加载列表等,保证多设备适配和渲染性能
  • 交互友好:添加点击反馈、进度展示、详情弹窗等,提升用户体验

13. 常见踩坑点:忘记 StatelessWidget 导致状态混乱

如果误用 StatefulWidget 而没有状态管理,会导致不必要的复杂性.
你用了 StatelessWidget,页面简洁且符合需求.
这是"组件选择"的正确体现.

13.1 错误示例(StatefulWidget 滥用)

// 不推荐的写法(无状态却用 StatefulWidget)
class AchievementSystemPage extends StatefulWidget {
  const AchievementSystemPage({super.key});

  
  State<AchievementSystemPage> createState() => _AchievementSystemPageState();
}

class _AchievementSystemPageState extends State<AchievementSystemPage> {
  
  Widget build(BuildContext context) {
    // 无任何状态变量,纯静态展示
    return Scaffold(...);
  }
}
  • 无状态变量却使用 StatefulWidget,增加代码冗余
  • 状态类的创建和维护增加不必要的成本
  • 组件重建时无状态可更新,违背 StatefulWidget 的设计初衷

14. 常见踩坑点:ListView 缺失导致溢出

如果用 Column 替代 ListView,成就项过多时会溢出.
你用了 ListView,确保内容可滚动.
这是"布局安全"的体现.

14.1 错误示例(Column 导致溢出)

// 错误写法:Column 无滚动能力
body: Padding(
  padding: EdgeInsets.all(16.w),
  child: Column(
    children: [
      Text('成就徽章', style: TextStyle(fontSize: 20.sp)),
      // 多个成就项,超出屏幕高度时溢出
      _buildAchievement(...),
      _buildAchievement(...),
      // 更多成就项...
    ],
  ),
),
  • 成就项数量超过屏幕高度时,会触发 BottomOverflowed 异常
  • 无滚动能力,用户无法查看超出可视区域的成就项
  • 需手动添加 SingleChildScrollView 包裹,代码冗余

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

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐