Flutter三方库适配OpenHarmony【word_scramble】单词乱序游戏项目完整实战

前言

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

word_scramble 是一个文字益智类 Flutter 小游戏。它内置 25 个开发相关英文单词,每一轮随机选择一个目标词,通过字母洗牌生成乱序单词,玩家在输入框中填写答案并点击 Check 校验。答对后获得基础分和连击奖励,答错后会弹出提示弹窗,玩家也可以使用 HintSkip 辅助继续游戏。

这类小游戏非常适合练习 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 表单输入和弹窗反馈。

从玩家视角看,流程是:

  1. 看到一个乱序单词。
  2. 在输入框中填写答案。
  3. 点击 Check 或回车提交。
  4. 答对后进入下一词。
  5. 答错后可以继续尝试或查看提示。
  6. 不想继续当前词时可以点击 Skip

从工程视角看,流程是:

  1. 从词库中随机选择目标词。
  2. 将目标词拆分成字符列表。
  3. 打乱字符顺序并展示。
  4. 获取输入框内容。
  5. 统一转小写并去掉首尾空格。
  6. 与目标词比较。
  7. 根据结果更新分数、连击和弹窗状态。

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'),
    );
  }
}

它完成了:

  1. 设置应用标题为 Word Scramble
  2. 使用橙色作为 Material 3 种子色。
  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 与软件开发
来源 硬编码在源码中
难度 apiapplication 不等

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 = [];
}

它完成了:

  1. 创建随机数对象。
  2. _words 中随机取一个目标单词。
  3. 调用 _scrambleWord() 生成乱序单词。
  4. 清空输入框。
  5. 重置提示索引。
  6. 清空提示位置列表。

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;
}

流程是:

  1. 从最后一个字符开始。
  2. [0, i] 范围内随机选一个位置。
  3. 交换两个字符。
  4. 继续向前处理。

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 正确答案路径

答对时会:

  1. 根据当前 streak 计算 bonus。
  2. 分数增加 10 + bonus
  3. streak 加 1。
  4. 弹出 Correct 对话框。

8.3 错误答案路径

答错时会:

  1. 将 streak 清零。
  2. 弹出 Try Again 对话框。
  3. 保留当前单词,允许继续尝试。

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 长单词适配

词库里有 programmingapplicationnavigation 这类较长单词。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 设备上需要确认:

  1. 图标是否可见。
  2. 图标颜色是否正确。
  3. 图标与文字是否对齐。
  4. 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

适合覆盖的行为包括:

  1. 页面显示 Score 和 Streak。
  2. 初始显示乱序字母块。
  3. 输入正确答案后分数增加。
  4. 输入错误答案后 streak 清零。
  5. 点击 Hint 后有字母块变成提示样式。
  6. 点击 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 手动验证流程

手动验证可以按如下顺序进行:

  1. 启动应用,确认显示乱序字母。
  2. 在输入框中输入错误答案,确认弹出 Try Again。
  3. 点击 Need a hint,确认字母块提示样式变化。
  4. 输入正确答案,确认弹出 Correct。
  5. 点击 Next Word,确认进入下一题。
  6. 连续答对,确认 Streak 和 bonus 增加。
  7. 点击 Skip,确认 Streak 清零并换词。
  8. 在 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 只是高亮乱序字母位置,不是真正揭示正确字符;随机选词可能连续重复;分数没有持久化。这些边界不影响项目作为入门实战案例使用,但在继续工程化时需要优先修正新词刷新路径和提示语义。

如果这篇文章对你有帮助,欢迎点赞、收藏、关注,你的支持是我持续创作的动力!


相关资源:

Logo

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

更多推荐