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. 打开应用,看到猜测范围 1 到 100。
  2. 输入一个猜测值,或通过 Slider 调整。
  3. 点击 Submit Guess
  4. 根据提示继续猜。
  5. 猜中后查看尝试次数。
  6. 点击 Play Again 开始新游戏。

从工程视角看,流程是:

  1. 初始化随机目标数字。
  2. 初始化猜测值为范围中点。
  3. 获取玩家输入。
  4. 校验输入是否在范围内。
  5. 比较猜测值和目标值。
  6. 写入历史记录。
  7. 猜中时进入胜利状态。

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 管理猜测输入
反馈组件 SnackBarAlertDialog 即时提示和胜利提示
历史展示 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'),
    );
  }
}

它完成了:

  1. 设置应用标题为 Number Guess
  2. 使用 Colors.teal 生成 Material 3 色彩方案。
  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(() {});
}

它会:

  1. 生成随机目标数字。
  2. 清空尝试次数。
  3. 将输入框设置为中间值。
  4. 重置胜利状态。
  5. 清空历史记录。
  6. 触发 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 设备上需要验证:

  1. 图标是否正常显示。
  2. 图标颜色是否符合预期。
  3. 图标和按钮文字是否对齐。
  4. 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. 初始显示范围 1-100
  2. 初始 Attempts 为 0。
  3. 输入合法数字后提交,Attempts 增加。
  4. 非法输入不会增加 Attempts。
  5. 猜中后 Submit Guess 禁用。
  6. 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. 启动应用,确认范围显示 1 到 100。
  2. 通过 Slider 调整数字,确认输入框同步变化。
  3. 点击 Submit Guess,确认出现 SnackBar。
  4. 多次猜测,确认历史记录倒序追加。
  5. 输入越界值,观察当前是否无反馈。
  6. 猜中后确认弹出胜利对话框。
  7. 点击 Play Again,确认新游戏重置。
  8. 在 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;
}

这样显示逻辑不需要依赖字符串 containssplit

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 的联动安全性。

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


相关资源:

Logo

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

更多推荐