鸿蒙flutter第三方库适配 - 数据网格
运行效果图数据网格应用是一款专业的数据管理工具,为用户提供高效的数据展示、筛选、编辑和导出功能。应用采用现代化的表格界面设计,支持多列数据展示、点击列头排序、多条件筛选、单元格编辑等核心功能,满足用户日常数据管理需求。应用以清新的青绿色为主色调,象征数据的专业与可靠。涵盖数据网格、数据筛选、数据统计、系统设置四大模块。用户可以快速浏览数据、筛选目标记录、编辑数据内容、导出CSV文件,实现数据的高效
数据网格应用
欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net
适配的第三方库地址:
- shared_preferences: https://pub.dev/packages/shared_preferences
- file_selector: https://pub.dev/packages/file_selector
- path_provider: https://pub.dev/packages/path_provider
- intl: https://pub.dev/packages/intl
一、项目概述
运行效果图





1.1 应用简介
数据网格应用是一款专业的数据管理工具,为用户提供高效的数据展示、筛选、编辑和导出功能。应用采用现代化的表格界面设计,支持多列数据展示、点击列头排序、多条件筛选、单元格编辑等核心功能,满足用户日常数据管理需求。
应用以清新的青绿色为主色调,象征数据的专业与可靠。涵盖数据网格、数据筛选、数据统计、系统设置四大模块。用户可以快速浏览数据、筛选目标记录、编辑数据内容、导出CSV文件,实现数据的高效管理。
1.2 核心功能
| 功能模块 | 功能描述 | 实现方式 |
|---|---|---|
| 数据展示 | 多列数据表格展示 | DataTable |
| 数据排序 | 点击列头升序/降序排序 | 排序算法 |
| 数据筛选 | 多条件组合筛选 | 筛选引擎 |
| 数据编辑 | 点击单元格编辑数据 | TextField |
| 数据导出 | 导出为CSV文件 | 文件写入 |
| 数据导入 | 导入CSV文件 | 文件解析 |
| 数据统计 | 数据汇总统计分析 | 统计引擎 |
| 数据持久化 | 本地数据存储 | SharedPreferences |
1.3 筛选类型定义
| 序号 | 筛选类型 | Emoji | 描述 | 适用数据 |
|---|---|---|---|---|
| 1 | 包含 | 🔍 | 包含指定文本 | 文本/数字 |
| 2 | 等于 | ⚖️ | 完全匹配 | 文本/数字 |
| 3 | 开头为 | 🔤 | 以指定文本开头 | 文本 |
| 4 | 结尾为 | 📝 | 以指定文本结尾 | 文本 |
| 5 | 大于 | ⬆️ | 数值大于指定值 | 数字 |
| 6 | 小于 | ⬇️ | 数值小于指定值 | 数字 |
1.4 排序方向定义
| 序号 | 排序方向 | Emoji | 描述 | 图标 |
|---|---|---|---|---|
| 1 | 升序 | ⬆️ | 从小到大排列 | arrow_upward |
| 2 | 降序 | ⬇️ | 从大到小排列 | arrow_downward |
1.5 数据列定义
| 序号 | 列名 | 数据类型 | 可编辑 | 宽度 |
|---|---|---|---|---|
| 1 | ID | 数字 | 否 | 80 |
| 2 | 姓名 | 文本 | 是 | 100 |
| 3 | 部门 | 文本 | 是 | 120 |
| 4 | 职位 | 文本 | 是 | 120 |
| 5 | 薪资 | 数字 | 是 | 100 |
| 6 | 入职日期 | 日期 | 是 | 110 |
| 7 | 状态 | 文本 | 是 | 80 |
| 8 | 邮箱 | 文本 | 是 | 180 |
| 9 | 电话 | 文本 | 是 | 120 |
1.6 技术栈
| 技术领域 | 技术选型 | 版本要求 |
|---|---|---|
| 开发框架 | Flutter | >= 3.0.0 |
| 编程语言 | Dart | >= 2.17.0 |
| 设计规范 | Material Design 3 | - |
| 数据存储 | SharedPreferences | >= 2.0.0 |
| 文件选择 | file_selector | >= 1.0.0 |
| 文件路径 | path_provider | >= 2.0.0 |
| 日期格式 | intl | >= 0.18.0 |
| 目标平台 | 鸿蒙OS / Web | API 21+ |
1.7 项目结构
lib/
└── main_data_grid.dart
├── DataGridApp # 应用入口
├── SortDirection # 排序方向枚举
├── FilterType # 筛选类型枚举
├── GridColumn # 网格列模型
├── GridRow # 网格行模型
├── DataGridHomePage # 主页面(底部导航)
├── _buildGridPage # 数据网格页
├── _buildFilterPage # 数据筛选页
├── _buildStatsPage # 数据统计页
└── _buildSettingsPage # 设置页
二、系统架构
2.1 整体架构图
2.2 类图设计
2.3 页面导航流程
2.4 数据操作流程
三、核心模块设计
3.1 数据模型设计
3.1.1 筛选类型枚举 (FilterType)
enum FilterType {
contains(label: '包含'),
equals(label: '等于'),
startsWith(label: '开头为'),
endsWith(label: '结尾为'),
greaterThan(label: '大于'),
lessThan(label: '小于');
final String label;
const FilterType({required this.label});
}
3.1.2 排序方向枚举 (SortDirection)
enum SortDirection {
ascending,
descending,
}
3.1.3 网格列模型 (GridColumn)
class GridColumn {
final String key;
final String label;
final bool isNumeric;
final bool isEditable;
final double width;
const GridColumn({
required this.key,
required this.label,
this.isNumeric = false,
this.isEditable = true,
this.width = 120,
});
}
3.1.4 网格行模型 (GridRow)
class GridRow {
final String id;
final Map<String, dynamic> values;
final DateTime createdAt;
final DateTime? updatedAt;
const GridRow({
required this.id,
required this.values,
required this.createdAt,
this.updatedAt,
});
GridRow copyWith({
Map<String, dynamic>? values,
DateTime? updatedAt,
}) {
return GridRow(
id: id,
values: values ?? this.values,
createdAt: createdAt,
updatedAt: updatedAt ?? this.updatedAt,
);
}
}
3.1.5 筛选类型分布
3.2 页面结构设计
3.2.1 主页面布局
3.2.2 数据网格页结构
3.2.3 数据筛选页结构
3.2.4 数据统计页结构
3.3 筛选引擎逻辑
3.4 排序引擎逻辑
四、UI设计规范
4.1 配色方案
应用以清新的青绿色为主色调,象征数据的专业与可靠:
| 颜色类型 | 色值 | 用途 |
|---|---|---|
| 主色 | #00897B (Teal) | 导航、主题元素 |
| 辅助色 | #4DB6AC | 数据表格 |
| 第三色 | #80CBC4 | 筛选页面 |
| 强调色 | #B2DFDB | 统计页面 |
| 背景色 | #FAFAFA | 页面背景 |
| 卡片背景 | #FFFFFF | 信息卡片 |
| 数字颜色 | #2196F3 | 数值显示 |
| 警告色 | #FF9800 | 删除警告 |
| 成功色 | #4CAF50 | 操作成功 |
4.2 统计卡片配色
| 统计项 | 色值 | 图标 |
|---|---|---|
| 总记录数 | #2196F3 (Blue) | table_rows |
| 总薪资 | #4CAF50 (Green) | attach_money |
| 平均薪资 | #FF9800 (Orange) | trending_up |
| 部门数 | #9C27B0 (Purple) | business |
4.3 字体规范
| 元素 | 字号 | 字重 | 颜色 |
|---|---|---|---|
| 页面标题 | 24px | Bold | 主色 |
| 表格标题 | 14px | Bold | #000000 |
| 表格内容 | 14px | Regular | #000000 |
| 数字内容 | 14px | Regular | #2196F3 |
| 统计数值 | 22px | Bold | 对应颜色 |
| 提示文字 | 14px | Regular | #666666 |
4.4 组件规范
4.4.1 数据表格
┌─────────────────────────────────────────────────────────────┐
│ 数据网格应用 [+] [🗑] [⬇] [⬆] │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 🔍 搜索数据... │ │
│ └─────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ ID ⬆ │ 姓名 │ 部门 │ 职位 │ 薪资 │ 状态 │
├─────────────────────────────────────────────────────────────┤
│ ☐ 1 │ 张三 │ 技术部 │ 高级工程师 │ 25000 │ 在职 │
│ ☑ 2 │ 李四 │ 产品部 │ 产品经理 │ 22000 │ 在职 │
│ ☐ 3 │ 王五 │ 技术部 │ 前端工程师 │ 18000 │ 在职 │
│ ☐ 4 │ 赵六 │ 市场部 │ 市场专员 │ 15000 │ 离职 │
└─────────────────────────────────────────────────────────────┘
4.4.2 筛选条件
┌─────────────────────────────────────────────────────────────┐
│ 筛选条件 [清除全部] │
├─────────────────────────────────────────────────────────────┤
│ 姓名 [包含 ▼] [输入筛选值... ] │
│ 部门 [等于 ▼] [技术部 ] │
│ 薪资 [大于 ▼] [15000 ] │
│ 状态 [包含 ▼] [在职 ] │
└─────────────────────────────────────────────────────────────┘
4.4.3 统计卡片
┌─────────────────────────────────────────────────────────────┐
│ ┌───────────────┐ ┌───────────────┐ │
│ │ 📊 8 │ │ 💰 151000 │ │
│ │ 总记录数 │ │ 总薪资 │ │
│ └───────────────┘ └───────────────┘ │
│ ┌───────────────┐ ┌───────────────┐ │
│ │ 📈 18888 │ │ 🏢 5 │ │
│ │ 平均薪资 │ │ 部门数 │ │
│ └───────────────┘ └───────────────┘ │
└─────────────────────────────────────────────────────────────┘
4.4.4 部门分布
┌─────────────────────────────────────────────────────────────┐
│ 部门分布 │
├─────────────────────────────────────────────────────────────┤
│ 技术部 3 人 (37.5%) │
│ ═══════════════════════════════░░░░░░░░░░░░░░░░░░░░░░░░░ │
│ │
│ 产品部 2 人 (25.0%) │
│ ═════════════════════════░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
│ │
│ 市场部 1 人 (12.5%) │
│ ═══════════░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
└─────────────────────────────────────────────────────────────┘
五、核心功能实现
5.1 数据加载实现
Future<void> _loadData() async {
final prefs = await SharedPreferences.getInstance();
final dataJson = prefs.getString('grid_data');
if (dataJson != null) {
try {
final List<dynamic> decoded = jsonDecode(dataJson);
setState(() {
_rows.clear();
for (var item in decoded) {
_rows.add(GridRow(
id: item['id'] ?? '',
values: Map<String, dynamic>.from(item['values'] ?? {}),
createdAt: DateTime.tryParse(item['createdAt'] ?? '') ?? DateTime.now(),
updatedAt: item['updatedAt'] != null
? DateTime.tryParse(item['updatedAt'])
: null,
));
}
_applyFiltersAndSort();
});
} catch (e) {
_loadSampleData();
}
} else {
_loadSampleData();
}
}
5.2 筛选引擎实现
void _applyFiltersAndSort() {
List<GridRow> result = List.from(_rows);
if (_searchQuery.isNotEmpty) {
result = result.where((row) {
return row.values.values.any((value) =>
value.toString().toLowerCase().contains(_searchQuery.toLowerCase()));
}).toList();
}
_filters.forEach((key, filterValue) {
if (filterValue.isNotEmpty) {
final filterType = _filterTypes[key] ?? FilterType.contains;
result = result.where((row) {
final cellValue = row.values[key]?.toString() ?? '';
switch (filterType) {
case FilterType.contains:
return cellValue.toLowerCase().contains(filterValue.toLowerCase());
case FilterType.equals:
return cellValue.toLowerCase() == filterValue.toLowerCase();
case FilterType.startsWith:
return cellValue.toLowerCase().startsWith(filterValue.toLowerCase());
case FilterType.endsWith:
return cellValue.toLowerCase().endsWith(filterValue.toLowerCase());
case FilterType.greaterThan:
final numValue = num.tryParse(cellValue);
final filterNum = num.tryParse(filterValue);
return numValue != null && filterNum != null && numValue > filterNum;
case FilterType.lessThan:
final numValue = num.tryParse(cellValue);
final filterNum = num.tryParse(filterValue);
return numValue != null && filterNum != null && numValue < filterNum;
}
}).toList();
}
});
if (_sortColumn != null) {
result.sort((a, b) {
final aValue = a.values[_sortColumn];
final bValue = b.values[_sortColumn];
int comparison = 0;
if (aValue is num && bValue is num) {
comparison = aValue.compareTo(bValue);
} else {
comparison = aValue.toString().compareTo(bValue.toString());
}
return _sortDirection == SortDirection.ascending ? comparison : -comparison;
});
}
setState(() {
_filteredRows.clear();
_filteredRows.addAll(result);
});
}
5.3 数据导出实现
Future<void> _exportData() async {
try {
final buffer = StringBuffer();
buffer.writeln(_columns.map((c) => c.label).join(','));
for (var row in _filteredRows) {
buffer.writeln(_columns.map((c) {
final value = row.values[c.key]?.toString() ?? '';
return value.contains(',') ? '"$value"' : value;
}).join(','));
}
final tempDir = await getTemporaryDirectory();
final file = File('${tempDir.path}/data_export_${DateTime.now().millisecondsSinceEpoch}.csv');
await file.writeAsString(buffer.toString());
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('数据已导出到: ${file.path}')),
);
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('导出失败: $e')),
);
}
}
}
5.4 数据导入实现
Future<void> _importData() async {
try {
final XFile? file = await openFile(acceptedTypeGroups: [
XTypeGroup(label: 'CSV文件', extensions: ['csv']),
]);
if (file == null) return;
final content = await file.readAsString();
final lines = content.split('\n');
if (lines.isEmpty) return;
final headers = lines[0].split(',').map((h) => h.trim().replaceAll('"', '')).toList();
setState(() {
_rows.clear();
for (int i = 1; i < lines.length; i++) {
if (lines[i].trim().isEmpty) continue;
final values = <String, dynamic>{};
final cells = lines[i].split(',');
for (int j = 0; j < headers.length && j < cells.length; j++) {
values[headers[j]] = cells[j].trim().replaceAll('"', '');
}
_rows.add(GridRow(
id: (i).toString(),
values: values,
createdAt: DateTime.now(),
));
}
_applyFiltersAndSort();
});
_saveData();
} catch (e) {
// 处理错误
}
}
5.5 统计计算实现
Widget _buildStatsPage() {
final totalSalary = _filteredRows.fold<double>(0, (sum, row) {
return sum + (num.tryParse(row.values['salary']?.toString() ?? '0') ?? 0);
});
final avgSalary = _filteredRows.isEmpty ? 0 : totalSalary / _filteredRows.length;
final departmentStats = <String, int>{};
for (var row in _filteredRows) {
final dept = row.values['department']?.toString() ?? '未知';
departmentStats[dept] = (departmentStats[dept] ?? 0) + 1;
}
// ... 渲染统计页面
}
六、交互设计
6.1 数据编辑流程
6.2 行选择流程
6.3 数据导出流程
七、扩展功能规划
7.1 后续版本规划
7.2 功能扩展建议
7.2.1 多表格支持
扩展功能:
- 创建多个数据表
- 表格间数据关联
- 表格切换管理
- 表格模板保存
7.2.2 数据可视化
可视化功能:
- 图表展示数据
- 数据趋势分析
- 自定义图表类型
- 图表导出功能
7.2.3 云端同步
同步功能:
- 数据云端备份
- 多设备同步
- 数据版本管理
- 协作编辑功能
八、注意事项
8.1 开发注意事项
-
数据量控制:大数据量时需考虑分页加载
-
内存管理:及时释放不需要的数据和控制器
-
文件权限:导入导出需要文件读写权限
-
数据验证:导入数据时需验证格式正确性
-
异常处理:文件操作需完善的异常处理
8.2 常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 数据加载失败 | JSON格式错误 | 捕获异常并加载示例数据 |
| 导出文件乱码 | 编码问题 | 使用UTF-8编码 |
| 筛选无结果 | 条件过于严格 | 提示用户调整条件 |
| 排序异常 | 数据类型不一致 | 统一数据类型处理 |
| 导入失败 | CSV格式错误 | 验证文件格式 |
8.3 使用技巧
📊 数据网格应用技巧 📊
数据管理
- 定期导出数据备份
- 使用筛选快速定位数据
- 批量选择提高删除效率
- 合理设置列宽便于查看
筛选技巧
- 组合多个筛选条件
- 使用数值比较筛选范围
- 清除筛选恢复完整数据
- 搜索框快速全文搜索
数据编辑
- 点击单元格直接编辑
- 数字列自动转换为数值
- 编辑后自动保存
- 支持撤销操作
九、运行说明
9.1 环境要求
| 环境 | 版本要求 |
|---|---|
| Flutter SDK | >= 3.0.0 |
| Dart SDK | >= 2.17.0 |
| 鸿蒙OS | API 21+ |
| Web浏览器 | Chrome 90+ |
9.2 运行命令
# 查看可用设备
flutter devices
# 运行到Web服务器
flutter run -d web-server -t lib/main_data_grid.dart --web-port 8144
# 运行到鸿蒙设备
flutter run -d 127.0.0.1:5555 lib/main_data_grid.dart
# 代码分析
flutter analyze lib/main_data_grid.dart
十、总结
数据网格应用是一款专业的数据管理工具,为用户提供高效的数据展示、筛选、编辑和导出功能。应用支持多列数据展示、点击列头排序、多条件筛选、单元格编辑、CSV导入导出等核心功能,满足用户日常数据管理需求。
核心功能涵盖数据展示、数据排序、数据筛选、数据编辑、数据导出、数据导入、数据统计、数据持久化八大模块。用户可以快速浏览数据、筛选目标记录、编辑数据内容、导出CSV文件,实现数据的高效管理。
应用采用 Material Design 3 设计规范,以清新的青绿色为主色调,象征数据的专业与可靠。通过本应用,希望能够为用户提供便捷的数据管理体验,提升工作效率。
数据网格应用——高效管理您的数据
更多推荐



所有评论(0)