Flutter地图导航插件flutter_mapbox_navigation的使用

发布于 1周前 作者 sinazl 来自 Flutter

Flutter地图导航插件flutter_mapbox_navigation的使用

Pub BuyMeACoffee

简介

flutter_mapbox_navigation 是一个强大的 Flutter 插件,允许你在应用中添加专业的地图导航功能。通过 MapBox 提供的地图服务,你可以实现详细的路线导航、交通避让、实时语音提示等功能,而无需离开你的应用。

特性

  • 完整的导航 UI:为 Flutter 应用提供了一个完整的转弯导航界面,可以轻松集成到你的应用中。
  • 专业设计的地图样式:支持白天和夜晚驾驶的地图样式。
  • 全球导航数据:提供全球范围内的驾驶、骑行和步行导航数据,基于开放数据和用户反馈。
  • 交通避让和主动重新规划:根据当前路况在超过 55 个国家提供交通避让和主动重新规划功能。
  • 自然语音提示:通过 Amazon Polly 提供自然的语音提示(无需额外配置)。
  • 多语言支持:支持超过两打语言。

配置

iOS 配置

  1. 创建 MapBox 访问令牌

    • 前往 Mapbox 账户仪表板 并创建一个具有 DOWNLOADS:READ 权限范围的访问令牌。
    • 创建一个名为 .netrc 的文件在你的主目录中,并添加以下内容:
      machine api.mapbox.com
        login mapbox
        password PRIVATE_MAPBOX_API_TOKEN
      
      其中 PRIVATE_MAPBOX_API_TOKEN 是你的 MapBox API 令牌。
  2. 设置 MBXAccessToken

    • 在项目编辑器中选择应用目标,然后转到 Info 标签页。
    • 在“Custom iOS Target Properties”部分,设置 MBXAccessToken 为你的访问令牌。
  3. 设置位置权限

    • 在 Info 标签页中,设置 NSLocationWhenInUseUsageDescription 为:
      Shows your location on the map and helps improve OpenStreetMap.
      
  4. 启用后台模式

    • 转到 Capabilities 标签页,启用“Audio, AirPlay, and Picture in Picture”和“Location updates”。
    • 或者在 Info 标签页中将 audiolocation 值添加到 UIBackgroundModes 数组中。

Android 配置

  1. 设置 mapbox_access_token

    • 创建一个新的资源文件 mapbox_access_token.xml,路径为 <YOUR_FLUTTER_APP_ROOT>/android/app/src/main/res/values/mapbox_access_token.xml
    • 添加一个字符串资源,名称为 mapbox_access_token,值为你的令牌:
      <?xml version="1.0" encoding="utf-8"?>
      <resources xmlns:tools="http://schemas.android.com/tools">
          <string name="mapbox_access_token" translatable="false" tools:ignore="UnusedResources">ADD_MAPBOX_ACCESS_TOKEN_HERE</string>
      </resources>
      
  2. 添加权限

    • 在应用级别的 AndroidManifest.xml 中添加以下权限:
      <manifest>
          ...
          <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
          <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
          <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
          ...
      </manifest>
      
  3. 设置 MAPBOX_DOWNLOADS_TOKEN

    • 将具有 downloads:read 权限范围的 MapBox 下载令牌添加到 gradle.properties 文件中:
      MAPBOX_DOWNLOADS_TOKEN=sk.XXXXXXXXXXXXXXX
      
  4. 更新 MainActivity.kt

    • MainActivity 继承自 FlutterFragmentActivity 而不是 FlutterActivity
      import io.flutter.embedding.android.FlutterFragmentActivity
      
      class MainActivity: FlutterFragmentActivity() {
      }
      
  5. 添加 Kotlin BOM

    • android/app/build.gradle 中添加以下依赖:
      implementation platform("org.jetbrains.kotlin:kotlin-bom:1.8.0")
      

使用方法

设置默认路由选项(可选)

MapBoxNavigation.instance.setDefaultOptions(MapBoxOptions(
    initialLatitude: 36.1175275,
    initialLongitude: -115.1839524,
    zoom: 13.0,
    tilt: 0.0,
    bearing: 0.0,
    enableRefresh: false,
    alternatives: true,
    voiceInstructionsEnabled: true,
    bannerInstructionsEnabled: true,
    allowsUTurnAtWayPoints: true,
    mode: MapBoxNavigationMode.drivingWithTraffic,
    mapStyleUrlDay: "https://url_to_day_style",
    mapStyleUrlNight: "https://url_to_night_style",
    units: VoiceUnits.imperial,
    simulateRoute: true,
    language: "en"
));

监听事件

MapBoxNavigation.instance.registerRouteEventListener(_onRouteEvent);

Future<void> _onRouteEvent(e) async {
    _distanceRemaining = await _directions.distanceRemaining;
    _durationRemaining = await _directions.durationRemaining;

    switch (e.eventType) {
        case MapBoxEvent.progress_change:
            var progressEvent = e.data as RouteProgressEvent;
            _arrived = progressEvent.arrived;
            if (progressEvent.currentStepInstruction != null)
                _instruction = progressEvent.currentStepInstruction;
            break;
        case MapBoxEvent.route_building:
        case MapBoxEvent.route_built:
            _routeBuilt = true;
            break;
        case MapBoxEvent.route_build_failed:
            _routeBuilt = false;
            break;
        case MapBoxEvent.navigation_running:
            _isNavigating = true;
            break;
        case MapBoxEvent.on_arrival:
            _arrived = true;
            if (!_isMultipleStop) {
                await Future.delayed(Duration(seconds: 3));
                await _controller.finishNavigation();
            } else {}
            break;
        case MapBoxEvent.navigation_finished:
        case MapBoxEvent.navigation_cancelled:
            _routeBuilt = false;
            _isNavigating = false;
            break;
        default:
            break;
    }
    // 刷新 UI
    setState(() {});
}

开始导航

final cityhall = WayPoint(name: "City Hall", latitude: 42.886448, longitude: -78.878372);
final downtown = WayPoint(name: "Downtown Buffalo", latitude: 42.8866177, longitude: -78.8814924);

var wayPoints = List<WayPoint>();
wayPoints.add(cityhall);
wayPoints.add(downtown);

await MapBoxNavigation.instance.startNavigation(wayPoints: wayPoints);

嵌入导航视图

声明控制器

MapBoxNavigationViewController _controller;

将导航视图添加到小部件树

Container(
    color: Colors.grey,
    child: MapBoxNavigationView(
        options: _options,
        onRouteEvent: _onRouteEvent,
        onCreated: (MapBoxNavigationViewController controller) async {
            _controller = controller;
        }),
),

构建路线

var wayPoints = List<WayPoint>();
wayPoints.add(_origin);
wayPoints.add(_stop1);
wayPoints.add(_stop2);
wayPoints.add(_stop3);
wayPoints.add(_stop4);
wayPoints.add(_origin);
_controller.buildRoute(wayPoints: wayPoints);

开始导航

_controller.startNavigation();

其他 iOS 配置

info.plist 文件中添加以下内容:

<dict>
    ...
    <key>io.flutter.embedded_views_preview</key>
    <true/>
    ...
</dict>

嵌入导航视图截图

iOS 视图 Android 视图

待办事项

  • [已完成] Android 实现
  • [已完成] 添加更多设置(如导航模式:驾驶、步行等)
  • [已完成] 流式事件(如相关导航通知、指标、当前位置等)
  • [已完成] 可嵌入的导航视图
  • 离线路由

示例代码

import 'package:flutter/material.dart';
import 'package:flutter_mapbox_navigation/flutter_mapbox_navigation.dart';

void main() => runApp(const SampleNavigationApp());

class SampleNavigationApp extends StatelessWidget {
  const SampleNavigationApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('MapBox Navigation Example')),
        body: Center(
          child: ElevatedButton(
            onPressed: () async {
              final cityhall = WayPoint(name: "City Hall", latitude: 42.886448, longitude: -78.878372);
              final downtown = WayPoint(name: "Downtown Buffalo", latitude: 42.8866177, longitude: -78.8814924);

              var wayPoints = List<WayPoint>();
              wayPoints.add(cityhall);
              wayPoints.add(downtown);

              await MapBoxNavigation.instance.startNavigation(wayPoints: wayPoints);
            },
            child: const Text('Start Navigation'),
          ),
        ),
      ),
    );
  }
}

以上是一个简单的示例,展示了如何使用 flutter_mapbox_navigation 插件在 Flutter 应用中实现地图导航功能。希望对你有所帮助!


更多关于Flutter地图导航插件flutter_mapbox_navigation的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter地图导航插件flutter_mapbox_navigation的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何使用 flutter_mapbox_navigation 插件在 Flutter 应用中实现地图导航的示例代码。这个示例假定你已经设置好 Flutter 开发环境,并且已经添加了 flutter_mapbox_navigation 依赖到你的 pubspec.yaml 文件中。

首先,确保你的 pubspec.yaml 文件中包含以下依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_mapbox_navigation: ^x.y.z  # 请替换为最新版本号

然后运行 flutter pub get 来安装依赖。

接下来,你需要配置 Mapbox 的访问令牌。这通常在你的应用的 AndroidManifest.xmlInfo.plist 文件中设置,但也可能在代码中设置。为了简单起见,这里假设你已经在这些文件中配置好了。

下面是一个完整的 Flutter 应用示例,展示如何使用 flutter_mapbox_navigation 进行地图导航:

import 'package:flutter/material.dart';
import 'package:flutter_mapbox_navigation/flutter_mapbox_navigation.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Mapbox Navigation Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MapboxNavigationScreen(),
    );
  }
}

class MapboxNavigationScreen extends StatefulWidget {
  @override
  _MapboxNavigationScreenState createState() => _MapboxNavigationScreenState();
}

class _MapboxNavigationScreenState extends State<MapboxNavigationScreen> {
  late MapboxNavigationController _navigationController;

  @override
  void initState() {
    super.initState();
    // 初始化 MapboxNavigationController
    _navigationController = MapboxNavigationController(
      accessToken: 'YOUR_MAPBOX_ACCESS_TOKEN',  // 请替换为你的 Mapbox 访问令牌
      origin: MapboxPoint(latitude: 40.730610, longitude: -73.935242), // 起点坐标
      destination: MapboxPoint(latitude: 37.774929, longitude: -122.419418), // 终点坐标
    );

    // 监听导航事件
    _navigationController.navigationStateChanges.listen((state) {
      print('Navigation state changed to: $state');
    });

    // 开始导航
    _navigationController.startNavigation();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Mapbox Navigation Demo'),
      ),
      body: Stack(
        children: [
          // MapboxNavigationView 用于显示地图和导航信息
          MapboxNavigationView(
            controller: _navigationController,
            options: MapboxNavigationViewOptions(
              // 你可以在这里配置导航视图的各种选项
              // 例如:显示路线、交通信息等
            ),
          ),
          // 自定义按钮用于停止导航(仅示例,实际应用中可能需要更复杂的逻辑)
          Positioned(
            bottom: 20,
            right: 20,
            child: ElevatedButton(
              onPressed: () {
                _navigationController.stopNavigation();
              },
              child: Text('Stop Navigation'),
            ),
          ),
        ],
      ),
    );
  }

  @override
  void dispose() {
    // 释放资源
    _navigationController.dispose();
    super.dispose();
  }
}

在这个示例中,我们创建了一个简单的 Flutter 应用,其中包含一个 MapboxNavigationView 用于显示地图和导航信息。我们还初始化了一个 MapboxNavigationController 并设置了起点和终点坐标。通过监听 navigationStateChanges 流,我们可以获取导航状态的变化。

请注意,你需要替换 'YOUR_MAPBOX_ACCESS_TOKEN' 为你的实际 Mapbox 访问令牌。

此外,这个示例仅展示了基本的导航功能。在实际应用中,你可能需要处理更多的导航事件、错误处理、用户交互等。你可以参考 flutter_mapbox_navigation 的官方文档以获取更多信息和高级功能。

回到顶部