Flutter三方库适配OpenHarmony【word_scramble】单词乱序游戏项目完整实战
Flutter三方库适配OpenHarmony【word_scramble】单词乱序游戏项目完整实战
前言
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
word_scramble 是一个文字益智类 Flutter 小游戏。它内置 25 个开发相关英文单词,每一轮随机选择一个目标词,通过字母洗牌生成乱序单词,玩家在输入框中填写答案并点击 Check 校验。答对后获得基础分和连击奖励,答错后会弹出提示弹窗,玩家也可以使用 Hint 或 Skip 辅助继续游戏。
这类小游戏非常适合练习 Flutter 的 随机选词、洗牌算法、输入框控制、弹窗反馈、状态统计、Wrap 字母布局、按钮交互和 OpenHarmony 输入法适配。它没有复杂图形引擎,也不依赖网络,非常适合用来拆解一套完整的轻量游戏闭环。

图示说明:本文围绕 Flutter 工程中的 word_scramble 项目展开,重点分析词库随机、字母洗牌、答案校验、连击奖励、提示机制、弹窗反馈和 OpenHarmony 适配关注点。
单词乱序游戏的关键不是把字母打乱这么简单,而是随机性、可验证性、输入体验和奖励反馈能否组成一个顺畅的游戏循环。
本文将基于项目真实源码展开,核心内容包括:
WordScrambleApp的应用入口与橙色 Material 3 主题_words词库如何组织 25 个开发相关单词_loadNewWord()如何随机选择目标词并生成乱序词_scrambleWord()如何实现 Fisher-Yates 风格洗牌TextEditingController如何管理玩家输入_checkAnswer()如何判断正确、错误与连击奖励_showSuccessDialog()与_showWrongDialog()如何提供反馈_showHint()当前只是高亮位置,不是真正揭示正确字符_loadNewWord()在部分路径中没有触发setState的真实边界
一、项目背景与目标
1.1 游戏定位
word_scramble 的定位是一个单词还原游戏。系统展示被打乱的字母,玩家需要还原成正确英文单词。它适合用来练习英文词汇,也适合用来学习 Flutter 表单输入和弹窗反馈。
从玩家视角看,流程是:
- 看到一个乱序单词。
- 在输入框中填写答案。
- 点击
Check或回车提交。 - 答对后进入下一词。
- 答错后可以继续尝试或查看提示。
- 不想继续当前词时可以点击
Skip。
从工程视角看,流程是:
- 从词库中随机选择目标词。
- 将目标词拆分成字符列表。
- 打乱字符顺序并展示。
- 获取输入框内容。
- 统一转小写并去掉首尾空格。
- 与目标词比较。
- 根据结果更新分数、连击和弹窗状态。
1.2 当前功能概览
| 功能 | 当前实现 | 技术点 |
|---|---|---|
| 应用入口 | runApp(const WordScrambleApp()) |
Flutter 启动流程 |
| 应用主题 | 橙色 Material 3 | ColorScheme.fromSeed |
| 内置词库 | 25 个英文单词 | List<String> |
| 随机选词 | math.Random() |
从词库取目标词 |
| 字母打乱 | Fisher-Yates 风格洗牌 | _scrambleWord() |
| 输入控制 | TextEditingController |
获取答案 |
| 正确反馈 | AlertDialog |
Correct 弹窗 |
| 错误反馈 | AlertDialog |
Try Again 弹窗 |
| 提示 | 高亮乱序位置 | _showHint() |
| 跳过 | 重置连击并换词 | _skipWord() |
| 得分 | 10 分基础分 + 连击奖励 | _score、_streak |
1.3 适合学习的能力
这个项目适合学习:
- 词库型小游戏设计
- 随机数与洗牌算法
- 递归避免原词不变
- TextEditingController 生命周期
- onSubmitted 与按钮提交
- AlertDialog 游戏反馈
- Wrap 自适应字母布局
- OpenHarmony 下输入框和键盘验证
二、环境准备与工程结构
2.1 技术栈概览
项目只使用 Flutter SDK 和 Dart 标准库能力。
| 类别 | 当前使用 | 说明 |
|---|---|---|
| 开发语言 | Dart | Flutter 应用主语言 |
| UI 框架 | Flutter Material | 页面、按钮、弹窗、输入框 |
| 随机数 | dart:math |
随机选词和洗牌 |
| 状态管理 | StatefulWidget + setState |
游戏状态刷新 |
| 输入控制 | TextEditingController |
管理答案输入 |
| 字母布局 | Wrap + Container |
自适应字母块 |
| 弹窗反馈 | showDialog + AlertDialog |
正确和错误提示 |
| 目标适配 | 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
当前项目没有网络、存储、数据库或三方游戏引擎依赖,适合聚焦 Flutter 基础 UI 和交互逻辑。
2.3 主源码结构
核心代码集中在 lib/main.dart:
import 'package:flutter/material.dart';
import 'dart:math' as math;
void main() {
runApp(const WordScrambleApp());
}
主要结构如下:
| 结构 | 类型 | 作用 |
|---|---|---|
WordScrambleApp |
StatelessWidget |
应用根组件 |
WordScrambleHomePage |
StatefulWidget |
游戏首页 |
_WordScrambleHomePageState |
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 WordScrambleApp());
}
runApp 会挂载根组件。
3.2 WordScrambleApp 根组件
根组件代码如下:
class WordScrambleApp extends StatelessWidget {
const WordScrambleApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'Word Scramble',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.orange),
useMaterial3: true,
),
home: const WordScrambleHomePage(title: 'Word Scramble'),
);
}
}
它完成了:
- 设置应用标题为
Word Scramble。 - 使用橙色作为 Material 3 种子色。
- 将首页设置为
WordScrambleHomePage。
3.3 橙色主题的意义
橙色常用于轻松、活跃、益智类场景。项目中橙色主要用于:
- 应用主题 seedColor。
- 连击奖励文字。
- Hint 高亮字母块。
- 游戏整体视觉氛围。
四、词库设计
4.1 内置词库
项目内置了 25 个英文单词:
final List<String> _words = [
'flutter', 'dart', 'mobile', 'android', 'coding',
'programming', 'developer', 'application', 'widget', 'screen',
'button', 'text', 'image', 'container', 'column',
'row', 'list', 'grid', 'stack', 'navigation',
'api', 'database', 'server', 'client', 'network',
];
这些词大多和 Flutter、开发、应用界面相关。
4.2 词库特点
| 特点 | 说明 |
|---|---|
| 数量 | 25 个 |
| 语言 | 英文 |
| 主题 | Flutter 与软件开发 |
| 来源 | 硬编码在源码中 |
| 难度 | 从 api 到 application 不等 |
4.3 本地词库的优点
本地词库适合当前项目:
- 不需要网络。
- 启动即可出题。
- 逻辑容易调试。
- 适合教学演示。
- OpenHarmony 适配时能聚焦 UI 和输入体验。
4.4 本地词库的边界
当前词库也有几个边界:
- 单词数量固定。
- 没有难度分类。
- 没有去重答题记录。
- 可能连续随机到同一个单词。
- 不支持动态扩展词库。
五、随机选词与加载新题
5.1 _loadNewWord 方法
加载新单词的方法如下:
void _loadNewWord() {
final random = math.Random();
_currentWord = _words[random.nextInt(_words.length)];
_scrambledWord = _scrambleWord(_currentWord);
_controller.clear();
_hintIndex = 0;
_hintPositions = [];
}
它完成了:
- 创建随机数对象。
- 从
_words中随机取一个目标单词。 - 调用
_scrambleWord()生成乱序单词。 - 清空输入框。
- 重置提示索引。
- 清空提示位置列表。
5.2 initState 中初始化
页面初始化时会加载第一题:
void initState() {
super.initState();
_loadNewWord();
}
这条路径没有问题,因为首次 build 前已经准备好了 _currentWord 和 _scrambledWord。
5.3 新题刷新边界
_loadNewWord() 自身没有调用 setState。如果它在没有其他 setState 的路径中被调用,UI 可能不会立刻刷新。
例如正确答题弹窗中:
onPressed: () {
Navigator.pop(context);
_loadNewWord();
}
更稳妥的写法是:
onPressed: () {
Navigator.pop(context);
setState(() {
_loadNewWord();
});
}
或者让 _loadNewWord() 内部负责 setState。当前源码中,这属于真实工程边界。
六、字母洗牌算法
6.1 _scrambleWord 方法
打乱单词的方法如下:
String _scrambleWord(String word) {
final letters = word.split('');
final random = math.Random();
for (int i = letters.length - 1; i > 0; i--) {
final j = random.nextInt(i + 1);
final temp = letters[i];
letters[i] = letters[j];
letters[j] = temp;
}
final scrambled = letters.join();
if (scrambled == word && word.length > 1) {
return _scrambleWord(word);
}
return scrambled;
}
这是一种 Fisher-Yates 风格的洗牌过程。
6.2 洗牌流程
核心循环如下:
for (int i = letters.length - 1; i > 0; i--) {
final j = random.nextInt(i + 1);
final temp = letters[i];
letters[i] = letters[j];
letters[j] = temp;
}
流程是:
- 从最后一个字符开始。
- 在
[0, i]范围内随机选一个位置。 - 交换两个字符。
- 继续向前处理。
6.3 避免原词不变
如果洗牌结果和原词一样,会递归重新洗牌:
if (scrambled == word && word.length > 1) {
return _scrambleWord(word);
}
这样能避免玩家看到未打乱的原词。
6.4 重复字符的边界
对于包含重复字符的单词,洗牌后可能在视觉上仍接近原词。当前递归只判断完整字符串是否完全相同,不判断难度是否足够。
例如某些重复字母较多的词,可能需要额外规则保证打乱效果更明显。
七、输入框与答案提交
7.1 TextEditingController
输入框使用控制器:
final TextEditingController _controller = TextEditingController();
它用于读取玩家输入,也用于加载新词时清空输入框。
7.2 dispose 释放资源
控制器在 dispose() 中释放:
void dispose() {
_controller.dispose();
super.dispose();
}
这是使用 TextEditingController 的正确做法。
7.3 TextField 配置
输入框代码如下:
TextField(
controller: _controller,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 24),
decoration: InputDecoration(
hintText: 'Type your answer...',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
),
onSubmitted: (_) => _checkAnswer(),
)
关键点:
| 配置 | 作用 |
|---|---|
controller |
获取输入内容 |
textAlign: center |
输入文字居中 |
fontSize: 24 |
提升可读性 |
hintText |
给出输入提示 |
onSubmitted |
回车提交答案 |
7.4 输入规范化
检查答案时会先处理输入:
final answer = _controller.text.toLowerCase().trim();
这样可以忽略大小写和首尾空格。
八、答案检查与得分系统
8.1 _checkAnswer 方法
答案检查逻辑如下:
void _checkAnswer() {
final answer = _controller.text.toLowerCase().trim();
if (answer == _currentWord) {
final bonus = _streak * 5;
setState(() {
_score += 10 + bonus;
_streak++;
});
_showSuccessDialog(bonus);
} else {
setState(() {
_streak = 0;
});
_showWrongDialog();
}
}
8.2 正确答案路径
答对时会:
- 根据当前 streak 计算 bonus。
- 分数增加
10 + bonus。 - streak 加 1。
- 弹出 Correct 对话框。
8.3 错误答案路径
答错时会:
- 将 streak 清零。
- 弹出 Try Again 对话框。
- 保留当前单词,允许继续尝试。
8.4 连击奖励计算
连击奖励计算如下:
final bonus = _streak * 5;
示例:
| 答题前 Streak | 基础分 | Bonus | 本题得分 |
|---|---|---|---|
| 0 | 10 | 0 | 10 |
| 1 | 10 | 5 | 15 |
| 2 | 10 | 10 | 20 |
| 3 | 10 | 15 | 25 |
这能鼓励玩家连续答对。
九、弹窗反馈设计
9.1 正确弹窗
正确弹窗如下:
void _showSuccessDialog(int bonus) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Row(
children: [
const Icon(Icons.check_circle, color: Colors.green),
const SizedBox(width: 8),
const Text('Correct!'),
],
),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('The word was: $_currentWord'),
if (bonus > 0)
Text('Streak bonus: +$bonus'),
],
),
),
);
}
它展示正确答案和连击奖励。
9.2 Next Word 按钮
正确弹窗中的按钮是:
ElevatedButton(
onPressed: () {
Navigator.pop(context);
_loadNewWord();
},
child: const Text('Next Word'),
)
这里如前面所说,建议把 _loadNewWord() 放进 setState 中,保证新题 UI 刷新。
9.3 错误弹窗
错误弹窗如下:
void _showWrongDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Row(
children: [
const Icon(Icons.cancel, color: Colors.red),
const SizedBox(width: 8),
const Text('Try Again!'),
],
),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text('That\\'s not the word. Keep trying!'),
TextButton(
onPressed: () {
Navigator.pop(context);
_showHint();
},
child: const Text('Need a hint?'),
),
],
),
),
);
}
9.4 错误后继续尝试
错误弹窗不会换词,玩家关闭弹窗后可以继续输入。这个设计对文字游戏比较友好,因为玩家可以多次尝试。
十、提示机制
10.1 提示状态
提示相关字段如下:
int _hintIndex = 0;
List<int> _hintPositions = [];
_hintIndex 表示下一次提示的位置,_hintPositions 保存已经提示过的位置。
10.2 _showHint 方法
提示方法如下:
void _showHint() {
if (_hintIndex < _currentWord.length) {
setState(() {
_hintPositions.add(_hintIndex);
_hintIndex++;
});
}
}
每次调用都会把一个位置加入提示列表。
10.3 字母块高亮
字母块根据位置判断是否高亮:
final isHinted = _hintPositions.contains(index);
如果该位置被提示,就使用橙色样式:
color: isHinted ? Colors.orange.shade100 : Colors.grey.shade100
10.4 当前提示的真实边界
当前提示高亮的是乱序字母位置,不是正确单词中的答案位置,也没有显示正确字符。因此它更像“位置高亮”而不是“揭示答案字母”。
如果要做成真正提示,可以展示目标词的某个字符,例如:
final revealedChar = _currentWord[_hintIndex];
或者在输入框下方显示已揭示的正确字母。
十一、字母块布局
11.1 Wrap 自适应布局
乱序字母使用 Wrap 展示:
Wrap(
spacing: 8,
children: List.generate(_scrambledWord.length, (index) {
final char = _scrambledWord[index];
final isHinted = _hintPositions.contains(index);
return Container(...);
}),
)
Wrap 相比 Row 更适合不同长度单词,因为空间不足时会自动换行。
11.2 字母块样式
每个字母块使用 Container:
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: isHinted ? Colors.orange.shade100 : Colors.grey.shade100,
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: isHinted ? Colors.orange : Colors.grey.shade300,
),
),
child: Text(
char.toUpperCase(),
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
color: isHinted ? Colors.orange : Colors.black,
),
),
)
11.3 大写展示
字母显示时使用:
char.toUpperCase()
这能让字母块更醒目,但答案校验仍然使用小写目标词。
11.4 长单词适配
词库里有 programming、application、navigation 这类较长单词。Wrap 能自动换行,但在小屏幕上仍需要关注字母块是否过大。
如果字母数量继续增加,可以减小 padding 或字号。
十二、Skip 与游戏节奏
12.1 _skipWord 方法
跳过逻辑如下:
void _skipWord() {
setState(() {
_streak = 0;
});
_loadNewWord();
}
它会清空连击,然后加载新单词。
12.2 为什么跳过会清空连击
连击奖励代表连续答对。如果玩家跳过当前单词,就不应该继续保留 streak,因此 _streak = 0 是合理的。
12.3 Skip 路径的 UI 更新
虽然 _loadNewWord() 自身不调用 setState,但 _skipWord() 中先执行了 setState,随后同步修改 _currentWord 和 _scrambledWord。由于本轮状态已经被标记为需要重建,新单词通常会在这次 rebuild 中显示出来。
不过从代码清晰度看,把加载新词统一放入 setState 仍然更稳。
12.4 不扣分的边界
当前 Skip 只清空 streak,不扣分。这让玩家可以跳过难词而不损失现有分数。如果希望增加挑战,可以让 Skip 扣分或限制次数。
十三、OpenHarmony 适配要点
13.1 适配关注范围
word_scramble 没有平台插件,适配重点在 Flutter 输入、弹窗和布局。
| 适配项 | 涉及源码 | 验证重点 |
|---|---|---|
| MaterialApp | 根组件 | 应用启动和主题 |
| TextField | 答案输入 | 输入法、回车提交 |
| TextEditingController | 输入控制 | 清空和释放 |
| Wrap | 字母块布局 | 长单词换行 |
| AlertDialog | 正确/错误反馈 | 弹窗显示和关闭 |
| ElevatedButton | Check | 点击与样式 |
| TextButton | Hint / Skip | 交互状态 |
| Material Icons | 多个图标 | 字体资源 |
13.2 图标资源验证
项目使用了多个图标:
Icons.check_circle
Icons.cancel
Icons.lightbulb
Icons.check
Icons.skip_next
OpenHarmony 设备上需要确认:
- 图标是否可见。
- 图标颜色是否正确。
- 图标与文字是否对齐。
- Material Icons 字体是否随包体加载。
13.3 输入法验证
输入框是游戏核心:
TextField(
controller: _controller,
onSubmitted: (_) => _checkAnswer(),
)
适配时需要验证:
- 点击输入框是否弹出键盘。
- 英文字母输入是否正常。
- 回车是否触发提交。
- 弹窗出现后键盘是否收起或被正确遮挡。
- 新词加载后输入框是否清空。
13.4 弹窗验证
正确和错误反馈都依赖 AlertDialog:
showDialog(
context: context,
builder: (context) => AlertDialog(...),
)
需要验证:
- Correct 弹窗是否显示。
- Try Again 弹窗是否显示。
- Next Word 是否关闭弹窗。
- Need a hint 是否关闭弹窗并触发提示。
- 小屏幕上弹窗内容是否溢出。
十四、测试与验证
14.1 静态分析
建议执行:
flutter analyze
重点关注:
TextEditingController是否释放。_loadNewWord()的刷新路径。- 递归洗牌是否存在极端情况。
- 弹窗回调中的状态更新是否清晰。
14.2 组件测试方向
可以执行:
flutter test
适合覆盖的行为包括:
- 页面显示 Score 和 Streak。
- 初始显示乱序字母块。
- 输入正确答案后分数增加。
- 输入错误答案后 streak 清零。
- 点击 Hint 后有字母块变成提示样式。
- 点击 Skip 后 streak 清零并换词。
14.3 示例测试代码
下面是一段基础页面测试思路:
testWidgets('shows word scramble dashboard', (tester) async {
await tester.pumpWidget(const WordScrambleApp());
expect(find.text('Word Scramble'), findsOneWidget);
expect(find.text('Score'), findsOneWidget);
expect(find.text('Streak'), findsOneWidget);
expect(find.text('Unscramble the word:'), findsOneWidget);
});
这能确认页面基础结构正常。
14.4 输入提交测试思路
如果测试中能读取当前目标词,可以验证:
testWidgets('submits answer from text field', (tester) async {
await tester.pumpWidget(const WordScrambleApp());
await tester.enterText(find.byType(TextField), 'flutter');
await tester.testTextInput.receiveAction(TextInputAction.done);
await tester.pumpAndSettle();
// assert dialog or score according to current word
});
由于当前目标词随机,稳定测试通常需要把选词逻辑抽出来,或注入固定词库。
14.5 手动验证流程
手动验证可以按如下顺序进行:
- 启动应用,确认显示乱序字母。
- 在输入框中输入错误答案,确认弹出 Try Again。
- 点击 Need a hint,确认字母块提示样式变化。
- 输入正确答案,确认弹出 Correct。
- 点击 Next Word,确认进入下一题。
- 连续答对,确认 Streak 和 bonus 增加。
- 点击 Skip,确认 Streak 清零并换词。
- 在 OpenHarmony 设备上验证输入法和弹窗表现。
十五、常见问题与优化建议
15.1 为什么正确后新词可能不刷新
因为正确弹窗的 Next Word 回调直接调用 _loadNewWord(),但没有包在 setState 中:
Navigator.pop(context);
_loadNewWord();
更稳妥的写法是:
Navigator.pop(context);
setState(() {
_loadNewWord();
});
这样新词、乱序字母和输入框状态会稳定刷新。
15.2 Hint 为什么没有显示正确字母
当前 Hint 只是把 _hintIndex 加入 _hintPositions:
_hintPositions.add(_hintIndex);
UI 只是高亮乱序字母对应位置,并没有展示目标词中的正确字符。要做真正提示,需要基于 _currentWord 展示正确字母。
15.3 为什么可能连续出现同一个单词
因为每轮都是直接随机:
_words[random.nextInt(_words.length)]
没有记录上一题或已出题集合。因此可能连续抽到同一个单词。
15.4 为什么 Skip 不扣分
当前 _skipWord() 只清空连击:
_streak = 0;
它不修改 _score。这是一种偏休闲的设计。如果希望更有挑战性,可以给 Skip 增加扣分或次数限制。
15.5 是否需要持久化分数
当前 Score 和 Streak 都是内存状态,应用重启后会清零。如果想做长期排行榜,需要本地存储或远程账号能力。
十六、工程扩展方向
16.1 抽离词库模型
可以设计单词模型:
class WordItem {
const WordItem({
required this.word,
required this.category,
required this.difficulty,
});
final String word;
final String category;
final int difficulty;
}
这样可以支持分类和难度。
16.2 稳定测试随机逻辑
为了让测试更稳定,可以把随机选词抽象出来:
String pickWord(List<String> words, math.Random random) {
return words[random.nextInt(words.length)];
}
测试时传入固定随机对象或固定返回值。
16.3 改造真正提示
可以展示正确单词前几个字符:
String buildHint(String word, int count) {
return word.substring(0, count).padRight(word.length, '_');
}
例如 flutter 的前两个提示可以显示:
fl_____
16.4 增加倒计时
可以为每题增加时间限制:
int _remainingSeconds = 30;
时间结束后自动判错或跳过。
16.5 增加难度分层
可以按单词长度划分难度:
| 难度 | 单词长度 |
|---|---|
| Easy | 3 到 5 |
| Normal | 6 到 8 |
| Hard | 9 及以上 |
这样游戏节奏会更清晰。
十七、完整核心代码回顾
17.1 应用入口
void main() {
runApp(const WordScrambleApp());
}
入口负责启动根组件。
17.2 根组件
class WordScrambleApp extends StatelessWidget {
const WordScrambleApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'Word Scramble',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.orange),
useMaterial3: true,
),
home: const WordScrambleHomePage(title: 'Word Scramble'),
);
}
}
根组件负责主题和首页绑定。
17.3 加载新词
void _loadNewWord() {
final random = math.Random();
_currentWord = _words[random.nextInt(_words.length)];
_scrambledWord = _scrambleWord(_currentWord);
_controller.clear();
_hintIndex = 0;
_hintPositions = [];
}
它负责选词、打乱、清空输入和重置提示。
17.4 检查答案
void _checkAnswer() {
final answer = _controller.text.toLowerCase().trim();
if (answer == _currentWord) {
final bonus = _streak * 5;
setState(() {
_score += 10 + bonus;
_streak++;
});
_showSuccessDialog(bonus);
} else {
setState(() {
_streak = 0;
});
_showWrongDialog();
}
}
这是游戏判定和得分的核心。
17.5 提示
void _showHint() {
if (_hintIndex < _currentWord.length) {
setState(() {
_hintPositions.add(_hintIndex);
_hintIndex++;
});
}
}
当前提示会逐步高亮乱序字母位置。
17.6 跳过
void _skipWord() {
setState(() {
_streak = 0;
});
_loadNewWord();
}
跳过会清空连击并加载新词。
总结
word_scramble 用 Flutter 实现了一个完整的单词乱序小游戏:它通过 _words 管理本地词库,通过 _loadNewWord() 随机选择目标词,通过 _scrambleWord() 打乱字母,通过 TextEditingController 获取玩家输入,通过 _checkAnswer() 进行答案校验,并用 AlertDialog、Score、Streak 和 bonus 组成反馈闭环。
从 OpenHarmony 适配角度看,这个项目覆盖了 Material 主题、TextField 输入、软键盘、Wrap 字母布局、AlertDialog 弹窗、TextButton、ElevatedButton 和 Material Icons 等基础能力,很适合验证 Flutter 文字益智类页面在 OpenHarmony 上的表现。
当前源码也有几个真实边界:正确答题后调用 _loadNewWord() 没有显式 setState,新词 UI 可能不稳定刷新;Hint 只是高亮乱序字母位置,不是真正揭示正确字符;随机选词可能连续重复;分数没有持久化。这些边界不影响项目作为入门实战案例使用,但在继续工程化时需要优先修正新词刷新路径和提示语义。
如果这篇文章对你有帮助,欢迎点赞、收藏、关注,你的支持是我持续创作的动力!
相关资源:
- OpenHarmony 官网:https://www.openharmony.cn
- OpenHarmony 文档:https://docs.openharmony.cn
- 开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
- Flutter 官网:https://flutter.dev
更多推荐



所有评论(0)