旅行足迹地图应用


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

适配的第三方库地址:

  • huawei_map: https://pub.dev/packages/huawei_map
  • location: https://pub.dev/packages/location
  • image_picker: https://pub.dev/packages/image_picker
  • sqflite: https://pub.dev/packages/sqflite

一、项目概述

运行效果图

image-20260412105022011

image-20260412104847826

image-20260412104852519

image-20260412104857424

1.1 应用简介

旅行足迹地图是一款记录用户旅行经历的地图应用,致力于帮助用户在地图上标记去过的城市和景点,记录旅行故事,生成旅行足迹报告。通过可视化的方式展示用户的旅行轨迹,让每一次旅行都成为珍贵的回忆。

应用以清新的青色为主色调,象征旅行的自由与探索。涵盖地图展示、时间线、统计分析、个人中心四大模块。用户可以添加足迹、查看时间线、分析旅行数据、生成旅行报告,打造专属的旅行记忆库。

1.2 核心功能

功能模块 功能描述 实现方式
地图展示 在地图上标记旅行足迹 地图组件
足迹管理 添加、编辑、删除足迹 数据管理
时间线 按时间顺序展示旅行历程 时间排序
统计分析 旅行数据可视化统计 图表展示
旅行故事 记录每段旅行的故事 文本编辑
足迹报告 生成旅行足迹报告 数据汇总
照片管理 添加旅行照片 图片选择
筛选功能 按类型、季节筛选 过滤器

1.3 足迹类型定义

序号 类型名称 Emoji 描述 颜色
1 城市 🏙️ 城市地标建筑 蓝色
2 景点 🏞️ 自然人文景观 绿色
3 美食 🍜 特色美食体验 橙色
4 住宿 🏨 酒店民宿体验 紫色
5 交通 🚄 交通出行记录 灰色
6 文化 🏛️ 文化历史场所 棕色

1.4 旅行季节定义

序号 季节名称 Emoji 月份范围 特点
1 春季 🌸 3-5月 万物复苏
2 夏季 ☀️ 6-8月 热情奔放
3 秋季 🍂 9-11月 金秋收获
4 冬季 ❄️ 12-2月 银装素裹

1.5 评分等级定义

评分 描述 推荐度
体验一般 不推荐
⭐⭐ 有待改进 谨慎前往
⭐⭐⭐ 值得一去 值得推荐
⭐⭐⭐⭐ 非常不错 强烈推荐
⭐⭐⭐⭐⭐ 极佳体验 必去之地

1.6 技术栈

技术领域 技术选型 版本要求
开发框架 Flutter >= 3.0.0
编程语言 Dart >= 2.17.0
设计规范 Material Design 3 -
地图展示 huawei_map >= 6.0.0
定位服务 location >= 5.0.0
图片选择 image_picker >= 1.0.0
数据存储 sqflite >= 2.0.0
目标平台 鸿蒙OS / Android / iOS API 21+

1.7 项目结构

lib/
└── main_travel_footprint.dart
    ├── TravelFootprintApp              # 应用入口
    ├── TravelType                      # 足迹类型枚举
    ├── TravelSeason                    # 旅行季节枚举
    ├── TravelLocation                  # 足迹位置模型
    ├── TravelStats                     # 旅行统计模型
    ├── TravelFootprintController       # 足迹控制器
    ├── TravelHomePage                  # 主页面(底部导航)
    ├── _buildMapPage                   # 地图页
    ├── _buildTimelinePage              # 时间线页
    ├── _buildStatsPage                 # 统计页
    ├── _buildProfilePage               # 个人中心页
    ├── ChinaMapPainter                 # 中国地图绘制器
    ├── _AddLocationSheet               # 添加足迹表单
    └── _LocationDetailSheet            # 足迹详情页

二、系统架构

2.1 整体架构图

Data Layer

Business Layer

Presentation Layer

主页面
TravelHomePage

地图页

时间线页

统计页

个人中心

地图展示

足迹列表

添加足迹

时间线展示

足迹详情

统计概览

类型分布

季节分布

省份排行

足迹控制器
TravelFootprintController

地图绘制器
ChinaMapPainter

统计计算器
StatsCalculator

TravelLocation
足迹位置

TravelStats
旅行统计

TravelType
足迹类型

TravelSeason
旅行季节

2.2 类图设计

manages

calculates

has

has

TravelFootprintApp

+Widget build()

«enumeration»

TravelType

+String label

+String emoji

+Color color

+city()

+scenic()

+food()

+hotel()

+transport()

+culture()

«enumeration»

TravelSeason

+String label

+String emoji

+String months

+spring()

+summer()

+autumn()

+winter()

TravelLocation

+String id

+String name

+String province

+double latitude

+double longitude

+TravelType type

+DateTime visitDate

+String story

+List<String> photos

+int rating

+String? tips

+TravelSeason season

+copyWith()

TravelStats

+int totalLocations

+int totalProvinces

+int totalCities

+int totalDays

+Map<TravelType,int> typeCount

+Map<TravelSeason,int> seasonCount

+double totalDistance

+String distanceText

TravelFootprintController

-List<TravelLocation> _locations

-TravelStats _stats

-Set<String> _provinces

+List<TravelLocation> locations

+TravelStats stats

+Set<String> provinces

+initializeWithMockData()

+addLocation()

+updateLocation()

+deleteLocation()

+getLocationsByProvince()

+getLocationsByType()

+getLocationsBySeason()

+generateReport()

2.3 页面导航流程

地图

时间线

统计

我的

应用启动

主页面

底部导航

地图页

时间线页

统计页

个人中心

查看地图

点击足迹

添加足迹

足迹详情

添加表单

时间线列表

统计概览

生成报告

账户设置

数据管理

2.4 足迹管理流程

数据存储 足迹控制器 地图页 用户 数据存储 足迹控制器 地图页 用户 点击添加足迹 显示添加表单 填写足迹信息 addLocation(location) 更新列表 计算统计数据 保存数据 通知更新 显示新足迹 点击足迹 显示详情 编辑/删除 updateLocation/deleteLocation 更新数据 通知更新

三、核心模块设计

3.1 数据模型设计

3.1.1 足迹类型枚举 (TravelType)
enum TravelType {
  city(label: '城市', emoji: '🏙️', color: Color(0xFF2196F3)),
  scenic(label: '景点', emoji: '🏞️', color: Color(0xFF4CAF50)),
  food(label: '美食', emoji: '🍜', color: Color(0xFFFF9800)),
  hotel(label: '住宿', emoji: '🏨', color: Color(0xFF9C27B0)),
  transport(label: '交通', emoji: '🚄', color: Color(0xFF607D8B)),
  culture(label: '文化', emoji: '🏛️', color: Color(0xFF795548));

  final String label;
  final String emoji;
  final Color color;
  const TravelType({required this.label, required this.emoji, required this.color});
}
3.1.2 旅行季节枚举 (TravelSeason)
enum TravelSeason {
  spring(label: '春季', emoji: '🌸', months: '3-5月'),
  summer(label: '夏季', emoji: '☀️', months: '6-8月'),
  autumn(label: '秋季', emoji: '🍂', months: '9-11月'),
  winter(label: '冬季', emoji: '❄️', months: '12-2月');

  final String label;
  final String emoji;
  final String months;
  const TravelSeason({required this.label, required this.emoji, required this.months});
}
3.1.3 足迹位置模型 (TravelLocation)
class TravelLocation {
  final String id;
  final String name;
  final String province;
  final double latitude;
  final double longitude;
  final TravelType type;
  final DateTime visitDate;
  final String story;
  final List<String> photos;
  final int rating;
  final String? tips;
  final TravelSeason season;

  const TravelLocation({
    required this.id,
    required this.name,
    required this.province,
    required this.latitude,
    required this.longitude,
    required this.type,
    required this.visitDate,
    required this.story,
    this.photos = const [],
    required this.rating,
    this.tips,
    required this.season,
  });
}
3.1.4 旅行统计模型 (TravelStats)
class TravelStats {
  final int totalLocations;
  final int totalProvinces;
  final int totalCities;
  final int totalDays;
  final Map<TravelType, int> typeCount;
  final Map<TravelSeason, int> seasonCount;
  final double totalDistance;

  const TravelStats({
    this.totalLocations = 0,
    this.totalProvinces = 0,
    this.totalCities = 0,
    this.totalDays = 0,
    this.typeCount = const {},
    this.seasonCount = const {},
    this.totalDistance = 0,
  });

  String get distanceText {
    if (totalDistance >= 1000) {
      return '${(totalDistance / 1000).toStringAsFixed(1)}万公里';
    }
    return '${totalDistance.toInt()}公里';
  }
}
3.1.5 足迹类型分布
45% 20% 15% 10% 5% 5% 足迹类型分布示例 景点 城市 文化 美食 住宿 交通

3.2 页面结构设计

3.2.1 主页面布局

TravelHomePage

IndexedStack

地图页

时间线页

统计页

个人中心

FloatingActionButton

NavigationBar

添加足迹

地图 Tab

时间线 Tab

统计 Tab

我的 Tab

3.2.2 地图页结构

地图页

SliverAppBar

地图展示

足迹列表

渐变背景

省份统计

搜索按钮

筛选按钮

中国地图

足迹标记

足迹卡片

类型图标

名称地点

评分日期

3.2.3 时间线页结构

时间线页

SliverAppBar

统计摘要

时间线列表

总足迹

省份

天数

里程

日期显示

时间线节点

足迹卡片

连接线

3.2.4 统计页结构

统计页

统计概览

类型分布

季节分布

省份排行

生成报告按钮

总足迹数

省份数

旅行天数

总里程

类型进度条

季节统计圈

省份排行列表

3.3 足迹控制器逻辑

添加足迹

编辑足迹

删除足迹

生成报告

初始化

加载模拟数据

计算统计数据

用户操作

操作类型

添加到列表

更新足迹

从列表移除

汇总数据

重新计算统计

更新省份集合

计算类型分布

计算季节分布

计算总里程

通知UI更新

3.4 距离计算逻辑

获取两个足迹坐标

计算纬度差

计算经度差

转换为弧度

应用Haversine公式

计算球面距离

返回公里数


四、UI设计规范

4.1 配色方案

应用以清新的青色为主色调,象征旅行的自由与探索:

颜色类型 色值 用途
主色 #00BCD4 (Cyan) 导航、主题元素
辅助色 #4DD0E1 渐变效果
第三色 #80DEEA 卡片背景
强调色 #00ACC1 按钮高亮
背景色 #FAFAFA 页面背景
卡片背景 #FFFFFF 信息卡片
城市色 #2196F3 城市类型
景点色 #4CAF50 景点类型

4.2 足迹类型配色

类型 色值 视觉效果
城市 #2196F3 蓝色
景点 #4CAF50 绿色
美食 #FF9800 橙色
住宿 #9C27B0 紫色
交通 #607D8B 灰色
文化 #795548 棕色

4.3 字体规范

元素 字号 字重 颜色
页面标题 24px Bold 主色
足迹名称 18px Bold #000000
省份信息 14px Regular #666666
旅行故事 14px Regular #333333
统计数字 24px Bold 主色
日期显示 16px Bold #000000

4.4 组件规范

4.4.1 足迹卡片
┌─────────────────────────────────────┐
│  ┌────┐  故宫博物院            ⭐⭐⭐⭐⭐ │
│  │ 🏛️ │  北京 · 文化                  │
│  └────┘  2024年3月15日               │
└─────────────────────────────────────┘
4.4.2 时间线项
┌─────────────────────────────────────┐
│  3月   ●──────────────────────────  │
│  15日  │ ┌───────────────────────┐  │
│        │ │ 🏛️ 故宫博物院    北京  │  │
│        │ │ 第一次来到故宫...     │  │
│        │ │ 🌸 春季    ⭐⭐⭐⭐⭐  │  │
│        │ └───────────────────────┘  │
└─────────────────────────────────────┘
4.4.3 统计卡片
┌─────────────────────────────────────┐
│  旅行概览                            │
│  ┌─────────────┐ ┌─────────────┐   │
│  │ 📍 总足迹   │ │ 🗺️ 省份    │   │
│  │    15      │ │    10      │   │
│  └─────────────┘ └─────────────┘   │
│  ┌─────────────┐ ┌─────────────┐   │
│  │ 📅 天数    │ │ 🛤️ 里程    │   │
│  │   300天    │ │  1.2万公里  │   │
│  └─────────────┘ └─────────────┘   │
└─────────────────────────────────────┘
4.4.4 类型分布
┌─────────────────────────────────────┐
│  足迹类型分布                        │
│                                     │
│  🏞️ 景点  7个 (46.7%)              │
│  ═══════════════●────────────       │
│                                     │
│  🏙️ 城市  3个 (20.0%)              │
│  ═════●─────────────────────        │
│                                     │
│  🏛️ 文化  2个 (13.3%)              │
│  ═══●───────────────────────        │
└─────────────────────────────────────┘
4.4.5 添加足迹表单
┌─────────────────────────────────────┐
│  添加足迹                       ✕  │
│                                     │
│  📍 地点名称                        │
│  ┌─────────────────────────────┐   │
│  │                             │   │
│  └─────────────────────────────┘   │
│                                     │
│  🗺️ 所在省份                        │
│  ┌─────────────────────────────┐   │
│  │                             │   │
│  └─────────────────────────────┘   │
│                                     │
│  足迹类型                           │
│  [🏙️城市] [🏞️景点] [🍜美食]...     │
│                                     │
│  旅行季节                           │
│  [🌸春季] [☀️夏季] [🍂秋季] [❄️冬季]│
│                                     │
│  评分                               │
│  ⭐ ⭐ ⭐ ⭐ ⭐                       │
│                                     │
│  📝 旅行故事                        │
│  ┌─────────────────────────────┐   │
│  │                             │   │
│  │                             │   │
│  └─────────────────────────────┘   │
│                                     │
│  📅 旅行日期  2024年3月15日    >    │
│                                     │
│  ┌─────────────────────────────┐   │
│  │         保存足迹             │   │
│  └─────────────────────────────┘   │
└─────────────────────────────────────┘

五、核心功能实现

5.1 足迹控制器实现

class TravelFootprintController extends ChangeNotifier {
  List<TravelLocation> _locations = [];
  TravelStats _stats = const TravelStats();
  Set<String> _provinces = {};

  List<TravelLocation> get locations => _locations;
  TravelStats get stats => _stats;
  Set<String> get provinces => _provinces;

  void addLocation(TravelLocation location) {
    _locations.add(location);
    _calculateStats();
    notifyListeners();
  }

  void updateLocation(TravelLocation location) {
    final index = _locations.indexWhere((l) => l.id == location.id);
    if (index != -1) {
      _locations[index] = location;
      _calculateStats();
      notifyListeners();
    }
  }

  void deleteLocation(String id) {
    _locations.removeWhere((l) => l.id == id);
    _calculateStats();
    notifyListeners();
  }
}

5.2 统计计算实现

void _calculateStats() {
  _provinces = _locations.map((l) => l.province).toSet();

  final typeCount = <TravelType, int>{};
  final seasonCount = <TravelSeason, int>{};

  for (var type in TravelType.values) {
    typeCount[type] = _locations.where((l) => l.type == type).length;
  }

  for (var season in TravelSeason.values) {
    seasonCount[season] = _locations.where((l) => l.season == season).length;
  }

  double totalDistance = 0;
  for (int i = 1; i < _locations.length; i++) {
    totalDistance += _calculateDistance(
      _locations[i - 1].latitude,
      _locations[i - 1].longitude,
      _locations[i].latitude,
      _locations[i].longitude,
    );
  }

  _stats = TravelStats(
    totalLocations: _locations.length,
    totalProvinces: _provinces.length,
    totalCities: _cities.length,
    totalDays: totalDays,
    typeCount: typeCount,
    seasonCount: seasonCount,
    totalDistance: totalDistance,
  );
}

5.3 距离计算实现

double _calculateDistance(double lat1, double lon1, double lat2, double lon2) {
  const double earthRadius = 6371;
  final dLat = _toRadians(lat2 - lat1);
  final dLon = _toRadians(lon2 - lon1);
  final a = sin(dLat / 2) * sin(dLat / 2) +
      cos(_toRadians(lat1)) * cos(_toRadians(lat2)) * sin(dLon / 2) * sin(dLon / 2);
  final c = 2 * atan2(sqrt(a), sqrt(1 - a));
  return earthRadius * c;
}

double _toRadians(double degree) => degree * pi / 180;

5.4 报告生成实现

Map<String, dynamic> generateReport() {
  return {
    'totalLocations': _stats.totalLocations,
    'totalProvinces': _stats.totalProvinces,
    'totalCities': _stats.totalCities,
    'totalDays': _stats.totalDays,
    'totalDistance': _stats.distanceText,
    'typeDistribution': _stats.typeCount.map((k, v) => MapEntry(k.label, v)),
    'seasonDistribution': _stats.seasonCount.map((k, v) => MapEntry(k.label, v)),
    'provinces': _provinces.toList(),
    'topRated': _locations.where((l) => l.rating == 5).map((l) => l.name).toList(),
  };
}

5.5 地图绘制实现

class ChinaMapPainter extends CustomPainter {
  final List<TravelLocation> locations;
  final Set<String> provinces;

  
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.grey.shade200
      ..style = PaintingStyle.fill;

    final visitedPaint = Paint()
      ..color = const Color(0xFF00BCD4).withValues(alpha: 0.3)
      ..style = PaintingStyle.fill;

    final markerPaint = Paint()
      ..color = const Color(0xFF00BCD4)
      ..style = PaintingStyle.fill;

    // 绘制地图轮廓
    final path = Path();
    // ... 绘制路径

    canvas.drawPath(path, paint);

    if (provinces.isNotEmpty) {
      canvas.drawPath(path, visitedPaint);
    }

    // 绘制足迹标记
    for (var location in locations) {
      final x = centerX + (location.longitude - 105) * scale * 1.5;
      final y = centerY - (location.latitude - 35) * scale * 1.5;
      canvas.drawCircle(Offset(x, y), 6, markerPaint);
    }
  }
}

六、交互设计

6.1 添加足迹流程

足迹控制器 添加表单 地图页 用户 足迹控制器 添加表单 地图页 用户 点击添加按钮 显示表单 显示输入项 输入地点名称 选择省份 选择类型 选择季节 设置评分 输入故事 选择日期 点击保存 addLocation() 更新数据 通知更新 显示新足迹

6.2 查看足迹详情流程

编辑

删除

关闭

点击足迹卡片

显示详情弹窗

展示足迹信息

用户操作

编辑表单

确认删除

关闭弹窗

更新数据

移除足迹

刷新列表

6.3 生成报告流程

点击保存

点击关闭

点击生成报告

汇总数据

计算统计

生成报告内容

显示报告弹窗

保存报告

关闭


七、扩展功能规划

7.1 后续版本规划

2024-01-07 2024-01-14 2024-01-21 2024-01-28 2024-02-04 2024-02-11 2024-02-18 2024-02-25 2024-03-03 2024-03-10 2024-03-17 2024-03-24 2024-03-31 基础UI框架 足迹管理功能 统计分析功能 真实地图集成 照片管理功能 数据持久化 社交分享 离线地图 路线规划 V1.0 基础版本 V1.1 增强版本 V1.2 进阶版本 旅行足迹地图开发计划

7.2 功能扩展建议

7.2.1 真实地图集成

地图功能:

  • 集成华为地图SDK
  • 真实地理位置标记
  • 地图缩放拖拽
  • 卫星图切换
7.2.2 照片管理

照片功能:

  • 拍照添加照片
  • 相册选择照片
  • 照片时间线
  • 照片地图标记
7.2.3 社交分享

分享功能:

  • 分享足迹到社交平台
  • 生成精美分享图
  • 好友足迹互动
  • 旅行故事分享

八、注意事项

8.1 开发注意事项

  1. 地图权限:确保申请定位权限和地图使用权限

  2. 数据存储:使用SQLite进行本地数据持久化

  3. 照片处理:注意照片压缩和存储空间管理

  4. 性能优化:大量足迹时优化列表渲染

  5. 隐私保护:保护用户旅行轨迹隐私

8.2 常见问题

问题 原因 解决方案
地图不显示 权限未授予 申请定位权限
定位失败 GPS未开启 引导用户开启
照片加载慢 图片过大 压缩后存储
数据丢失 未正确存储 使用事务存储
统计不准确 数据未更新 重新计算统计

8.3 使用技巧

🗺️ 旅行足迹地图使用技巧 🗺️

足迹记录

  • 及时添加旅行足迹
  • 详细记录旅行故事
  • 添加旅行小贴士
  • 设置准确的评分

数据管理

  • 定期备份旅行数据
  • 整理照片和故事
  • 生成旅行报告
  • 分享精彩足迹

统计分析

  • 查看旅行统计
  • 分析旅行偏好
  • 规划未来行程
  • 回顾美好回忆

九、鸿蒙适配说明

9.1 权限配置

ohos/entry/src/main/module.json5 中添加权限:

{
  "module": {
    "requestPermissions": [
      {"name": "ohos.permission.INTERNET"},
      {"name": "ohos.permission.LOCATION"},
      {"name": "ohos.permission.APPROXIMATELY_LOCATION"},
      {"name": "ohos.permission.READ_MEDIA"},
      {"name": "ohos.permission.WRITE_MEDIA"}
    ]
  }
}

9.2 第三方库适配状态

第三方库 适配状态 说明
huawei_map ✅ 已适配 华为地图SDK
location ✅ 已适配 定位服务
image_picker ✅ 已适配 图片选择
sqflite ✅ 已适配 数据库存储
shared_preferences ✅ 已适配 偏好设置
path_provider ✅ 已适配 文件路径

9.3 运行命令

# 查看可用设备
flutter devices

# 运行到Web服务器
flutter run -d web-server -t lib/main_travel_footprint.dart --web-port 8147

# 运行到鸿蒙设备
flutter run -d 127.0.0.1:5555 -t lib/main_travel_footprint.dart

# 代码分析
flutter analyze lib/main_travel_footprint.dart

十、总结

旅行足迹地图应用为用户提供了一套完整的旅行记录解决方案。应用支持在地图上标记用户去过的城市和景点,记录旅行故事,生成旅行足迹报告,让每一次旅行都成为珍贵的回忆。

核心功能涵盖地图展示、足迹管理、时间线、统计分析、旅行故事、足迹报告、照片管理、筛选功能八大模块。用户可以添加足迹、查看时间线、分析旅行数据、生成旅行报告,打造专属的旅行记忆库。

应用采用 Material Design 3 设计规范,以清新的青色为主色调,象征旅行的自由与探索。通过本应用,希望能够帮助用户记录美好旅程,珍藏旅行回忆,让足迹遍布世界每个角落。

旅行足迹地图——记录每一段精彩旅程


Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐