【maaath】 Flutter for OpenHarmony 导航地图应用开发实战
本文基于 Flutter for OpenHarmony 跨平台技术,实现了一个功能完整的导航地图应用。Flutter for OpenHarmony 为开发者提供了强大的跨平台开发能力,一套代码即可同时运行在 Android、iOS 和 OpenHarmony 设备上,大幅降低了开发成本。随着 OpenHarmony 生态的不断完善,Flutter 跨平台方案将在鸿蒙应用开发中发挥越来越重要的作
Flutter for OpenHarmony 导航地图应用开发实战
欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net
作者:maaath
一、引言
随着 OpenHarmony 生态的快速发展,跨平台开发框架在鸿蒙设备上的适配成为开发者关注的焦点。Flutter 作为业界领先的跨平台 UI 框架,已经在 OpenHarmony 上实现了良好的运行支持。本文将基于 Flutter for OpenHarmony 跨平台技术,带领读者从零构建一个功能完整的导航地图应用,涵盖实时导航、POI 搜索、离线地图等核心功能。
本文所有代码均已在鸿蒙设备上验证通过,读者可前往 AtomGit(https://atomgit.com)获取完整项目源码。
二、项目架构设计
导航地图应用采用分层架构设计,分为数据模型层、服务层和 UI 层三层结构:
- 数据模型层:定义导航相关的数据结构,如路线、POI 点、电子眼、交通路况等
- 服务层:提供导航业务逻辑,包括路线规划、POI 搜索、收藏管理、离线地图下载等
- UI 层:基于 Flutter Material Design 3 构建用户界面,包含地图展示、搜索面板、导航控制等组件
三、核心数据模型设计
首先,我们需要定义导航应用的核心数据类型。以下是导航路线模型的关键代码:
enum TrafficLevel {
smooth,
slow,
congested,
blocked,
}
extension TrafficLevelExtension on TrafficLevel {
String get levelText {
switch (this) {
case TrafficLevel.smooth:
return '畅通';
case TrafficLevel.slow:
return '缓行';
case TrafficLevel.congested:
return '拥堵';
case TrafficLevel.blocked:
return '严重拥堵';
}
}
}
class NavigationRoute {
final String id;
final String name;
final GeoPoint origin;
final GeoPoint destination;
final String originName;
final String destinationName;
final double totalDistance;
final int totalDuration;
final double tollFee;
final List<RouteStep> steps;
final List<TrafficSegment> trafficSegments;
final List<CameraInfo> cameras;
final List<VoiceGuidance> voiceGuidances;
final String routeType;
const NavigationRoute({
required this.id,
required this.name,
required this.origin,
required this.destination,
required this.originName,
required this.destinationName,
required this.totalDistance,
required this.totalDuration,
this.tollFee = 0,
this.steps = const [],
this.trafficSegments = const [],
this.cameras = const [],
this.voiceGuidances = const [],
this.routeType = 'fastest',
});
String get distanceText {
if (totalDistance < 1) {
return '${(totalDistance * 1000).toInt()}米';
}
return '${totalDistance.toStringAsFixed(1)}公里';
}
String get durationText {
if (totalDuration < 60) {
return '$totalDuration分钟';
}
final hours = totalDuration ~/ 60;
final minutes = totalDuration % 60;
if (minutes == 0) return '$hours小时';
return '$hours小时$minutes分钟';
}
}
该模型包含了导航路线的全部核心信息:起终点坐标、总距离/时长、分段导航指令、沿途交通路况、电子眼信息以及语音播报提示。distanceText 和 durationText 计算属性将原始数据转换为用户友好的显示格式。
四、导航服务层实现
服务层是整个应用的业务逻辑核心。我们采用单例模式实现 NavigationService,提供路线规划、POI 搜索、收藏管理等功能:
class NavigationService {
static final NavigationService _instance = NavigationService._();
factory NavigationService() => _instance;
NavigationService._();
NavigationRoute planRoute(GeoPoint origin, GeoPoint destination,
{String routeType = 'fastest'}) {
final distance = _calculateDistance(
origin.latitude, origin.longitude,
destination.latitude, destination.longitude);
final duration = (distance / 40 * 60).toInt();
final steps = _generateRouteSteps(origin, destination, distance);
final trafficSegments = _generateTrafficSegments(
origin, destination, distance);
final cameras = _generateCameras(origin, destination, distance);
final voiceGuidances = _generateVoiceGuidances(steps);
return NavigationRoute(
id: 'R${DateTime.now().millisecondsSinceEpoch}',
name: routeType == 'fastest' ? '最快路线' : '推荐路线',
origin: origin,
destination: destination,
originName: '我的位置',
destinationName: '目的地',
totalDistance: distance,
totalDuration: duration,
steps: steps,
trafficSegments: trafficSegments,
cameras: cameras,
voiceGuidances: voiceGuidances,
routeType: routeType,
);
}
double _calculateDistance(
double lat1, double lng1, double lat2, double lng2) {
const double r = 6371;
final dLat = _toRadians(lat2 - lat1);
final dLng = _toRadians(lng2 - lng1);
final a = sin(dLat / 2) * sin(dLat / 2) +
cos(_toRadians(lat1)) * cos(_toRadians(lat2)) *
sin(dLng / 2) * sin(dLng / 2);
final c = 2 * atan2(sqrt(a), sqrt(1 - a));
return double.parse((r * c).toStringAsFixed(1));
}
}
路线规划的核心是 Haversine 公式,用于计算地球表面两点间的球面距离。在此基础上,我们生成分段导航指令、模拟交通路况和电子眼分布,为导航体验提供完整的数据支撑。
五、实时导航页面实现
实时导航页面是应用的核心交互界面。它包含路线选择、导航过程控制、语音播报和电子眼提醒等功能:
class NavigationRoutePage extends StatefulWidget {
final GeoPoint origin;
final GeoPoint destination;
final String destinationName;
const NavigationRoutePage({
super.key,
required this.origin,
required this.destination,
required this.destinationName,
});
State<NavigationRoutePage> createState() => _NavigationRoutePageState();
}
class _NavigationRoutePageState extends State<NavigationRoutePage> {
final _navigationService = NavigationService();
late NavigationRoute _currentRoute;
List<NavigationRoute> _allRoutes = [];
int _selectedRouteIndex = 0;
bool _isNavigating = false;
int _currentStepIndex = 0;
double _progress = 0;
Timer? _navigationTimer;
bool _voiceEnabled = true;
bool _trafficOverlay = true;
void _startNavigation() {
setState(() {
_isNavigating = true;
_currentStepIndex = 0;
_progress = 0;
});
_navigationTimer = Timer.periodic(const Duration(seconds: 2), (timer) {
if (!mounted) {
timer.cancel();
return;
}
setState(() {
_progress += 0.04;
if (_progress >= 1.0) {
_progress = 1.0;
_stopNavigation();
_showArrivalDialog();
return;
}
final stepProgress = _progress * _currentRoute.steps.length;
final newStepIndex =
stepProgress.floor().clamp(0, _currentRoute.steps.length - 1);
if (newStepIndex != _currentStepIndex) {
_currentStepIndex = newStepIndex;
if (_voiceEnabled) {
_triggerVoiceGuidance();
}
}
_updateNearestCamera();
});
});
}
void _triggerVoiceGuidance() {
if (_currentStepIndex < _currentRoute.steps.length) {
final step = _currentRoute.steps[_currentStepIndex];
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Row(
children: [
const Icon(Icons.volume_up, color: Colors.white, size: 18),
const SizedBox(width: 8),
Expanded(
child: Text(step.instruction,
style: const TextStyle(fontSize: 14)),
),
],
),
duration: const Duration(seconds: 3),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
backgroundColor: Colors.blueGrey.shade800,
),
);
}
}
}
导航过程通过 Timer 定时器模拟位置更新,每 2 秒推进一次进度。当进入新的导航步骤时,自动触发语音播报提示。同时,系统会实时检测前方电子眼,当距离电子眼小于 500 米时弹出提醒。
六、POI 搜索与收藏管理
POI 搜索功能支持关键词检索和分类浏览,覆盖美食、加油站、停车场、医院等 7 大类别:
class POISearchPage extends StatefulWidget {
final POICategory? initialCategory;
const POISearchPage({super.key, this.initialCategory});
// ...
}
// 搜索核心逻辑
List<POIInfo> searchPOIs(String keyword, {POICategory? category}) {
final query = keyword.toLowerCase();
return _pois.where((poi) {
final matchKeyword =
poi.name.toLowerCase().contains(query) ||
poi.address.toLowerCase().contains(query);
final matchCategory =
category == null || poi.category == category;
return matchKeyword && matchCategory;
}).toList();
}
收藏地点管理支持添加、删除、标签分类和滑动删除操作,方便用户快速访问常用地点:
Widget _buildFavoriteCard(FavoriteLocation fav, ThemeData theme) {
return Dismissible(
key: Key(fav.id),
direction: DismissDirection.endToStart,
background: Container(
alignment: Alignment.centerRight,
padding: const EdgeInsets.only(right: 20),
margin: const EdgeInsets.only(bottom: 10),
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(14),
),
child: const Icon(Icons.delete, color: Colors.white),
),
onDismissed: (_) {
_navigationService.removeFavoriteLocation(fav.id);
setState(() {});
},
child: Container(
margin: const EdgeInsets.only(bottom: 10),
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(14),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.05),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
// ... 收藏卡片 UI
),
);
}
七、离线地图下载
离线地图功能让用户在没有网络的情况下也能使用导航。我们模拟了下载进度管理,支持开始下载、取消下载、删除已下载城市等操作:
void _startDownload(String cityId) {
_navigationService.startDownloadCity(cityId);
_downloadingCityId = cityId;
setState(() {});
double progress = 0;
_downloadTimer = Timer.periodic(
const Duration(milliseconds: 300),
(timer) {
progress += 0.05;
if (progress >= 1.0) {
progress = 1.0;
timer.cancel();
_downloadingCityId = null;
}
_navigationService.updateDownloadProgress(cityId, progress);
if (mounted) setState(() {});
},
);
}
八、运行效果截图
以下是在鸿蒙设备上运行导航地图应用的实际效果截图:
截图一:导航首页

应用首页展示地图背景、搜索入口、快捷分类和底部功能面板
截图二:路线规划

多路线方案选择,展示距离、时长和费用对比
截图三:实时导航

导航过程中显示剩余距离、预计到达时间和转向指令
截图四:POI搜索

搜索附近地点,支持分类浏览和关键词检索
截图五:离线地图管理

城市列表展示、下载进度管理和存储空间统计
截图六:收藏地点

收藏地点列表,支持添加、删除和标签分类
九、总结
本文基于 Flutter for OpenHarmony 跨平台技术,实现了一个功能完整的导航地图应用。通过分层架构设计、数据模型抽象和服务封装,我们成功在鸿蒙设备上实现了实时导航路线、语音播报提示、电子眼提醒、离线地图下载、实时路况显示、POI 搜索、收藏地点管理和导航路线分享等 8 大核心功能。
Flutter for OpenHarmony 为开发者提供了强大的跨平台开发能力,一套代码即可同时运行在 Android、iOS 和 OpenHarmony 设备上,大幅降低了开发成本。随着 OpenHarmony 生态的不断完善,Flutter 跨平台方案将在鸿蒙应用开发中发挥越来越重要的作用。
完整项目源码已托管至 AtomGit:https://atomgit.com,欢迎开发者下载体验。
更多推荐


所有评论(0)