Flutter三方库适配OpenHarmony【color_memory】颜色记忆游戏项目完整实战
Flutter三方库适配OpenHarmony【color_memory】颜色记忆游戏项目完整实战
前言
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
color_memory 是一个典型的记忆类小游戏项目。它内置 8 种颜色,系统每一关会向序列中追加一个随机颜色,并按顺序高亮展示给玩家;玩家需要在自己的回合按相同顺序点击颜色。如果玩家点击正确,就进入下一关并增加分数;如果点击错误,游戏结束并更新最高分。
这类小游戏很适合练习 Flutter 的 状态机设计、异步序列播放、GridView 网格布局、AnimatedContainer 动画反馈、GestureDetector 点击输入、分数统计和 OpenHarmony 触摸交互适配。虽然代码量不大,但它包含了一个完整游戏循环的关键结构。

图示说明:本文围绕 Flutter 工程中的 color_memory 项目展开,重点分析颜色序列生成、展示阶段、玩家输入校验、得分系统、游戏结束页面和 OpenHarmony 适配关注点。
记忆游戏的核心不是颜色方块本身,而是状态流转:系统展示序列、玩家复现序列、逐步校验输入、成功后进入下一关、失败后结束游戏。
本文将基于项目真实源码展开,核心内容包括:
ColorMemoryApp的应用入口与紫色 Material 3 主题_colors如何定义 8 色游戏面板_sequence与_userSequence如何保存系统序列和玩家输入_showSequence()如何异步播放高亮序列_onColorTap()如何逐步判断点击是否正确_level、_score、_highScore如何构成分数系统_isShowingSequence、_isUserTurn、_isGameOver如何组成状态机- 当前初始流程没有触发
_startGame()的真实边界 - OpenHarmony 适配时需要验证的网格、动画、触摸和图标能力
一、项目背景与目标
1.1 游戏定位
color_memory 的定位是一个颜色序列记忆游戏。它类似经典 Simon 记忆玩法:系统先展示一个颜色序列,玩家记住顺序后进行复现。每通过一关,序列长度增加 1,难度逐步提升。
从玩家视角看,完整游戏流程应该是:
- 开始游戏。
- 系统展示颜色序列。
- 玩家按顺序点击颜色。
- 点击正确则进入下一关。
- 点击错误则游戏结束。
- 玩家可以重新开始。
从工程视角看,它对应的是:
- 维护一个系统颜色序列。
- 每关追加一个随机颜色。
- 异步逐个高亮展示系统序列。
- 开放玩家输入。
- 每次点击后立即比较当前位置。
- 完整匹配后加分并升级。
- 匹配失败后更新游戏结束状态。
1.2 当前功能概览
| 功能 | 当前实现 | 技术点 |
|---|---|---|
| 应用入口 | runApp(const ColorMemoryApp()) |
Flutter 启动流程 |
| 应用主题 | 紫色 Material 3 | ColorScheme.fromSeed |
| 颜色面板 | 8 种颜色 | List<Color> |
| 系统序列 | _sequence |
随机颜色列表 |
| 玩家输入 | _userSequence |
点击颜色列表 |
| 随机追加 | math.Random() |
生成下一关颜色 |
| 序列展示 | _showSequence() |
异步高亮 |
| 输入校验 | _onColorTap() |
逐位比较 |
| 得分 | level * 10 |
关卡递增 |
| 最高分 | _highScore |
内存中保存 |
| 结束页 | _buildGameOverView() |
Game Over 状态 |
1.3 适合学习的能力
这个项目适合学习:
- 小游戏状态机
- 随机序列生成
- 异步流程控制
- GridView.builder 网格布局
- AnimatedContainer 颜色动画
- GestureDetector 点击输入
- 列表序列比较
- 游戏分数和最高分管理
二、环境准备与工程结构
2.1 技术栈概览
项目使用 Flutter SDK 自带能力,不依赖复杂游戏引擎。
| 类别 | 当前使用 | 说明 |
|---|---|---|
| 开发语言 | Dart | Flutter 应用主语言 |
| UI 框架 | Flutter Material | 页面、卡片、按钮、图标 |
| 随机数 | dart:math |
随机生成颜色 |
| 状态管理 | StatefulWidget + setState |
游戏状态刷新 |
| 网格布局 | GridView.builder |
颜色方块排列 |
| 动画组件 | AnimatedContainer |
展示阶段高亮过渡 |
| 点击输入 | GestureDetector |
玩家点击颜色 |
| 目标适配 | Flutter / OpenHarmony | UI、动画、触摸验证 |
2.2 pubspec 关键配置
工程配置如下:
environment:
sdk: ^3.9.2
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.8
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^5.0.0
flutter:
uses-material-design: true
这里需要关注:
- 当前项目没有接入音效、震动或游戏引擎。
- Material Icons 依赖
uses-material-design: true。 - 游戏主要依靠 Flutter Framework 组件实现。
2.3 主源码结构
核心代码集中在 lib/main.dart:
import 'package:flutter/material.dart';
import 'dart:math' as math;
void main() {
runApp(const ColorMemoryApp());
}
主要结构如下:
| 结构 | 类型 | 作用 |
|---|---|---|
ColorMemoryApp |
StatelessWidget |
应用根组件 |
ColorMemoryHomePage |
StatefulWidget |
游戏首页 |
_ColorMemoryHomePageState |
State |
管理序列、分数、状态 |
2.4 常用运行命令
完成 Flutter 环境准备后,可以执行:
flutter pub get
flutter analyze
flutter test
flutter run
OpenHarmony 环境运行时,需要结合本地 Flutter OpenHarmony 发行版、DevEco Studio、设备连接和签名配置。
三、应用入口与主题配置
3.1 main 函数
应用入口如下:
void main() {
runApp(const ColorMemoryApp());
}
runApp 会把根组件挂载到 Flutter 渲染树。
3.2 ColorMemoryApp 根组件
根组件代码如下:
class ColorMemoryApp extends StatelessWidget {
const ColorMemoryApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'Color Memory',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.purple),
useMaterial3: true,
),
home: const ColorMemoryHomePage(title: 'Color Memory'),
);
}
}
它完成了:
- 设置应用标题为
Color Memory。 - 使用紫色作为 Material 3 种子色。
- 将首页绑定为
ColorMemoryHomePage。
3.3 紫色主题的意义
紫色常用于娱乐、益智和游戏类界面。项目中紫色主要体现在:
- 根主题 seedColor。
- Play Again 按钮。
- AppBar 主题色。
- 游戏整体氛围。
四、颜色面板与序列数据
4.1 颜色列表
游戏内置 8 种颜色:
final List<Color> _colors = [
Colors.red,
Colors.blue,
Colors.green,
Colors.yellow,
Colors.purple,
Colors.orange,
Colors.pink,
Colors.cyan,
];
这些颜色用于生成系统序列,也用于构建玩家可点击的颜色网格。
4.2 序列状态
系统序列和玩家输入分别保存:
List<Color> _sequence = [];
List<Color> _userSequence = [];
字段含义如下:
| 字段 | 作用 |
|---|---|
_sequence |
系统生成的目标颜色序列 |
_userSequence |
玩家当前回合已点击的颜色序列 |
4.3 为什么分开保存
分开保存有两个好处:
- 系统序列保持不变,作为正确答案。
- 玩家输入逐步增长,每次点击都能即时比较。
点击第 1 个颜色时只比较第 1 位;点击第 2 个颜色时再比较第 2 位。这样错误可以被立即发现,不需要等玩家点完整个序列。
五、游戏状态机设计
5.1 状态字段
项目使用多个字段表示游戏状态:
int _level = 1;
int _score = 0;
int _highScore = 0;
bool _isShowingSequence = false;
bool _isGameOver = false;
bool _isUserTurn = false;
int _currentIndex = 0;
字段含义如下:
| 字段 | 作用 |
|---|---|
_level |
当前关卡 |
_score |
当前分数 |
_highScore |
最高分 |
_isShowingSequence |
是否正在展示系统序列 |
_isGameOver |
是否游戏结束 |
_isUserTurn |
是否轮到玩家输入 |
_currentIndex |
展示阶段当前高亮的序列位置 |
5.2 三种主状态
当前页面主要在三种状态之间切换:
| 状态 | 条件 | 页面 |
|---|---|---|
| 展示序列 | _isShowingSequence == true |
_buildSequenceView() |
| 玩家输入 | 非 game over 且非展示阶段 | _buildColorGrid() |
| 游戏结束 | _isGameOver == true |
_buildGameOverView() |
代码体现如下:
child: _isGameOver
? _buildGameOverView()
: _isShowingSequence
? _buildSequenceView()
: _buildColorGrid()
5.3 当前初始流程边界
当前源码中 _startGame() 没有在 initState() 中调用,也没有在初始页面提供 Start 按钮。初始状态为:
bool _isGameOver = false;
bool _isUserTurn = false;
bool _isShowingSequence = false;
因此首次打开页面时会显示颜色网格,但 _onColorTap() 会因为 _isUserTurn 为 false 直接返回:
if (!_isUserTurn || _isGameOver) return;
这意味着首次进入页面无法真正开始游戏。这是当前源码最重要的真实边界。
六、启动游戏与随机追加
6.1 _startGame 方法
启动游戏方法如下:
void _startGame() {
setState(() {
_sequence = [];
_userSequence = [];
_level = 1;
_score = 0;
_isGameOver = false;
});
_addToSequence();
}
它会清空序列、重置关卡和分数,然后追加第一个颜色。
6.2 _addToSequence 方法
追加颜色逻辑如下:
void _addToSequence() {
final random = math.Random();
_sequence.add(_colors[random.nextInt(_colors.length)]);
_showSequence();
}
它从 8 种颜色中随机选择一个追加到 _sequence,随后调用 _showSequence() 展示完整序列。
6.3 随机序列特点
当前随机逻辑允许连续出现相同颜色。例如序列可能是:
red, red, blue, green
这符合记忆游戏的常见规则,因为玩家不仅要记住颜色,还要记住每一步的位置。
6.4 启动流程修正思路
可以在 initState() 中启动游戏:
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
_startGame();
});
}
也可以增加一个初始 Start 按钮,由用户主动开始。两种方式都比初始网格不可点击更自然。
七、序列展示阶段
7.1 _showSequence 方法
展示序列的方法如下:
void _showSequence() async {
setState(() {
_isShowingSequence = true;
_isUserTurn = false;
});
await Future.delayed(const Duration(seconds: 1));
for (int i = 0; i < _sequence.length; i++) {
if (!mounted) return;
setState(() {
_currentIndex = i;
});
await Future.delayed(const Duration(milliseconds: 600));
}
if (!mounted) return;
setState(() {
_isShowingSequence = false;
_isUserTurn = true;
_userSequence = [];
_currentIndex = -1;
});
}
这段代码实现了:
- 进入展示阶段。
- 禁止用户输入。
- 等待 1 秒。
- 按顺序高亮每个颜色 600ms。
- 展示结束后进入用户回合。
- 清空玩家输入序列。
7.2 mounted 判断
异步过程中多次使用:
if (!mounted) return;
这是必要的保护。如果页面已经销毁,就不能再调用 setState。
7.3 展示阶段页面
展示阶段使用 _buildSequenceView():
Widget _buildSequenceView() {
return GridView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(16),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
),
itemCount: _colors.length,
itemBuilder: (context, index) {
final isHighlighted = _currentIndex >= 0 &&
index == _colors.indexOf(_sequence[_currentIndex]);
return AnimatedContainer(
duration: const Duration(milliseconds: 200),
decoration: BoxDecoration(
color: isHighlighted
? _colors[index]
: _colors[index].withValues(alpha: 0.3),
borderRadius: BorderRadius.circular(12),
),
);
},
);
}
7.4 AnimatedContainer 的作用
AnimatedContainer 会在颜色变化时自动过渡:
AnimatedContainer(
duration: const Duration(milliseconds: 200),
)
展示阶段未高亮颜色使用透明度降低的版本,高亮颜色使用原色。
八、玩家输入与逐步校验
8.1 玩家点击入口
玩家点击颜色时调用:
onTap: () => _onColorTap(_colors[index])
每个颜色方块都由 GestureDetector 包裹。
8.2 输入前置判断
点击处理方法开头是:
if (!_isUserTurn || _isGameOver) return;
这确保:
- 展示序列时不能点击。
- 游戏结束后不能继续输入。
- 只有玩家回合才处理点击。
8.3 添加玩家输入
玩家点击后,颜色会被加入 _userSequence:
setState(() {
_userSequence.add(color);
});
随后计算当前输入位置:
final currentIndex = _userSequence.length - 1;
8.4 即时比较
每次点击后立即比较当前位置:
if (_userSequence[currentIndex] != _sequence[currentIndex]) {
_endGame();
return;
}
如果不匹配,游戏立即结束。
8.5 完整匹配后升级
当玩家输入长度等于系统序列长度时,说明本关通过:
if (_userSequence.length == _sequence.length) {
_score += _level * 10;
setState(() {
_level++;
_userSequence = [];
});
Future.delayed(const Duration(seconds: 1), () {
_addToSequence();
});
}
通过后会加分、关卡加 1,并在 1 秒后追加新颜色进入下一关。
九、颜色网格与点击区域
9.1 玩家回合网格
玩家回合显示 _buildColorGrid():
Widget _buildColorGrid() {
return GridView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(16),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
),
itemCount: _colors.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () => _onColorTap(_colors[index]),
child: Container(
decoration: BoxDecoration(
color: _colors[index],
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: _colors[index].withValues(alpha: 0.5),
blurRadius: 8,
offset: const Offset(2, 2),
),
],
),
),
);
},
);
}
9.2 4 列布局
网格设置为 4 列:
crossAxisCount: 4
8 种颜色会排列成 2 行 4 列,适合移动端屏幕展示。
9.3 方块视觉设计
每个方块包含:
- 原始颜色。
- 12 像素圆角。
- 与自身颜色一致的阴影。
- 8 像素横纵间距。
这让颜色按钮更有游戏感,也便于用户点击。
9.4 当前点击反馈边界
玩家点击颜色后,当前源码没有额外闪烁、音效或震动反馈。玩家只能通过后续状态变化判断是否正确。
如果要增强体验,可以在点击时增加短暂缩放、边框高亮、音效或震动。
十、分数与最高分
10.1 分数字段
分数相关字段如下:
int _level = 1;
int _score = 0;
int _highScore = 0;
顶部统计卡片展示 Level、Score 和 Best。
10.2 得分公式
每通过一关增加:
_score += _level * 10;
示例:
| 通过关卡 | 本关加分 |
|---|---|
| 1 | 10 |
| 2 | 20 |
| 3 | 30 |
| 4 | 40 |
10.3 最高分更新
游戏结束时更新最高分:
if (_score > _highScore) {
_highScore = _score;
}
最高分只保存在内存中,应用重启后会恢复为 0。
10.4 统计卡片
统计卡片由 _buildStatCard() 复用:
Widget _buildStatCard(String label, String value) {
return Card(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
child: Column(
children: [
Text(label, style: TextStyle(color: Colors.grey.shade600)),
const SizedBox(height: 4),
Text(
value,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
],
),
),
);
}
十一、游戏结束页面
11.1 _endGame 方法
游戏结束方法如下:
void _endGame() {
setState(() {
_isGameOver = true;
_isUserTurn = false;
if (_score > _highScore) {
_highScore = _score;
}
});
}
它会进入 Game Over 状态,并关闭用户输入。
11.2 Game Over 视图
结束页面如下:
Widget _buildGameOverView() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.sentiment_dissatisfied, size: 80, color: Colors.red),
const SizedBox(height: 16),
const Text(
'Game Over!',
style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
Text(
'You reached level $_level',
style: const TextStyle(fontSize: 18),
),
Text(
'Score: $_score',
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
],
);
}
11.3 Play Again 按钮
游戏结束后底部显示:
ElevatedButton.icon(
onPressed: _startGame,
icon: const Icon(Icons.refresh),
label: const Text('Play Again'),
)
点击后会重新开始游戏。
11.4 当前初始按钮缺失
Play Again 只在 _isGameOver 为 true 时显示。首次进入页面时 _isGameOver 为 false,因此不会显示这个按钮。
这也说明初始状态需要额外的开始入口,否则玩家第一次进入时无法触发 _startGame()。
十二、OpenHarmony 适配要点
12.1 适配关注范围
color_memory 没有平台插件,适配重点在 Flutter UI、触摸、动画和异步流程。
| 适配项 | 涉及源码 | 验证重点 |
|---|---|---|
| MaterialApp | 根组件 | 应用启动和主题 |
| AppBar | 标题栏 | 颜色和文字 |
| Card | 统计卡片 | 布局、阴影 |
| GridView | 颜色网格 | 4 列布局 |
| AnimatedContainer | 高亮动画 | 颜色过渡 |
| GestureDetector | 玩家点击 | 触摸响应 |
| Future.delayed | 序列播放 | 异步节奏 |
| ElevatedButton | Play Again | 按钮显示和点击 |
| Material Icons | 刷新和失败图标 | 字体资源 |
12.2 图标资源验证
项目使用了:
Icons.refresh
Icons.sentiment_dissatisfied
OpenHarmony 设备上需要验证:
- 图标是否可见。
- 图标大小是否符合预期。
- 图标颜色是否正确。
- Material Icons 字体是否随包体加载。
12.3 动画验证
展示阶段依赖 AnimatedContainer:
AnimatedContainer(
duration: const Duration(milliseconds: 200),
)
需要观察:
- 颜色是否逐个高亮。
- 未高亮颜色是否变浅。
- 高亮过渡是否自然。
- 长序列播放时是否卡顿。
12.4 触摸验证
玩家输入依赖 GestureDetector:
GestureDetector(
onTap: () => _onColorTap(_colors[index]),
)
需要验证:
- 点击区域是否足够大。
- 快速点击时状态是否稳定。
- 展示阶段点击是否被忽略。
- 游戏结束后点击是否被忽略。
十三、测试与验证
13.1 静态分析
建议执行:
flutter analyze
重点关注:
- 初始状态是否能进入游戏流程。
- 异步回调中是否检查
mounted。 Future.delayed后是否可能在已结束游戏状态下继续追加序列。- 高分是否只存在内存中。
13.2 组件测试方向
可以执行:
flutter test
适合覆盖的行为包括:
- 页面显示 Level、Score、Best。
- 初始状态是否有开始入口。
- 启动游戏后进入展示阶段。
- 展示结束后进入玩家回合。
- 错误点击进入 Game Over。
- Game Over 后显示 Play Again。
13.3 示例测试代码
下面是一段验证初始标题和统计卡片的测试思路:
testWidgets('shows color memory dashboard', (tester) async {
await tester.pumpWidget(const ColorMemoryApp());
expect(find.text('Color Memory'), findsOneWidget);
expect(find.text('Level'), findsOneWidget);
expect(find.text('Score'), findsOneWidget);
expect(find.text('Best'), findsOneWidget);
});
这能确认页面基础结构正常。
13.4 启动流程测试思路
如果添加 Start 按钮,可以验证:
testWidgets('starts game from initial state', (tester) async {
await tester.pumpWidget(const ColorMemoryApp());
await tester.tap(find.text('Start Game'));
await tester.pump();
expect(find.text('Watch the sequence...'), findsOneWidget);
});
这类测试能避免初始页面不可交互的问题再次出现。
13.5 手动验证流程
手动验证可以按如下顺序进行:
- 启动应用,确认是否能触发游戏开始。
- 观察系统颜色序列是否逐个高亮。
- 展示阶段尝试点击颜色,确认不会误记录。
- 展示结束后按顺序点击颜色。
- 正确复现后确认 Level 和 Score 增加。
- 故意点错颜色,确认进入 Game Over。
- 点击 Play Again,确认游戏重置并重新开始。
- 连续玩多轮,确认 Best 会更新。
十四、常见问题与优化建议
14.1 为什么首次进入页面点击颜色没有反应
因为初始状态下 _isUserTurn 为 false,而 _startGame() 没有被触发:
if (!_isUserTurn || _isGameOver) return;
因此首次点击会被直接忽略。可以在初始页面增加 Start 按钮,或在页面加载后自动调用 _startGame()。
14.2 为什么最高分重启后会清零
因为 _highScore 是内存状态:
int _highScore = 0;
应用重启后 State 会重新创建,最高分恢复为 0。要长期保存,需要接入本地存储。
14.3 为什么没有音效或震动反馈
当前源码只实现颜色高亮和页面状态切换,没有音效、震动或触觉反馈。记忆游戏如果增加音效,会更接近经典玩法,但也需要额外适配平台能力。
14.4 随机颜色可以连续重复吗
可以。当前随机逻辑没有排除上一次颜色:
_colors[random.nextInt(_colors.length)]
连续重复颜色是合法序列,玩家需要记住每一步位置。
14.5 是否需要游戏引擎
当前项目是轻量 UI 游戏,不需要 Flame 或其他游戏引擎。使用 Flutter 组件就能完成网格、动画和点击输入。如果后续加入复杂音效、粒子、排行榜和关卡系统,再考虑更专业的游戏架构。
十五、工程扩展方向
15.1 增加 Start Game 初始入口
可以增加一个初始状态:
bool _hasStarted = false;
首次页面显示 Start 按钮:
if (!_hasStarted)
ElevatedButton(
onPressed: () {
setState(() {
_hasStarted = true;
});
_startGame();
},
child: const Text('Start Game'),
)
这样流程更完整。
15.2 持久化最高分
可以把最高分保存到本地:
Future<void> saveHighScore(int score) async {
// save score locally
}
读取时在页面初始化阶段恢复:
Future<void> loadHighScore() async {
// load score locally
}
这样玩家重启应用后仍能看到历史最高分。
15.3 增加点击反馈
可以在玩家点击时增加视觉反馈:
setState(() {
_currentIndex = _colors.indexOf(color);
});
再短暂延迟后恢复。也可以加入缩放动画或边框高亮。
15.4 增加音效和震动
记忆游戏常见扩展包括:
- 每种颜色对应不同音效。
- 点击正确时轻微震动。
- 点击错误时错误音效。
- 通关时播放成功反馈。
这些能力需要额外平台适配。
15.5 增加难度设置
可以让玩家选择展示速度:
final Duration _showDuration = const Duration(milliseconds: 600);
后续可以设置为 Easy、Normal、Hard:
| 难度 | 展示时长 |
|---|---|
| Easy | 800ms |
| Normal | 600ms |
| Hard | 400ms |
十六、完整核心代码回顾
16.1 应用入口
void main() {
runApp(const ColorMemoryApp());
}
入口负责启动根组件。
16.2 根组件
class ColorMemoryApp extends StatelessWidget {
const ColorMemoryApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'Color Memory',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.purple),
useMaterial3: true,
),
home: const ColorMemoryHomePage(title: 'Color Memory'),
);
}
}
根组件负责主题和首页绑定。
16.3 随机追加颜色
void _addToSequence() {
final random = math.Random();
_sequence.add(_colors[random.nextInt(_colors.length)]);
_showSequence();
}
每一关都会追加一个随机颜色。
16.4 展示序列
for (int i = 0; i < _sequence.length; i++) {
if (!mounted) return;
setState(() {
_currentIndex = i;
});
await Future.delayed(const Duration(milliseconds: 600));
}
这段代码按顺序高亮系统序列。
16.5 校验点击
if (_userSequence[currentIndex] != _sequence[currentIndex]) {
_endGame();
return;
}
每次点击都会立即比较当前位置。
16.6 游戏结束
void _endGame() {
setState(() {
_isGameOver = true;
_isUserTurn = false;
if (_score > _highScore) {
_highScore = _score;
}
});
}
游戏结束时会更新最高分并关闭玩家输入。
总结
color_memory 用 Flutter 组件实现了一个完整的颜色记忆游戏核心:它通过 _colors 提供 8 色面板,通过 _sequence 保存系统序列,通过 _userSequence 保存玩家输入,通过 _showSequence() 异步展示高亮,通过 _onColorTap() 逐位校验点击,并通过 _level、_score、_highScore 构建关卡和分数系统。
从 OpenHarmony 适配角度看,这个项目覆盖了 Material 主题、统计卡片、GridView 网格、AnimatedContainer 动画、GestureDetector 触摸、Future.delayed 异步流程和 Material Icons 等基础能力,很适合验证 Flutter 小游戏页面在 OpenHarmony 上的表现。
当前源码也有几个真实边界:首次进入页面没有触发 _startGame(),导致初始颜色网格不可交互;最高分只保存在内存中;玩家点击没有额外音效或震动;随机序列没有难度设置。这些边界不影响它作为状态机和小游戏入门案例使用,但在继续工程化时需要优先完善启动入口。
如果这篇文章对你有帮助,欢迎点赞、收藏、关注,你的支持是我持续创作的动力!
相关资源:
- OpenHarmony 官网:https://www.openharmony.cn
- OpenHarmony 文档:https://docs.openharmony.cn
- 开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
- Flutter 官网:https://flutter.dev
更多推荐



所有评论(0)