Flutter三方库适配OpenHarmony【random_number】随机数生成器项目完整实战

Flutter三方库适配OpenHarmony【random_number】随机数生成器项目完整实战

前言

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

random_number 是一个基于 Flutter 的随机数生成器项目,核心代码位于 lib/main.dart。它通过两个输入框接收最小值和最大值,点击按钮后生成指定范围内的随机整数,并使用短暂的滚动数字效果增强反馈。最终结果会展示在渐变卡片中,最近生成过的数字会以 Chip 形式保存在历史记录区域。

这个项目适合用来讲解 Flutter 在 OpenHarmony 上的基础表单交互、数字输入解析、范围校验、异步状态刷新、随机数生成、历史记录维护和 Material 组件渲染。它没有复杂插件依赖,问题定位集中,非常适合做跨平台适配入门案例。

图片说明:本文基于 Flutter 表单、状态管理和 OpenHarmony 承载工程展开,随机数生成逻辑来自 random_number 的真实源码。

随机数生成器的核心不只是 Random().nextInt(),还包括输入容错、范围边界、异步刷新、历史记录和跨端 UI 表现。

一、项目背景与目标

1.1 项目定位

random_number 是一个轻量工具类应用。用户输入最小值和最大值,点击按钮后,应用在该闭区间内生成随机整数。例如默认范围是 1 到 100,结果可能是 1、100 或中间任意整数。

当前项目真实支持的功能包括:

  • 默认最小值为 1
  • 默认最大值为 100
  • 通过两个 TextField 输入范围。
  • 使用 int.tryParse 安全解析输入。
  • 当最小值大于最大值时显示 SnackBar
  • 点击按钮后进入 Rolling... 状态。
  • 通过 20 次延迟刷新模拟滚动数字。
  • 1 秒后生成最终随机结果。
  • 将最终结果插入历史记录顶部。
  • 历史记录最多保留 10 条。
  • 使用 WrapChip 展示历史记录。

1.2 技术目标

本文围绕真实源码拆解以下技术点:

  1. Flutter 应用入口和 Material 3 主题配置。
  2. TextEditingController 如何管理输入框默认值。
  3. int.tryParse 如何避免输入解析异常。
  4. SnackBar 如何提示范围错误。
  5. math.Random().nextInt() 如何生成闭区间随机数。
  6. Future.delayed 如何模拟滚动数字效果。
  7. mounted 如何保护异步回调中的 setState
  8. List<int> 如何维护最多 10 条历史记录。
  9. Wrap + Chip 如何展示动态历史数据。
  10. OpenHarmony 侧如何验证输入、滚动、结果和历史展示。

1.3 核心实现速览

能力 当前实现 适配关注点
应用入口 runApp(const RandomNumberApp()) 确认首帧正常渲染
主题 ColorScheme.fromSeed(seedColor: Colors.purple) 确认紫色主题和 Material 3 样式
输入 两个 TextField 确认软键盘、输入框、焦点状态
解析 int.tryParse 确认非法输入有默认兜底
校验 min > max 时显示 SnackBar 确认错误提示展示
随机数 min + Random().nextInt(max - min + 1) 确认结果落在闭区间
滚动效果 20 个延迟任务刷新结果 确认异步刷新稳定
历史记录 _history.insert(0, finalResult) 确认最新结果置顶
历史上限 超过 10 条删除末尾 确认历史数量稳定

二、环境准备与工程结构

2.1 工程结构

项目保持 Flutter 标准结构,同时包含 OpenHarmony 平台工程目录。

文件或目录 作用
lib/main.dart 应用入口、输入框、随机逻辑、结果卡片和历史记录
pubspec.yaml SDK 约束、Flutter 依赖和资源配置
analysis_options.yaml Dart 静态分析规则
test/ Flutter 测试目录
ohos/ OpenHarmony 平台承载工程
README.md 项目说明文件

lib/main.dart 是本文的核心,当前项目所有业务逻辑都在这个文件中完成。

2.2 依赖配置

项目使用 Dart SDK ^3.9.2,业务实现依赖 Flutter SDK 和 Dart 标准库。

yaml 复制代码
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

随机数生成来自 dart:math,输入、按钮、卡片、图标和提示组件来自 Flutter Material。

2.3 常用命令

bash 复制代码
flutter pub get
flutter analyze
flutter test
flutter run
命令 用途
flutter pub get 获取依赖
flutter analyze 静态分析 Dart 代码
flutter test 执行测试
flutter run 在目标设备运行

OpenHarmony 工程中还需要结合本地 Flutter OpenHarmony 工具链完成 HAP 构建和设备运行。

三、应用入口与主题配置

3.1 main 函数

项目入口很简洁:

dart 复制代码
import 'package:flutter/material.dart';
import 'dart:math' as math;

void main() {
  runApp(const RandomNumberApp());
}

material.dart 提供页面、输入框、按钮、卡片、图标、SnackBar 和布局组件。dart:math as math 用于生成随机数。

3.2 RandomNumberApp

根组件负责创建 MaterialApp

dart 复制代码
class RandomNumberApp extends StatelessWidget {
  const RandomNumberApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Random Number',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.purple),
        useMaterial3: true,
      ),
      home: const RandomNumberHomePage(title: 'Random Number'),
    );
  }
}

这段代码包含三个关键点:

  • title 设置应用标题。
  • ColorScheme.fromSeed 使用紫色生成主题色。
  • home 指向随机数生成页面。

3.3 OpenHarmony 首屏验证

OpenHarmony 上运行时,首屏需要确认:

  1. AppBar 显示 Random Number
  2. 两个输入框默认值分别为 1100
  3. 结果卡片初始显示 ?
  4. 按钮显示 Generate Random Number
  5. 未生成结果前不显示历史记录区域。

如果首屏异常,优先检查 Flutter 页面是否被 OpenHarmony entry 模块正确承载。

四、页面状态设计

4.1 StatefulWidget

随机数生成器需要记录输入、结果、滚动状态和历史,因此页面使用 StatefulWidget

dart 复制代码
class RandomNumberHomePage extends StatefulWidget {
  const RandomNumberHomePage({super.key, required this.title});
  final String title;

  @override
  State<RandomNumberHomePage> createState() => _RandomNumberHomePageState();
}

title 用于 AppBar。状态由 _RandomNumberHomePageState 管理。

4.2 状态字段

dart 复制代码
class _RandomNumberHomePageState extends State<RandomNumberHomePage> {
  final TextEditingController _minController = TextEditingController(text: '1');
  final TextEditingController _maxController = TextEditingController(text: '100');
  int? _result;
  bool _isRolling = false;
  final List<int> _history = [];
}
字段 类型 初始值 作用
_minController TextEditingController 1 管理最小值输入
_maxController TextEditingController 100 管理最大值输入
_result int? null 当前结果,空值时显示 ?
_isRolling bool false 标记是否正在滚动
_history List<int> 空列表 保存最近结果

4.3 状态关系

_result_isRolling_history 分别控制三个界面区域:

  • _result 控制结果卡片显示 ? 还是具体数字。
  • _isRolling 控制结果状态文字和数字颜色。
  • _history 控制历史记录区域是否展示,以及展示哪些 Chip

对表单工具类应用来说,状态字段要尽量少而明确。当前代码把输入、结果、状态和历史分开维护,阅读成本较低。

五、范围输入实现

5.1 输入区域布局

页面顶部用 Row 放置两个输入框。

dart 复制代码
Row(
  children: [
    Expanded(
      child: TextField(
        controller: _minController,
        keyboardType: TextInputType.number,
        decoration: InputDecoration(
          labelText: 'Min',
          border: OutlineInputBorder(
            borderRadius: BorderRadius.circular(12),
          ),
          filled: true,
        ),
      ),
    ),
    const SizedBox(width: 16),
    Expanded(
      child: TextField(
        controller: _maxController,
        keyboardType: TextInputType.number,
        decoration: InputDecoration(
          labelText: 'Max',
          border: OutlineInputBorder(
            borderRadius: BorderRadius.circular(12),
          ),
          filled: true,
        ),
      ),
    ),
  ],
)

两个 Expanded 让输入框平分横向空间,中间使用 SizedBox 保持间距。

5.2 数字键盘

输入框设置了数字键盘:

dart 复制代码
keyboardType: TextInputType.number

这可以让移动端优先弹出数字输入键盘。OpenHarmony 侧需要确认软键盘弹出、输入焦点和页面滚动表现。

5.3 输入框样式

输入框使用圆角边框和填充背景:

dart 复制代码
decoration: InputDecoration(
  labelText: 'Min',
  border: OutlineInputBorder(
    borderRadius: BorderRadius.circular(12),
  ),
  filled: true,
)

labelText 分别为 MinMax,用户能清楚区分两个输入框的含义。

六、输入解析与范围校验

6.1 安全解析整数

生成随机数前,代码先读取并解析输入框内容。

dart 复制代码
final min = int.tryParse(_minController.text) ?? 1;
final max = int.tryParse(_maxController.text) ?? 100;

int.tryParse 解析失败时返回 null。代码使用 ?? 提供默认值,最小值默认 1,最大值默认 100

6.2 范围校验

当最小值大于最大值时,代码通过 SnackBar 提示错误并结束方法。

dart 复制代码
if (min > max) {
  ScaffoldMessenger.of(context).showSnackBar(
    const SnackBar(content: Text('Minimum must be less than maximum')),
  );
  return;
}

这段逻辑避免调用 nextInt 时出现非法范围。

6.3 校验场景

输入 Min 输入 Max 解析结果 页面表现
1 100 min=1, max=100 正常生成
50 50 min=50, max=50 结果恒为 50
100 1 min=100, max=1 显示 SnackBar
空字符串 空字符串 min=1, max=100 使用默认范围
非数字 非数字 min=1, max=100 使用默认范围

当前代码没有限制负数输入。如果平台键盘允许输入负号,公式仍然可以生成负数范围内的随机整数。

七、随机数生成逻辑

7.1 闭区间公式

项目使用下面的公式生成结果:

dart 复制代码
min + math.Random().nextInt(max - min + 1)

nextInt(n) 的结果范围是 0 <= value < n。因此 max - min + 1 可以覆盖闭区间 [min, max]

7.2 示例推导

以默认范围 1 到 100 为例:

dart 复制代码
final value = 1 + math.Random().nextInt(100 - 1 + 1);

nextInt(100) 返回 0 到 99,加上 1 后得到 1 到 100。

7.3 边界值

范围 nextInt 参数 可能结果
1 到 100 100 1 到 100
0 到 9 10 0 到 9
10 到 10 1 10
-5 到 5 11 -5 到 5

这个公式是随机整数工具中最常用的闭区间写法。

八、滚动数字效果

8.1 进入滚动状态

点击按钮后,代码先设置 _isRolling = true

dart 复制代码
setState(() {
  _isRolling = true;
});

页面随后显示 Rolling...,结果数字颜色变为灰色。

8.2 20 次延迟刷新

滚动效果通过循环创建 20 个延迟任务实现。

dart 复制代码
for (int i = 0; i < 20; i++) {
  Future.delayed(Duration(milliseconds: 50 * i), () {
    if (mounted) {
      setState(() {
        _result = min + math.Random().nextInt(max - min + 1);
      });
    }
  });
}

每隔 50 毫秒刷新一次临时结果,20 次刚好持续约 1 秒。用户看到的是数字快速变化的过程。

8.3 mounted 保护

循环中的异步回调使用了 mounted 判断。

dart 复制代码
if (mounted) {
  setState(() {
    _result = min + math.Random().nextInt(max - min + 1);
  });
}

如果页面已经销毁,mountedfalse,代码不会继续调用 setState。这是异步 UI 更新中非常重要的保护。

异步延迟和页面生命周期经常同时出现。只要回调里会调用 setState,就应该考虑页面是否仍然挂载。

九、最终结果与历史记录

9.1 最终结果生成

1 秒后,项目生成最终随机数。

dart 复制代码
Future.delayed(const Duration(milliseconds: 1000), () {
  final random = math.Random();
  final finalResult = min + random.nextInt(max - min + 1);
  setState(() {
    _result = finalResult;
    _isRolling = false;
    _history.insert(0, finalResult);
    if (_history.length > 10) {
      _history.removeLast();
    }
  });
});

最终结果会覆盖滚动过程中的临时结果。

9.2 历史记录置顶

dart 复制代码
_history.insert(0, finalResult);

insert(0, value) 把最新结果放在列表头部。这样页面展示时,最新数字会排在最前面。

9.3 历史记录上限

dart 复制代码
if (_history.length > 10) {
  _history.removeLast();
}

历史记录超过 10 条时,删除最后一个元素。这样列表不会无限增长。

9.4 历史维护流程

步骤 操作 结果
1 生成最终随机数 得到 finalResult
2 更新 _result 结果卡片显示最终值
3 设置 _isRolling = false 状态文字恢复为 Result
4 插入 _history 头部 最新记录置顶
5 判断长度 超过 10 条删除最旧记录

十、结果卡片 UI

10.1 Card 容器

结果区域使用 Card 和渐变背景。

dart 复制代码
Card(
  elevation: 8,
  shape: RoundedRectangleBorder(
    borderRadius: BorderRadius.circular(20),
  ),
  child: Container(
    width: double.infinity,
    padding: const EdgeInsets.all(48),
    decoration: BoxDecoration(
      borderRadius: BorderRadius.circular(20),
      gradient: LinearGradient(
        colors: [Colors.purple.shade200, Colors.purple.shade100],
        begin: Alignment.topLeft,
        end: Alignment.bottomRight,
      ),
    ),
  ),
)

卡片使用 double.infinity 占满横向宽度,padding: 48 给大号数字留出视觉空间。

10.2 结果数字

结果数字使用 80 号粗体。

dart 复制代码
Text(
  _result?.toString() ?? '?',
  style: TextStyle(
    fontSize: 80,
    fontWeight: FontWeight.bold,
    color: _isRolling ? Colors.grey : Colors.purple,
  ),
)

_resultnull 时显示 ?。滚动过程中数字为灰色,结束后变为紫色。

10.3 状态文案

dart 复制代码
Text(
  _isRolling ? 'Rolling...' : 'Result',
  style: TextStyle(
    fontSize: 16,
    color: Colors.purple.shade700,
  ),
)

Rolling...Result 让用户知道当前处于生成中还是结果展示状态。

十一、按钮交互

11.1 ElevatedButton.icon

生成按钮使用图标加文字。

dart 复制代码
ElevatedButton.icon(
  onPressed: _generateRandom,
  icon: const Icon(Icons.casino),
  label: const Text('Generate Random Number'),
  style: ElevatedButton.styleFrom(
    padding: const EdgeInsets.all(16),
    backgroundColor: Colors.purple,
    minimumSize: const Size(double.infinity, 56),
    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.circular(12),
    ),
  ),
)

按钮宽度为 double.infinity,高度至少 56,适合移动端点击。

11.2 当前交互特点

当前按钮在滚动过程中仍然可以点击,因为代码没有把 onPressed 设置为 null。如果用户连续点击,会创建多组延迟任务,结果和历史可能快速变化。

可以基于 _isRolling 增加交互保护:

dart 复制代码
ElevatedButton.icon(
  onPressed: _isRolling ? null : _generateRandom,
  icon: const Icon(Icons.casino),
  label: Text(_isRolling ? 'Rolling...' : 'Generate Random Number'),
)

这类优化适合在更正式的工具应用中加入。

11.3 OpenHarmony 点击验证

场景 操作 预期
默认范围 点击按钮 生成 1 到 100 的数字
修改 Min 输入 10 结果不小于 10
修改 Max 输入 20 结果不大于 20
错误范围 输入 Min=20, Max=10 显示 SnackBar
连续点击 快速点击按钮 数字和历史持续刷新

十二、历史记录展示

12.1 条件渲染

历史记录为空时不展示历史区域。

dart 复制代码
if (_history.isNotEmpty) ...[
  const SizedBox(height: 24),
  const Text(
    'History',
    style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
  ),
  const SizedBox(height: 8),
  Wrap(
    spacing: 8,
    runSpacing: 8,
    alignment: WrapAlignment.center,
    children: _history.map((num) {
      return Chip(
        label: Text(num.toString()),
        backgroundColor: Colors.purple.shade50,
      );
    }).toList(),
  ),
]

if (_history.isNotEmpty) ...[] 是 Flutter 中常见的集合展开写法,适合条件渲染多个 Widget。

12.2 Wrap 布局

历史数字使用 Wrap 展示。

dart 复制代码
Wrap(
  spacing: 8,
  runSpacing: 8,
  alignment: WrapAlignment.center,
  children: _history.map((num) {
    return Chip(
      label: Text(num.toString()),
      backgroundColor: Colors.purple.shade50,
    );
  }).toList(),
)

Wrap 会根据屏幕宽度自动换行,比横向 Row 更适合动态数量的历史记录。

12.3 Chip 组件

每条历史记录渲染为 Chip

dart 复制代码
Chip(
  label: Text(num.toString()),
  backgroundColor: Colors.purple.shade50,
)

Chip 的优点是视觉轻量、边界清晰,适合展示短文本标签。

十三、OpenHarmony 适配要点

13.1 表单输入

OpenHarmony 侧重点验证输入框能力:

  • 点击输入框后软键盘是否弹出。
  • 数字键盘是否符合 TextInputType.number
  • 输入内容是否能正确同步到 TextEditingController
  • AppBar、输入框、卡片和按钮在键盘弹出后是否仍可访问。

13.2 SnackBar 提示

范围错误时,SnackBarScaffoldMessenger 展示。

dart 复制代码
ScaffoldMessenger.of(context).showSnackBar(
  const SnackBar(content: Text('Minimum must be less than maximum')),
);

OpenHarmony 上需要确认 SnackBar 的位置、停留时间、文字颜色和页面遮挡情况。

13.3 异步刷新

滚动数字依赖多个 Future.delayed

异步任务 作用 持续时间
20 次短延迟 模拟滚动数字 约 1 秒
1 次最终延迟 确认最终结果 1 秒

这类代码适合观察 OpenHarmony 上的定时任务、UI 刷新和页面生命周期。

13.4 滚动容器

页面根内容使用 SingleChildScrollView

dart 复制代码
body: SingleChildScrollView(
  padding: const EdgeInsets.all(24.0),
  child: Column(
    children: [
      // 输入、结果、按钮、历史
    ],
  ),
)

当屏幕较小或软键盘弹出时,滚动容器可以降低内容被遮挡的风险。

十四、测试与验证

14.1 初始页面测试

可以用 Widget 测试验证初始文案。

dart 复制代码
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';

void main() {
  testWidgets('random number page shows initial widgets', (tester) async {
    await tester.pumpWidget(const RandomNumberApp());

    expect(find.text('Random Number'), findsWidgets);
    expect(find.text('Min'), findsOneWidget);
    expect(find.text('Max'), findsOneWidget);
    expect(find.text('?'), findsOneWidget);
    expect(find.text('Generate Random Number'), findsOneWidget);
  });
}

这类测试不依赖随机结果,稳定性较高。

14.2 错误范围测试

错误范围可以验证 SnackBar。

dart 复制代码
testWidgets('shows snackbar when min is greater than max', (tester) async {
  await tester.pumpWidget(const RandomNumberApp());

  await tester.enterText(find.widgetWithText(TextField, 'Min'), '10');
  await tester.enterText(find.widgetWithText(TextField, 'Max'), '1');
  await tester.tap(find.text('Generate Random Number'));
  await tester.pump();

  expect(find.text('Minimum must be less than maximum'), findsOneWidget);
});

该测试覆盖输入、校验和提示链路。

14.3 手工验证矩阵

验证项 操作 预期
初始状态 打开应用 显示默认输入、问号和按钮
正常生成 点击按钮 结果在范围内
错误范围 Min 大于 Max 显示 SnackBar
相等范围 Min 等于 Max 结果恒等于该值
历史记录 连续生成 3 次 历史显示 3 个 Chip
历史上限 连续生成超过 10 次 历史最多 10 条
小屏显示 缩小屏幕或弹出键盘 页面可滚动

十五、常见问题与优化建议

15.1 为什么使用 int.tryParse

用户输入不一定是合法数字。int.parse 遇到非法输入会抛异常,而 int.tryParse 会返回 null

dart 复制代码
final min = int.tryParse(_minController.text) ?? 1;
final max = int.tryParse(_maxController.text) ?? 100;

这让页面在空输入或异常输入时仍能继续工作。

15.2 为什么公式里要加 1

nextInt(n) 不包含 n 本身。如果要生成 [min, max] 闭区间,就必须使用 max - min + 1

dart 复制代码
final finalResult = min + random.nextInt(max - min + 1);

如果少了 + 1,最大值永远不会出现。

15.3 为什么历史记录插入头部

最新结果通常最重要,因此使用 insert(0, finalResult)

dart 复制代码
_history.insert(0, finalResult);

这样历史区域从左到右优先展示最近结果。

15.4 如何避免滚动期间重复点击

当前代码允许滚动期间再次点击。可以使用 _isRolling 禁用按钮。

dart 复制代码
onPressed: _isRolling ? null : _generateRandom

同时可以让按钮文案跟随状态变化:

dart 复制代码
label: Text(_isRolling ? 'Rolling...' : 'Generate Random Number')

15.5 如何释放输入控制器

当前代码没有显式释放两个 TextEditingController。更完整的生命周期写法如下:

dart 复制代码
@override
void dispose() {
  _minController.dispose();
  _maxController.dispose();
  super.dispose();
}

输入控制器属于资源型对象,在正式项目中应在页面销毁时释放。

15.6 如何抽取随机范围函数

可以把随机数生成逻辑抽成纯函数,便于测试。

dart 复制代码
int generateRandomInRange(int min, int max, math.Random random) {
  return min + random.nextInt(max - min + 1);
}

测试时传入固定种子的 Random,可以更容易验证边界行为。

十六、工程扩展方向

16.1 支持清空历史

可以增加清空历史方法。

dart 复制代码
void _clearHistory() {
  setState(() {
    _history.clear();
  });
}

清空历史适合与历史区域放在一起,让用户能快速恢复初始状态。

16.2 支持复制结果

如果项目引入剪贴板能力,可以把当前结果复制出去。

dart 复制代码
final current = _result;
if (current != null) {
  // Clipboard.setData(ClipboardData(text: current.toString()));
}

复制能力适合抽签、随机编号和测试数据生成场景。

16.3 支持更多随机模式

基于当前结构可以扩展更多模式:

  • 随机整数。
  • 随机布尔值。
  • 随机百分比。
  • 随机列表项。
  • 随机颜色。

这些模式可以复用输入、结果卡片和历史记录,只需要替换生成逻辑。

16.4 支持状态持久化

历史记录现在只保存在内存中,应用重启后会丢失。后续可以通过本地存储保存最近结果,让用户重启应用后仍能看到历史。

dart 复制代码
class RandomHistoryStore {
  Future<void> save(List<int> history) async {
    // 持久化历史记录
  }

  Future<List<int>> load() async {
    // 读取历史记录
    return [];
  }
}

正式实现时可以根据 OpenHarmony 适配情况选择合适的本地存储方案。

十七、相关链接与延伸阅读

17.1 Flutter 官方资料

资料 链接
Flutter 官方文档 https://docs.flutter.dev/
Flutter API 文档 https://api.flutter.dev/
TextField https://api.flutter.dev/flutter/material/TextField-class.html
SnackBar https://api.flutter.dev/flutter/material/SnackBar-class.html
ScaffoldMessenger https://api.flutter.dev/flutter/material/ScaffoldMessenger-class.html
SingleChildScrollView https://api.flutter.dev/flutter/widgets/SingleChildScrollView-class.html
Wrap https://api.flutter.dev/flutter/widgets/Wrap-class.html
Chip https://api.flutter.dev/flutter/material/Chip-class.html

17.2 Dart 与 OpenHarmony 资料

资料 链接
Dart 官方文档 https://dart.dev/
Dart API 文档 https://api.dart.dev/
Random API https://api.dart.dev/stable/dart-math/Random-class.html
pub.dev https://pub.dev/
OpenHarmony docs https://github.com/openharmony/docs
开源鸿蒙跨平台社区 https://openharmonycrossplatform.csdn.net

总结

random_number 是一个结构清晰的 Flutter 随机数生成器。它通过 TextEditingController 管理最小值和最大值输入,通过 int.tryParse 做安全解析,通过 SnackBar 处理错误范围,通过 math.Random().nextInt(max - min + 1) 生成闭区间随机数,并用 Future.delayed 实现 1 秒滚动数字效果。最终结果会写入 _result,同时插入 _history 头部,历史数量超过 10 条时删除最旧记录。

从 OpenHarmony 适配角度看,这个项目适合验证 Flutter 表单输入、软键盘、SnackBar、异步 UI 刷新、渐变卡片、按钮点击、Wrap 自动换行和 Chip 渲染。由于功能集中在 Flutter 基础组件和 Dart 标准库上,适配问题可以按输入、校验、随机、展示、历史五条链路逐层排查。

掌握这个项目后,可以很自然地扩展到抽签器、随机密码生成器、随机颜色工具、随机列表选择器等应用场景。核心思路保持一致:输入范围要明确,生成公式要覆盖边界,异步刷新要保护生命周期,历史记录要控制容量。

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


相关资源:

相关推荐
FrameNotWork2 小时前
HarmonyOS 6.1 云应用客户端适配实战(三):触摸输入与坐标映射
华为·harmonyos
●VON2 小时前
鸿蒙Flutter实战:日期选择器与截止日期高亮提醒
android·flutter·华为·harmonyos·鸿蒙
●VON2 小时前
鸿蒙Flutter实战:Material 3种子色亮暗双主题系统
android·flutter·harmonyos
慧海灵舟2 小时前
鸿蒙南向开发教程 Day 3:OpenHarmony 线程管理
华为·harmonyos·写文章,赢小鸿ai
想你依然心痛2 小时前
HarmonyOS 6(API 23)实战:打造“光味智厨“——AI烹饪新体验
人工智能·华为·ar·harmonyos·智能体
灰鲸广告联盟2 小时前
新老用户广告价值不同?差异化策略如何实现收益最大化
android·开发语言·flutter·ios
颜淡慕潇3 小时前
低成本搭建鸿蒙PC运行环境:基于 Docker 的 x86_64 服务器
服务器·docker·harmonyos
慧海灵舟4 小时前
鸿蒙南向开发教程 Day 6:事件标志组(Event Flags)
华为·harmonyos
慧海灵舟4 小时前
鸿蒙南向开发教程 Day 5:延时与系统节拍
华为·harmonyos