Flutter三方库适配鸿蒙【number_guess】数字猜谜游戏项目完整实战
Flutter三方库适配鸿蒙【number_guess】数字猜谜游戏项目完整实战
前言
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
number_guess 是一个经典的数字猜谜 Flutter 小游戏。应用会在 1 到 100 之间随机生成目标数字,玩家通过输入框或 Slider 提交猜测值,系统使用 SnackBar 提示“更高”或“更低”,猜中后弹出胜利对话框,并展示目标数字、尝试次数和评价文案。
这类小游戏很适合练习 Flutter 的 随机数生成、输入校验、TextEditingController、Slider 联动、SnackBar 反馈、AlertDialog 弹窗、历史记录列表和 OpenHarmony 输入法适配。项目逻辑不复杂,但完整覆盖了一个小游戏从初始化到胜利再到重开的状态闭环。

图示说明:本文围绕 Flutter 工程中的 number_guess 项目展开,重点分析随机目标、输入提交、Slider 联动、历史记录、胜利弹窗和 OpenHarmony 适配关注点。
猜数字游戏的核心是反馈回路:玩家提交猜测,系统立即提示方向,历史记录帮助玩家逐步缩小范围。
本文将基于项目真实源码展开,核心内容包括:
NumberGuessApp的应用入口与青绿色 Material 3 主题_minNumber、_maxNumber、_targetNumber如何定义游戏范围_startNewGame()如何生成随机目标并重置状态TextEditingController如何管理输入框数字_makeGuess()如何判断高低、正确和历史记录- SnackBar 如何提供即时反馈
_showWinDialog()如何展示胜利信息- Slider 与输入框联动的实现和边界
- 当前无效输入没有提示、Slider value 解析可能异常的真实问题
一、项目背景与目标
1.1 游戏定位
number_guess 是一个入门级数字推理游戏。系统随机生成一个目标数字,玩家不断猜测,系统只告诉玩家当前猜测是偏低、偏高还是正确。玩家需要根据提示逐步缩小范围。
从玩家视角看,流程是:
- 打开应用,看到猜测范围 1 到 100。
- 输入一个猜测值,或通过 Slider 调整。
- 点击
Submit Guess。 - 根据提示继续猜。
- 猜中后查看尝试次数。
- 点击
Play Again开始新游戏。
从工程视角看,流程是:
- 初始化随机目标数字。
- 初始化猜测值为范围中点。
- 获取玩家输入。
- 校验输入是否在范围内。
- 比较猜测值和目标值。
- 写入历史记录。
- 猜中时进入胜利状态。
1.2 当前功能概览
| 功能 | 当前实现 | 技术点 |
|---|---|---|
| 应用入口 | runApp(const NumberGuessApp()) |
Flutter 启动流程 |
| 应用主题 | 青绿色 Material 3 | ColorScheme.fromSeed |
| 数字范围 | 1 到 100 | _minNumber、_maxNumber |
| 随机目标 | 每轮生成一个目标数字 | math.Random() |
| 默认猜测 | 范围中点 50 | _guess 和输入框 |
| 输入提交 | TextField + Button | TextEditingController |
| 数值滑动 | Slider | 输入框联动 |
| 反馈提示 | SnackBar | 高、低、正确 |
| 胜利反馈 | AlertDialog | 目标数和次数 |
| 历史记录 | ListView | 倒序显示猜测记录 |
1.3 适合学习的能力
这个项目适合学习:
- 随机数范围生成
- 小游戏状态重置
- TextField 数字输入
- Slider 与输入框联动
- SnackBar 即时反馈
- AlertDialog 胜利弹窗
- ListView 历史记录
- OpenHarmony 输入法和弹窗适配
二、环境准备与工程结构
2.1 技术栈概览
项目只使用 Flutter SDK 和 Dart 标准库。
| 类别 | 当前使用 | 说明 |
|---|---|---|
| 开发语言 | Dart | Flutter 应用主语言 |
| UI 框架 | Flutter Material | 页面、按钮、弹窗、列表 |
| 随机数 | dart:math |
生成目标数字 |
| 状态管理 | StatefulWidget + setState |
游戏状态刷新 |
| 输入控制 | TextEditingController |
管理猜测输入 |
| 反馈组件 | SnackBar、AlertDialog |
即时提示和胜利提示 |
| 历史展示 | ListView.builder |
展示猜测历史 |
| 目标适配 | 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 基础交互能力。
2.3 主源码结构
核心代码集中在 lib/main.dart:
import 'package:flutter/material.dart';
import 'dart:math' as math;
void main() {
runApp(const NumberGuessApp());
}
主要结构如下:
| 结构 | 类型 | 作用 |
|---|---|---|
NumberGuessApp |
StatelessWidget |
应用根组件 |
NumberGuessHomePage |
StatefulWidget |
游戏首页 |
_NumberGuessHomePageState |
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 NumberGuessApp());
}
runApp 会挂载根组件。
3.2 NumberGuessApp 根组件
根组件代码如下:
class NumberGuessApp extends StatelessWidget {
const NumberGuessApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'Number Guess',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
useMaterial3: true,
),
home: const NumberGuessHomePage(title: 'Number Guess'),
);
}
}
它完成了:
- 设置应用标题为
Number Guess。 - 使用
Colors.teal生成 Material 3 色彩方案。 - 将首页设置为
NumberGuessHomePage。
3.3 青绿色主题的作用
青绿色让页面更清爽,也和数字游戏的轻量工具感匹配。项目中青绿色用于:
- 主题 seedColor。
- 范围提示卡片背景。
- Submit Guess 按钮。
- 胜利弹窗中的 Attempts 强调色。
四、游戏状态设计
4.1 状态字段
页面状态如下:
class _NumberGuessHomePageState extends State<NumberGuessHomePage> {
int _minNumber = 1;
int _maxNumber = 100;
int _targetNumber = 50;
int _attempts = 0;
int _guess = 50;
bool _gameWon = false;
final TextEditingController _controller = TextEditingController();
final List<String> _history = [];
}
字段含义如下:
| 字段 | 作用 |
|---|---|
_minNumber |
最小猜测值 |
_maxNumber |
最大猜测值 |
_targetNumber |
系统随机生成的目标数字 |
_attempts |
已尝试次数 |
_guess |
初始猜测值,当前主要用于初始化输入框 |
_gameWon |
是否已经猜中 |
_controller |
输入框控制器 |
_history |
猜测历史 |
4.2 状态关系
_targetNumber 是正确答案,_attempts 统计提交次数,_history 记录每次提交的方向结果,_gameWon 控制提交按钮和 Slider 是否可用。
游戏进行中:
- Submit Guess 可点击。
- Slider 可显示。
- 历史记录不断增加。
游戏胜利后:
- Submit Guess 被禁用。
- Slider 不再显示。
- 弹出胜利对话框。
4.3 _guess 的当前作用
源码中 _guess 在新游戏时被设置为中间值:
_guess = (_minNumber + _maxNumber) ~/ 2;
_controller.text = _guess.toString();
后续 Slider 改变的是 _controller.text,没有同步更新 _guess。因此 _guess 当前主要是初始化字段,不是持续参与游戏逻辑的核心状态。
五、新游戏初始化
5.1 initState
页面初始化时启动新游戏:
void initState() {
super.initState();
_startNewGame();
}
这样用户打开应用后就已经有了随机目标数字。
5.2 _startNewGame 方法
新游戏逻辑如下:
void _startNewGame() {
final random = math.Random();
_targetNumber = _minNumber + random.nextInt(_maxNumber - _minNumber + 1);
_attempts = 0;
_guess = (_minNumber + _maxNumber) ~/ 2;
_controller.text = _guess.toString();
_gameWon = false;
_history.clear();
setState(() {});
}
它会:
- 生成随机目标数字。
- 清空尝试次数。
- 将输入框设置为中间值。
- 重置胜利状态。
- 清空历史记录。
- 触发 UI 刷新。
5.3 随机范围计算
目标数字生成代码如下:
_targetNumber = _minNumber + random.nextInt(_maxNumber - _minNumber + 1);
当范围是 1 到 100 时:
random.nextInt(100) => 0..99
1 + 0..99 => 1..100
因此目标数字包含上下边界。
5.4 initState 中 setState 的边界
_startNewGame() 最后调用了 setState(() {})。在 initState() 中调用它虽然能工作,但这一步不是必须的,因为第一次 build 本来就会发生。
更清晰的做法是把重置逻辑和刷新触发拆开,或者只在用户点击 Play Again 时调用 setState。
六、输入框与 Slider 联动
6.1 TextField 配置
输入框代码如下:
TextField(
controller: _controller,
keyboardType: TextInputType.number,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 32),
decoration: InputDecoration(
hintText: 'Enter your guess',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
filled: true,
),
onSubmitted: (_) => _makeGuessFromInput(),
)
它支持数字键盘、居中输入和回车提交。
6.2 Slider 配置
游戏未胜利时显示 Slider:
if (!_gameWon)
Slider(
value: double.parse(_controller.text.isEmpty ? '1' : _controller.text),
min: _minNumber * 1.0,
max: _maxNumber * 1.0,
divisions: _maxNumber - _minNumber,
label: _controller.text.isEmpty ? '1' : _controller.text,
onChanged: (value) {
setState(() {
_controller.text = value.toInt().toString();
});
},
)
Slider 的值直接来自输入框文本。
6.3 Slider 联动效果
用户拖动 Slider 时:
_controller.text = value.toInt().toString();
输入框数字会同步更新。
6.4 当前联动的真实边界
Slider 的 value 使用:
double.parse(_controller.text.isEmpty ? '1' : _controller.text)
如果输入框内容不是合法数字,例如粘贴了字母,double.parse 会抛异常。如果输入了 200,Slider 的 value 会超出 1 到 100 的范围,也可能触发断言。
更稳妥的方式是先解析并 clamp:
final parsed = int.tryParse(_controller.text) ?? _minNumber;
final safeValue = (parsed.clamp(_minNumber, _maxNumber) as int) * 1.0;
这样能避免输入框和 Slider 互相影响时出现不稳定状态。
七、提交猜测与输入校验
7.1 _makeGuessFromInput 方法
输入提交入口如下:
void _makeGuessFromInput() {
final guess = int.tryParse(_controller.text);
if (guess != null && guess >= _minNumber && guess <= _maxNumber) {
_makeGuess(guess);
}
}
它会尝试解析输入框文本,并检查范围。
7.2 校验规则
当前规则如下:
| 条件 | 行为 |
|---|---|
| 可以解析为整数 | 继续检查范围 |
| 小于最小值 | 忽略 |
| 大于最大值 | 忽略 |
| 在范围内 | 调用 _makeGuess() |
7.3 无效输入的边界
当前无效输入会被直接忽略,没有 SnackBar 或错误文案。例如:
- 输入空字符串。
- 输入字母。
- 输入 0。
- 输入 101。
从用户体验看,可以增加提示:
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Please enter a number between 1 and 100')),
);
7.4 键盘提交
输入框支持:
onSubmitted: (_) => _makeGuessFromInput()
这意味着用户可以直接使用键盘提交,不必须点击按钮。
八、猜测判断与 SnackBar 反馈
8.1 _makeGuess 方法
核心判断逻辑如下:
void _makeGuess(int guess) {
if (_gameWon) return;
setState(() {
_attempts++;
});
String hint;
if (guess < _targetNumber) {
hint = 'Try higher! ▲';
_history.add('$guess (too low)');
} else if (guess > _targetNumber) {
hint = 'Try lower! ▼';
_history.add('$guess (too high)');
} else {
_gameWon = true;
hint = 'Correct! 🎉';
_history.add('$guess (correct!)');
_showWinDialog();
}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(hint),
duration: const Duration(seconds: 1),
backgroundColor: _gameWon ? Colors.green : Colors.orange,
),
);
}
8.2 猜低逻辑
当 guess < _targetNumber:
hint = 'Try higher! ▲';
_history.add('$guess (too low)');
用户会看到“应该猜更高”的提示。
8.3 猜高逻辑
当 guess > _targetNumber:
hint = 'Try lower! ▼';
_history.add('$guess (too high)');
用户会看到“应该猜更低”的提示。
8.4 猜中逻辑
当 guess == _targetNumber:
_gameWon = true;
hint = 'Correct! 🎉';
_history.add('$guess (correct!)');
_showWinDialog();
游戏进入胜利状态,并弹出对话框。
8.5 setState 覆盖边界
当前 _attempts++ 放在 setState 中,但 _history.add() 和 _gameWon = true 在外层执行。由于后续 SnackBar 和 Dialog 会触发 UI 行为,通常能看到结果,但从代码规范看,相关状态更新放在同一个 setState 中会更清晰。
九、胜利弹窗
9.1 _showWinDialog 方法
胜利弹窗如下:
void _showWinDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Row(
children: [
Icon(Icons.celebration, color: Colors.amber),
SizedBox(width: 8),
Text('You Won!'),
],
),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('The number was: $_targetNumber'),
const SizedBox(height: 8),
Text('Attempts: $_attempts'),
],
),
),
);
}
弹窗展示目标数字和尝试次数。
9.2 评价文案
胜利弹窗根据尝试次数显示不同文案:
_attempts <= 5
? 'Amazing!'
: _attempts <= 7
? 'Good job!'
: 'Keep practicing!'
对应关系:
| 尝试次数 | 文案 |
|---|---|
| 1 到 5 | Amazing! |
| 6 到 7 | Good job! |
| 8 及以上 | Keep practicing! |
9.3 Play Again
弹窗按钮如下:
ElevatedButton(
onPressed: () {
Navigator.pop(context);
_startNewGame();
},
child: const Text('Play Again'),
)
点击后关闭弹窗并重新开始。
9.4 胜利后输入状态
猜中后 _gameWon 为 true,因此:
- Submit Guess 按钮被禁用。
- Slider 不再显示。
- 历史记录仍保留,直到新游戏开始。
十、历史记录列表
10.1 历史数据结构
历史记录保存为字符串列表:
final List<String> _history = [];
记录形式包括:
50 (too low)
80 (too high)
73 (correct!)
10.2 空状态
当历史为空时,页面显示:
const Center(
child: Text(
'Your guesses will appear here',
style: TextStyle(color: Colors.grey),
),
)
这能避免列表区域空白。
10.3 倒序展示
列表倒序展示最近记录:
final entry = _history[_history.length - 1 - index];
这样最新猜测会出现在最上方。
10.4 记录状态颜色
历史记录根据内容判断类型:
final isCorrect = entry.contains('correct');
final isTooHigh = entry.contains('too high');
颜色规则如下:
| 状态 | 颜色 |
|---|---|
| correct | 绿色 |
| too high | 橙色 |
| too low | 蓝色 |
10.5 序号显示边界
由于列表倒序展示,CircleAvatar 使用 index + 1 会让最新记录显示为 1,而不是实际第几次尝试。
如果希望展示真实尝试序号,可以使用:
final attemptNumber = _history.length - index;
这样倒序列表中的最新记录会显示真实尝试次数。
十一、统计卡片
11.1 顶部统计区域
顶部显示 Range 和 Attempts:
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildStatCard('Range', '$_minNumber-$_maxNumber'),
_buildStatCard('Attempts', _attempts.toString()),
],
)
11.2 _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.3 复用价值
这个方法让统计卡片:
- 间距一致。
- 字号一致。
- 标签和数值层级清晰。
- 后续增加更多统计项更容易。
十二、OpenHarmony 适配要点
12.1 适配关注范围
number_guess 没有平台插件,重点验证 Flutter UI、输入法、Slider、SnackBar 和弹窗。
| 适配项 | 涉及源码 | 验证重点 |
|---|---|---|
| MaterialApp | 根组件 | 应用启动和主题 |
| TextField | 数字输入 | 键盘、提交、粘贴 |
| Slider | 数值调整 | 拖动和取值 |
| SnackBar | 方向提示 | 显示和颜色 |
| AlertDialog | 胜利弹窗 | 展示和关闭 |
| ListView | 历史记录 | 倒序和滚动 |
| Card | 统计卡片 | 间距和阴影 |
| Material Icons | 发送、庆祝 | 字体资源 |
12.2 图标资源验证
项目使用了:
Icons.send
Icons.celebration
OpenHarmony 设备上需要验证:
- 图标是否正常显示。
- 图标颜色是否符合预期。
- 图标和按钮文字是否对齐。
- Material Icons 字体是否随包体加载。
12.3 输入法验证
输入框使用:
keyboardType: TextInputType.number
需要验证:
- 点击输入框是否弹出数字键盘。
- 输入数字是否同步到 Slider。
- 回车是否提交猜测。
- 粘贴非数字时页面是否稳定。
- 输入越界数字时是否有反馈。
12.4 Slider 验证
Slider 是数字选择入口:
Slider(
min: _minNumber * 1.0,
max: _maxNumber * 1.0,
divisions: _maxNumber - _minNumber,
)
需要验证:
- 拖动是否顺滑。
- label 是否显示当前值。
- 输入框是否跟随变化。
- 游戏胜利后是否隐藏。
十三、测试与验证
13.1 静态分析
建议执行:
flutter analyze
重点关注:
TextEditingController是否释放。- Slider 的 value 是否可能越界。
- 输入解析是否可能抛异常。
- 状态更新是否集中在
setState中。
13.2 组件测试方向
可以执行:
flutter test
适合覆盖的行为包括:
- 初始显示范围
1-100。 - 初始 Attempts 为 0。
- 输入合法数字后提交,Attempts 增加。
- 非法输入不会增加 Attempts。
- 猜中后 Submit Guess 禁用。
- Play Again 后 Attempts 清零。
13.3 示例测试代码
下面是一段基础页面测试思路:
testWidgets('shows number guess dashboard', (tester) async {
await tester.pumpWidget(const NumberGuessApp());
expect(find.text('Number Guess'), findsOneWidget);
expect(find.text('Range'), findsOneWidget);
expect(find.text('Attempts'), findsOneWidget);
expect(find.text('Submit Guess'), findsOneWidget);
});
这能确认页面基础结构正常。
13.4 输入提交测试思路
可以验证提交一次后 Attempts 增加:
testWidgets('submitting guess increases attempts', (tester) async {
await tester.pumpWidget(const NumberGuessApp());
await tester.enterText(find.byType(TextField), '50');
await tester.tap(find.text('Submit Guess'));
await tester.pump();
expect(find.text('1'), findsWidgets);
});
由于目标数字随机,测试重点可以先放在提交次数和历史记录,而不是固定胜利结果。
13.5 手动验证流程
手动验证可以按如下顺序进行:
- 启动应用,确认范围显示 1 到 100。
- 通过 Slider 调整数字,确认输入框同步变化。
- 点击 Submit Guess,确认出现 SnackBar。
- 多次猜测,确认历史记录倒序追加。
- 输入越界值,观察当前是否无反馈。
- 猜中后确认弹出胜利对话框。
- 点击 Play Again,确认新游戏重置。
- 在 OpenHarmony 设备上验证数字键盘、Slider 和弹窗。
十四、常见问题与优化建议
14.1 为什么输入非数字可能出问题
提交时使用 int.tryParse,这部分比较安全;但 Slider 的 value 使用 double.parse:
double.parse(_controller.text.isEmpty ? '1' : _controller.text)
如果输入框内容不是数字,build Slider 时可能异常。建议统一使用 tryParse 并限制范围。
14.2 为什么越界输入没有提示
_makeGuessFromInput() 对越界输入直接忽略:
if (guess != null && guess >= _minNumber && guess <= _maxNumber) {
_makeGuess(guess);
}
用户不会知道为什么没反应。可以增加 SnackBar 告知输入范围。
14.3 为什么历史记录序号看起来从 1 开始
列表倒序展示最新记录,但序号用的是 index + 1。所以最新记录显示为 1,而不是实际尝试次数。可以改为 _history.length - index。
14.4 为什么 targetNumber 不显示
目标数字只在胜利弹窗里显示:
Text('The number was: $_targetNumber')
游戏过程中不显示目标,这是猜数字游戏的正常规则。
14.5 是否需要持久化成绩
当前没有最高分、最佳次数或历史成绩持久化。应用重启后所有状态都会重置。如果要做完整游戏,可以记录最佳尝试次数。
十五、工程扩展方向
15.1 安全解析 Slider 值
可以抽出安全 getter:
double get _sliderValue {
final parsed = int.tryParse(_controller.text) ?? _minNumber;
return (parsed.clamp(_minNumber, _maxNumber) as int) * 1.0;
}
Slider 使用:
value: _sliderValue
这样输入框异常内容不会影响页面构建。
15.2 增加难度范围
可以增加难度配置:
| 难度 | 范围 |
|---|---|
| Easy | 1 到 50 |
| Normal | 1 到 100 |
| Hard | 1 到 500 |
状态可以扩展为:
void setRange(int min, int max) {
_minNumber = min;
_maxNumber = max;
_startNewGame();
}
15.3 增加最佳成绩
可以记录最少尝试次数:
int? _bestAttempts;
猜中时更新:
if (_bestAttempts == null || _attempts < _bestAttempts!) {
_bestAttempts = _attempts;
}
15.4 优化历史记录结构
当前历史记录是字符串,可以改成模型:
class GuessRecord {
const GuessRecord({
required this.value,
required this.result,
});
final int value;
final String result;
}
这样显示逻辑不需要依赖字符串 contains 和 split。
15.5 增加二分提示
可以根据历史记录给出当前可能范围:
int lowerBound = _minNumber;
int upperBound = _maxNumber;
当猜低时更新 lowerBound,猜高时更新 upperBound,帮助玩家理解推理过程。
十六、完整核心代码回顾
16.1 应用入口
void main() {
runApp(const NumberGuessApp());
}
入口负责启动根组件。
16.2 根组件
class NumberGuessApp extends StatelessWidget {
const NumberGuessApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'Number Guess',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
useMaterial3: true,
),
home: const NumberGuessHomePage(title: 'Number Guess'),
);
}
}
根组件负责主题和首页绑定。
16.3 新游戏
void _startNewGame() {
final random = math.Random();
_targetNumber = _minNumber + random.nextInt(_maxNumber - _minNumber + 1);
_attempts = 0;
_guess = (_minNumber + _maxNumber) ~/ 2;
_controller.text = _guess.toString();
_gameWon = false;
_history.clear();
setState(() {});
}
新游戏会生成目标数字并重置状态。
16.4 提交输入
void _makeGuessFromInput() {
final guess = int.tryParse(_controller.text);
if (guess != null && guess >= _minNumber && guess <= _maxNumber) {
_makeGuess(guess);
}
}
提交入口负责解析和范围校验。
16.5 判断高低
if (guess < _targetNumber) {
hint = 'Try higher! ▲';
_history.add('$guess (too low)');
} else if (guess > _targetNumber) {
hint = 'Try lower! ▼';
_history.add('$guess (too high)');
} else {
_gameWon = true;
hint = 'Correct! 🎉';
_history.add('$guess (correct!)');
_showWinDialog();
}
这是猜数字游戏的核心判断逻辑。
16.6 胜利弹窗
ElevatedButton(
onPressed: () {
Navigator.pop(context);
_startNewGame();
},
child: const Text('Play Again'),
)
Play Again 会关闭弹窗并重开一局。
总结
number_guess 用 Flutter 实现了一个完整的数字猜谜小游戏:它通过 _minNumber 和 _maxNumber 定义范围,通过 _targetNumber 保存随机目标,通过 TextEditingController 管理输入框,通过 _makeGuess() 判断高低和正确,通过 SnackBar 给出即时反馈,通过 ListView 展示历史记录,并通过 AlertDialog 在胜利时展示目标数字和尝试次数。
从 OpenHarmony 适配角度看,这个项目覆盖了 Material 主题、TextField 数字输入、Slider 拖动、SnackBar、AlertDialog、ListView、Card 和 Material Icons 等基础能力,很适合验证 Flutter 轻量小游戏页面在 OpenHarmony 上的表现。
当前源码也有几个真实边界:Slider 的 value 直接解析输入框文本,非数字或越界内容可能导致不稳定;无效输入没有用户提示;历史记录倒序展示时序号不是实际尝试次数; _guess 字段主要用于初始化,后续没有持续参与逻辑。这些边界不影响项目作为入门实战案例使用,但在继续工程化时需要优先修正输入与 Slider 的联动安全性。
如果这篇文章对你有帮助,欢迎点赞、收藏、关注,你的支持是我持续创作的动力!
相关资源:
- OpenHarmony 官网:https://www.openharmony.cn
- OpenHarmony 文档:https://docs.openharmony.cn
- 开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
- Flutter 官网:https://flutter.dev
更多推荐



所有评论(0)