Flutter 框架跨平台鸿蒙开发 - 实战棋谱记录应用
欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net
一、项目概述
运行效果图




1.1 应用简介
棋谱记录是一款专为棋类爱好者设计的棋谱管理工具,支持围棋和象棋两种棋类的棋谱记录与复盘。无论是职业比赛还是业余对局,都能完整记录对局信息、棋步序列,方便日后复盘研究。应用采用经典的棋谱卡片式布局,对局信息一目了然。
应用支持棋谱的创建、编辑、删除和详情查看,棋步记录功能让每一步棋都有迹可循。复盘功能支持逐步前进后退,帮助棋手深入分析对局过程。标签分类和搜索筛选功能让棋谱管理更加便捷高效。
1.2 核心功能
| 功能模块 | 功能描述 | 实现方式 |
|---|---|---|
| 棋谱列表 | 展示所有棋谱记录 | ListView + Card |
| 新建棋谱 | 创建新的棋谱记录 | 表单页面 |
| 编辑棋谱 | 修改已有棋谱信息 | 表单预填充 |
| 删除棋谱 | 删除不需要的记录 | 确认对话框 |
| 详情查看 | 查看棋谱完整信息 | 详情页面 |
| 棋步记录 | 记录每一步棋 | 列表管理 |
| 复盘功能 | 逐步回放棋局 | 播放控制 |
| 类型筛选 | 按围棋/象棋筛选 | PopupMenu |
| 搜索功能 | 关键词搜索棋谱 | TextField过滤 |
| 标签管理 | 棋谱标签分类 | Chip组件 |
1.3 棋谱记录字段
| 字段 | 类型 | 说明 |
|---|---|---|
| 棋谱标题 | String | 棋谱名称,必填 |
| 棋类类型 | ChessType | 围棋/象棋 |
| 对局结果 | GameResult | 黑胜/白胜/红胜/和棋 |
| 黑方棋手 | String | 黑方选手姓名 |
| 白方棋手 | String | 白方选手姓名(围棋) |
| 红方棋手 | String | 红方选手姓名(象棋) |
| 对局日期 | DateTime | 对局发生日期 |
| 对局地点 | String | 对局举办地点 |
| 赛事名称 | String | 所属赛事 |
| 棋步序列 | List | 棋步列表 |
| 棋谱备注 | String | 棋谱详细说明 |
| 标签 | List | 分类标签 |
| 创建时间 | DateTime | 记录创建时间 |
| 更新时间 | DateTime | 最后修改时间 |
1.4 技术栈
| 技术领域 | 技术选型 | 版本要求 |
|---|---|---|
| 开发框架 | Flutter | >= 3.0.0 |
| 编程语言 | Dart | >= 2.17.0 |
| 设计规范 | Material Design 3 | - |
| 状态管理 | setState | - |
| 目标平台 | 鸿蒙OS | API 21+ |
1.5 项目结构
lib/
└── main_chess_record.dart
├── ChessRecordApp # 应用入口
├── ChessType # 棋类类型枚举
├── GameResult # 对局结果枚举
├── ChessMove # 棋步模型
├── ChessRecord # 棋谱模型
├── ChessRecordPage # 主列表页面
│ ├── _buildSearchBar() # 搜索栏
│ ├── _buildRecordsList() # 记录列表
│ └── _buildRecordCard() # 记录卡片
├── ChessRecordEditPage # 编辑页面
│ ├── _selectDate() # 日期选择
│ ├── _addMove() # 添加棋步
│ ├── _addTag() # 添加标签
│ └── _save() # 保存棋谱
├── ChessRecordDetailPage # 详情页面
│ └── _buildPlayerAvatar() # 选手头像
└── MovesReplayPage # 复盘页面
├── _goToMove() # 跳转棋步
├── _previousMove() # 上一步
└── _nextMove() # 下一步
二、系统架构
2.1 整体架构图
2.2 类图设计
2.3 数据流程图
2.4 复盘流程
三、核心模块设计
3.1 数据模型设计
3.1.1 棋类类型枚举 (ChessType)
enum ChessType {
go, // 围棋
xiangqi, // 象棋
}
3.1.2 对局结果枚举 (GameResult)
enum GameResult {
unknown, // 未知
blackWin, // 黑胜(围棋)
whiteWin, // 白胜
redWin, // 红胜(象棋)
blackWinXiangqi, // 黑胜(象棋)
draw, // 和棋
}
3.1.3 棋步模型 (ChessMove)
class ChessMove {
final int moveNumber; // 手数
final String notation; // 棋步记法
final String? comment; // 注释
final int? x; // X坐标
final int? y; // Y坐标
}
3.1.4 棋谱模型 (ChessRecord)
class ChessRecord {
final String id; // 唯一标识
String title; // 棋谱标题
ChessType chessType; // 棋类类型
GameResult result; // 对局结果
String blackPlayer; // 黑方(围棋)
String whitePlayer; // 白方(围棋)
String redPlayer; // 红方(象棋)
String blackPlayerXiangqi; // 黑方(象棋)
DateTime gameDate; // 对局日期
String location; // 对局地点
String event; // 赛事名称
List<ChessMove> moves; // 棋步序列
String comment; // 棋谱备注
List<String> tags; // 标签列表
DateTime createdAt; // 创建时间
DateTime updatedAt; // 更新时间
}
3.2 筛选过滤算法
3.2.1 过滤流程
3.2.2 过滤实现
List<ChessRecord> get _filteredRecords {
var records = _records.toList();
// 类型过滤
if (_selectedType != '全部') {
ChessType? type;
switch (_selectedType) {
case '围棋': type = ChessType.go; break;
case '象棋': type = ChessType.xiangqi; break;
}
if (type != null) {
records = records.where((r) => r.chessType == type).toList();
}
}
// 关键词搜索
if (_searchQuery.isNotEmpty) {
records = records.where((r) {
return r.title.toLowerCase().contains(_searchQuery.toLowerCase()) ||
r.event.toLowerCase().contains(_searchQuery.toLowerCase()) ||
r.blackPlayer.toLowerCase().contains(_searchQuery.toLowerCase());
}).toList();
}
records.sort((a, b) => b.gameDate.compareTo(a.gameDate));
return records;
}
3.3 复盘控制算法
3.3.1 播放控制流程
3.3.2 播放控制实现
void _previousMove() {
if (_currentMove > 0) {
setState(() {
_currentMove--;
});
}
}
void _nextMove() {
if (_currentMove < widget.record.moves.length) {
setState(() {
_currentMove++;
});
}
}
void _goToMove(int index) {
setState(() {
_currentMove = index;
});
}
void _goToStart() {
setState(() {
_currentMove = 0;
});
}
void _goToEnd() {
setState(() {
_currentMove = widget.record.moves.length;
});
}
3.4 页面结构设计
3.4.1 列表页面布局
3.4.2 编辑页面布局
┌─────────────────────────────────────────────────────────────┐
│ AppBar: 新建/编辑棋谱 [💾 保存] │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 📝 棋谱标题 * │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ 棋类: 围棋/象棋 │ │ 结果: 黑胜/白胜 │ │
│ └──────────────────┘ └──────────────────┘ │
│ │
│ 📅 对局日期 | 🏆 赛事名称 | 📍 对局地点 │
│ │
│ 对局双方: │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ ● 黑方 │ │ ○ 白方 │ │
│ └──────────────────┘ └──────────────────┘ │
│ │
│ 棋步记录 [+ 添加] │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ① 炮二平五 ② 马8进7 ③ ... │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 📋 棋谱备注 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 多行文本输入 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 标签: [名局] [决赛] [×] │
└─────────────────────────────────────────────────────────────┘
3.5 状态管理
3.5.1 核心状态变量
class _ChessRecordPageState extends State<ChessRecordPage> {
final List<ChessRecord> _records = []; // 所有棋谱
String _searchQuery = ''; // 搜索关键词
String _selectedType = '全部'; // 选中的类型筛选
}
class _MovesReplayPageState extends State<MovesReplayPage> {
int _currentMove = 0; // 当前棋步位置
}
3.5.2 状态更新流程
// 添加棋步
void _addMove() {
if (_moveController.text.isNotEmpty) {
setState(() {
_moves.add(ChessMove(
moveNumber: _moves.length + 1,
notation: _moveController.text,
));
_moveController.clear();
});
}
}
// 删除棋步
void _removeMove(int index) {
setState(() {
_moves.removeAt(index);
// 重新编号
for (int i = 0; i < _moves.length; i++) {
_moves[i] = ChessMove(
moveNumber: i + 1,
notation: _moves[i].notation,
);
}
});
}
四、UI设计规范
4.1 配色方案
应用采用棕色主题风格,体现棋类的传统文化:
| 颜色类型 | 色值 | 用途 |
|---|---|---|
| 主色 | Brown | AppBar、按钮、强调 |
| 围棋标签 | Black | 围棋类型标识 |
| 象棋标签 | Red | 象棋类型标识 |
| 黑方 | Black | 黑方选手标识 |
| 白方 | Grey | 白方选手标识 |
| 红方 | Red | 红方选手标识 |
4.2 棋类类型样式
4.2.1 类型标签
| 类型 | 颜色 | 说明 |
|---|---|---|
| 围棋 | 黑色 | 黑白对弈 |
| 象棋 | 红色 | 红黑对弈 |
4.2.2 结果标签
| 结果 | 颜色 |
|---|---|
| 黑胜 | 黑色 |
| 白胜 | 灰色 |
| 红胜 | 红色 |
| 和棋 | 橙色 |
| 未知 | 灰色 |
4.3 组件规范
4.3.1 棋谱卡片
┌─────────────────────────────────────────────────────────────┐
│ [围棋] [黑胜] ⋮ │
│ 名人战决赛第一局 │
│ 🏆 名人战 📅 2024-01-15 │
│ ● 柯洁 vs ○ 申真谞 │
│ 📋 250手 [名局] [决赛] │
└─────────────────────────────────────────────────────────────┘
4.3.2 选手头像
┌─────────────────┐
│ ● 黑方 │
│ 柯洁 │
└─────────────────┘
4.4 交互设计
4.4.1 操作方式
| 操作 | 手势 | 效果 |
|---|---|---|
| 查看详情 | 点击卡片 | 跳转详情页 |
| 新建棋谱 | 点击浮动按钮 | 跳转编辑页 |
| 编辑棋谱 | 点击菜单-编辑 | 跳转编辑页 |
| 删除棋谱 | 点击菜单-删除 | 确认后删除 |
| 复盘棋局 | 点击菜单-复盘 | 跳转复盘页 |
| 搜索 | 输入关键词 | 实时过滤 |
| 筛选 | 点击筛选图标 | 选择类型 |
4.4.2 复盘控制
┌─────────────────────────────────────────────────────────────┐
│ ⏮ ◀ [ 50/250 ] ▶ ⏭ │
└─────────────────────────────────────────────────────────────┘
五、核心功能实现
5.1 列表页面构建
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('棋谱记录'),
actions: [
PopupMenuButton<String>(
icon: const Icon(Icons.filter_list),
onSelected: (value) {
setState(() {
_selectedType = value;
});
},
itemBuilder: (context) => _typeFilters.map((type) {
return PopupMenuItem(value: type, child: Text(type));
}).toList(),
),
],
),
body: Column(
children: [
_buildSearchBar(),
Expanded(
child: _filteredRecords.isEmpty
? _buildEmptyState()
: _buildRecordsList(),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: _addRecord,
child: const Icon(Icons.add),
),
);
}
5.2 棋步管理
// 添加棋步
void _addMove() {
if (_moveController.text.isNotEmpty) {
setState(() {
_moves.add(ChessMove(
moveNumber: _moves.length + 1,
notation: _moveController.text,
));
_moveController.clear();
});
}
}
// 删除棋步
void _removeMove(int index) {
setState(() {
_moves.removeAt(index);
// 重新编号所有棋步
for (int i = 0; i < _moves.length; i++) {
_moves[i] = ChessMove(
moveNumber: i + 1,
notation: _moves[i].notation,
comment: _moves[i].comment,
);
}
});
}
5.3 复盘功能
class _MovesReplayPageState extends State<MovesReplayPage> {
int _currentMove = 0;
void _previousMove() {
if (_currentMove > 0) {
setState(() {
_currentMove--;
});
}
}
void _nextMove() {
if (_currentMove < widget.record.moves.length) {
setState(() {
_currentMove++;
});
}
}
void _goToMove(int index) {
setState(() {
_currentMove = index;
});
}
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
// 当前手数显示
Text('第 $_currentMove / ${widget.record.moves.length} 手'),
// 棋步列表
Expanded(child: ListView.builder(...)),
// 播放控制
Row(
children: [
IconButton(onPressed: _goToStart, icon: Icon(Icons.skip_previous)),
IconButton(onPressed: _previousMove, icon: Icon(Icons.chevron_left)),
Text('$_currentMove/${widget.record.moves.length}'),
IconButton(onPressed: _nextMove, icon: Icon(Icons.chevron_right)),
IconButton(onPressed: _goToEnd, icon: Icon(Icons.skip_next)),
],
),
],
),
);
}
}
5.4 表单验证与保存
void _save() {
if (_formKey.currentState!.validate()) {
final now = DateTime.now();
final record = ChessRecord(
id: widget.record?.id ?? DateTime.now().millisecondsSinceEpoch.toString(),
title: _titleController.text,
chessType: _selectedType,
result: _selectedResult,
blackPlayer: _blackPlayerController.text,
whitePlayer: _whitePlayerController.text,
redPlayer: _redPlayerController.text,
blackPlayerXiangqi: _blackPlayerXiangqiController.text,
gameDate: _gameDate,
location: _locationController.text,
event: _eventController.text,
moves: _moves,
comment: _commentController.text,
tags: _tags,
createdAt: widget.record?.createdAt ?? now,
updatedAt: now,
);
Navigator.pop(context, record);
}
}
六、棋谱知识拓展
6.1 围棋棋谱
6.1.1 围棋记谱方式
6.1.2 围棋术语
| 术语 | 说明 |
|---|---|
| 星 | 棋盘上的9个星位 |
| 小目 | 三四路位置 |
| 目外 | 三五路位置 |
| 高目 | 四五路位置 |
| 天元 | 棋盘中心点 |
6.2 象棋棋谱
6.2.1 象棋记谱方式
6.2.2 象棋术语
| 术语 | 说明 |
|---|---|
| 平 | 棋子横向移动 |
| 进 | 棋子向前移动 |
| 退 | 棋子向后移动 |
| 将 | 将军 |
| 杀 | 绝杀 |
6.3 棋谱保存格式
6.3.1 常见格式
| 格式 | 说明 | 用途 |
|---|---|---|
| SGF | 围棋标准格式 | 围棋软件通用 |
| PGN | 国际象棋格式 | 国际象棋通用 |
| XQF | 象棋格式 | 象棋软件专用 |
| JSON | 通用数据格式 | 跨平台交换 |
七、扩展功能规划
7.1 后续版本规划
7.2 功能扩展建议
7.2.1 棋盘可视化
| 功能 | 说明 |
|---|---|
| 围棋棋盘 | 19x19路棋盘显示 |
| 象棋棋盘 | 9x10路棋盘显示 |
| 落子动画 | 棋子放置动画效果 |
| 标记功能 | 标记关键点 |
7.2.2 导入导出
| 功能 | 说明 |
|---|---|
| SGF导入 | 导入围棋SGF文件 |
| XQF导入 | 导入象棋XQF文件 |
| 图片导出 | 导出棋盘图片 |
| 分享功能 | 分享棋谱链接 |
7.2.3 AI分析
| 功能 | 说明 |
|---|---|
| 形势判断 | 分析当前局面 |
| 最佳选点 | AI推荐最佳着法 |
| 失误分析 | 标记关键失误 |
八、注意事项
8.1 开发注意事项
-
棋步编号:删除棋步后需要重新编号
-
类型切换:切换棋类类型时清空不相关字段
-
日期处理:注意日期的格式化和解析
-
表单验证:必填字段要进行验证
8.2 用户体验优化
💡 用户体验建议 💡
- 棋步记录简洁高效
- 复盘操作流畅自然
- 对局信息一目了然
- 支持快速搜索筛选
8.3 常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 棋步编号错误 | 删除后未重编号 | 检查重编号逻辑 |
| 类型切换异常 | 字段未清空 | 切换时清空字段 |
| 复盘位置错误 | 索引越界 | 检查边界条件 |
| 搜索无结果 | 过滤条件错误 | 检查过滤逻辑 |
九、运行说明
9.1 环境要求
| 环境 | 版本要求 |
|---|---|
| Flutter SDK | >= 3.0.0 |
| Dart SDK | >= 2.17.0 |
| 鸿蒙OS | API 21+ |
9.2 运行命令
# 查看可用设备
flutter devices
# 运行到鸿蒙设备
flutter run -d 127.0.0.1:5555 lib/main_chess_record.dart
# 运行到Windows
flutter run -d windows -t lib/main_chess_record.dart
# 代码分析
flutter analyze lib/main_chess_record.dart
十、总结
棋谱记录应用通过完善的功能设计,帮助棋类爱好者记录和管理棋谱。应用支持围棋和象棋两种棋类,对局信息、棋步序列、备注标签等核心数据完整记录。复盘功能支持逐步前进后退,帮助棋手深入分析对局过程。
棋步管理功能支持添加和删除操作,编号自动维护。标签功能帮助对棋谱进行分类,方便筛选和管理。搜索和筛选功能让用户快速定位目标棋谱,提高管理效率。
界面设计采用棕色主题风格,体现棋类的传统文化底蕴。类型标签使用不同颜色区分围棋和象棋,选手头像清晰展示对局双方。应用采用Material Design 3设计规范,遵循Flutter最佳实践,代码结构清晰,易于维护和扩展。
棋谱记录,传承经典,复盘研究,棋艺精进!
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)