Flutter导航管理插件nb_navigation_flutter的使用

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

Flutter导航管理插件nb_navigation_flutter的使用

简介

Nextbillion Navigation Flutter 是一个用于Flutter应用的地图和导航功能的插件。它提供了地图显示、路线规划、导航等功能,支持Android和iOS平台。本文将详细介绍如何使用该插件,并提供一个完整的示例demo。

前提条件

  • Access Key:需要一个有效的Access Key来初始化插件。
  • Android minSdkVersion 17+
  • iOS 11+
  • Flutter 3.10+
  • Pod 1.11.3+
  • 确保在构建设置中将“Build Libraries for Distribution”设置为No

安装

依赖项

pubspec.yaml文件中添加以下依赖项:

dependencies:
  nb_navigation_flutter: {version}
导入

在代码中导入导航插件:

import 'package:nb_navigation_flutter/nb_navigation_flutter.dart';
初始化

在应用启动时初始化插件:

import 'package:nb_navigation_flutter/nb_navigation_flutter.dart';

class _NavigationDemoState extends State<NavigationDemo> {
  [@override](/user/override)
  void initState() {
    super.initState();
    NextBillion.initNextBillion(YOUR_ACCESS_KEY);
  }
}

必需的权限

Android

AndroidManifest.xml中添加以下权限:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
iOS

Runner/Info.plist中添加以下键值对:

<key>NSLocationWhenInUseUsageDescription</key>
<string>[您的解释]</string>

观察和跟踪用户位置

为了跟踪用户位置,您需要添加回调函数并在地图加载完成后动画化相机到用户位置。

void _onMapCreated(NextbillionMapController controller) {
  this.controller = controller;
}

_onUserLocationUpdate(UserLocation location) {
  currentLocation = location;
}

_onStyleLoadedCallback() {
  if (currentLocation != null) {
    controller?.animateCamera(
      CameraUpdate.newLatLngZoom(currentLocation!.position, 14),
      duration: Duration(milliseconds: 400),
    );
  }
}

NBMap(
  onMapCreated: _onMapCreated,
  onStyleLoadedCallback: _onStyleLoadedCallback,
  initialCameraPosition: const CameraPosition(
    target: LatLng(0, 0),
    zoom: 14.0,
  ),
  trackCameraPosition: true,
  myLocationEnabled: true,
  myLocationTrackingMode: MyLocationTrackingMode.Tracking,
  onUserLocationUpdated: _onUserLocationUpdate,
)

使用

NB Maps

如果您需要使用地图相关功能(例如显示地图小部件),请参考Flutter Maps Plugin

获取路线

您可以使用RouteRequestParams请求路线:

RouteRequestParams requestParams = RouteRequestParams(
  origin: origin,
  destination: dest,
  // waypoints: [waypoint1, waypoint2],
  // language: 'en',
  // alternatives: true,
  // overview: ValidOverview.simplified,
  // avoid: [SupportedAvoid.toll, SupportedAvoid.ferry],
  // option: SupportedOption.flexible,
  // unit: SupportedUnits.imperial,
  // mode: ValidModes.car,
  // geometry: SupportedGeometry.polyline,
);

DirectionsRouteResponse routeResponse = await NBNavigation.fetchRoute(requestParams);
绘制路线

获取路线后,可以在地图上绘制路线:

void _onMapCreated(NextbillionMapController controller) {
  this.controller = controller;
}

void _onStyleLoaded() {
  if (controller != null) async {
    navNextBillionMap = await NavNextBillionMap.create(controller!);
  }
}

navNextBillionMap.drawRoute(routes);
拟合地图相机到路线点
void fitCameraToBounds(List<DirectionsRoute> routes) {
  List<LatLng> multiPoints = [];
  for (var route in routes) {
    var routePoints = decode(route.geometry ?? '', _getDecodePrecision(route.routeOptions));
    multiPoints.addAll(routePoints);
  }
  if (multiPoints.isNotEmpty) {
    var latLngBounds = LatLngBounds.fromMultiLatLng(multiPoints);
    controller?.animateCamera(
      CameraUpdate.newLatLngBounds(latLngBounds, top: 50, left: 50, right: 50, bottom: 50),
    );
  }
}

int _getDecodePrecision(RouteRequestParams? routeOptions) {
  return routeOptions?.geometry == SupportedGeometry.polyline ? PRECISION : PRECISION_6;
}
清除路线
navNextBillionMap.clearRoute();
切换替代路线可见性
navNextBillionMap.toggleAlternativeVisibilityWith(visible);
切换路线持续时间符号可见性
navNextBillionMap.toggleDurationSymbolVisibilityWith(visible);
添加路线选择监听器
onMapClick(Point<double> point, LatLng coordinates) {
  navNextBillionMap.addRouteSelectedListener(coordinates, (selectedRouteIndex) {});
}
开始导航

使用NavigationLauncherConfig开始导航:

NavigationLauncherConfig config = NavigationLauncherConfig(
  route: routes.first,
  routes: routes,
  shouldSimulateRoute: true,
);

NBNavigation.startNavigation(config);
启动嵌入式NavigationView

NBNavigationView是一个可自定义的导航视图小部件,提供无缝的导航体验。

注意事项

如果您想使用NavigationView,需要在Android项目中将MainActivity继承自FlutterFragmentActivity而不是FlutterActivity

class MainActivity: FlutterFragmentActivity() {
}
NBNavigationView构造函数
const NBNavigationView({
  super.key,
  required this.navigationOptions,
  this.onNavigationViewReady,
  this.onProgressChange,
  this.onNavigationCancelling,
  this.onArriveAtWaypoint,
  this.onRerouteFromLocation,
});
参数
  • navigationOptions(必需):提供导航视图的必要配置。
  • onNavigationViewReady:当导航视图准备好时触发的回调。
  • onProgressChange:当导航进度发生变化时触发的回调。
  • onNavigationCancelling:当导航被取消时触发的回调。
  • onArriveAtWaypoint:当到达某个路点时触发的回调。
  • onRerouteFromLocation:当从某个位置重新规划路线时触发的回调。
示例用法
NBNavigationView(
  navigationOptions: NavigationLauncherConfig(
    route: selectedRoute,
    routes: allRoutes,
    themeMode: NavigationThemeMode.system,
  ),
  onNavigationViewReady: (controller) {
    // 处理导航视图准备好的事件
  },
  onProgressChange: (progress) {
    // 处理导航进度变化的事件
  },
  onNavigationCancelling: () {
    // 处理导航取消的事件
  },
  onArriveAtWaypoint: (waypoint) {
    // 处理到达路点的事件
  },
  onRerouteFromLocation: (location) {
    // 处理从某个位置重新规划路线的事件
  },
);

UI组件

您可以自定义NavigationView的样式。

Android

styles.xml中添加自定义样式:

<style name="CustomNavigationViewLight" parent="NavigationViewLight">
  <!-- 自定义导航样式 -->
  <item name="navViewBannerBackground">@color/color</item>
  <item name="navViewBannerPrimaryText">@color/color</item>
  ...
</style>

<style name="CustomNavigationViewDark" parent="NavigationViewDark">
  <item name="navViewBannerBackground">@color/colorAccent</item>
</style>
iOS

AppDelegate.swift中导入nb_navigation_flutter并自定义NavigationView样式:

import nb_navigation_flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)
    customStyle()
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
  
  func customStyle() {
    NavStyleManager.customDayStyle = CustomDayStyle()
    NavStyleManager.customNightStyle = CustomNightStyle()
  }
}

import NbmapNavigation

class CustomDayStyle: DayStyle {
  required init() {
    super.init()
  }
  
  override func apply() {
    super.apply()
    ArrivalTimeLabel.appearance().font = UIFont.systemFont(ofSize: 18, weight: .medium).adjustedFont
    ArrivalTimeLabel.appearance().normalTextColor = #color
    BottomBannerContentView.appearance().backgroundColor = #color
  }
}

class CustomNightStyle: NightStyle {
  required init() {
    super.init()
  }
  
  override func apply() {
    super.apply()
    NavigationMapView.appearance().trafficUnknownColor = UIColor.green
  }
}

运行示例代码

请参考示例代码

完整示例Demo

以下是一个完整的示例demo,展示了如何使用nb_navigation_flutter插件进行导航管理:

import 'package:flutter/material.dart';
import 'package:nb_navigation_flutter/nb_navigation_flutter.dart';
import 'package:permission_handler/permission_handler.dart';

final Map<String, Widget> _allPages = <String, Widget>{
  'Full Navigation Example': const FullNavigationExample(),
  'Launch Embedded Navigation View': const LaunchEmbeddedNavigationView(),
  'Custom View On Navigation View': const CustomViewOnNavigationView(),
  'Launch Navigation': const LaunchNavigation(),
  'Draw Route Line': const DrawRouteLine(),
  'Draw Route Line With Raw JSON': const DrawRouteLineWithRawJson(),
  'Track Current Location': const TrackCurrentLocation(),
  'Route Line Style': const RouteLineStyle(),
  'Custom Navigation Style': const CustomNavigationStyle(),
  'Map View Style': const MapViewStyle(),
  'Navigation Theme': const NavigationTheme(),
  'Embedded Navigation View Integration': const EmbeddedNavigationViewIntegration(),
};

class NavigationDemo extends StatefulWidget {
  static const String accessKey = String.fromEnvironment("ACCESS_KEY");

  const NavigationDemo({super.key});

  [@override](/user/override)
  State<NavigationDemo> createState() => _NavigationDemoState();
}

class _NavigationDemoState extends State<NavigationDemo> {
  [@override](/user/override)
  void initState() {
    super.initState();
    NBNavigation.initNextBillion(NavigationDemo.accessKey);

    // Set user ID If needed
    NBNavigation.setUserId("123344").then((value) {});

    // Check user ID If needed
    NBNavigation.getUserId().then((value) {});

    // Get NB ID If needed
    NBNavigation.getNBId().then((value) {});
  }

  void _pushPage(BuildContext context, Widget page, bool isRequiredPermission) async {
    if (!mounted) {
      return;
    }

    if (isRequiredPermission) {
      var status = await Permission.location.status;

      if (!mounted) {
        return;
      }

      if (status.isDenied) {
        await [Permission.location].request();

        if (!mounted) {
          return;
        }
      }
    }

    WidgetsBinding.instance.addPostFrameCallback((_) {
      Navigator.push(
        context,
        MaterialPageRoute(
          builder: (context) => Builder(
            builder: (newContext) => page,
          ),
        ),
      );
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('NBNavigation examples')),
      body: NavigationDemo.accessKey.isEmpty
          ? buildAccessTokenWarning()
          : ListView.separated(
              itemCount: _allPages.length,
              separatorBuilder: (BuildContext context, int index) => const Divider(height: 1),
              itemBuilder: (_, int index) => ListTile(
                title: Text(_allPages.keys.toList()[index]),
                onTap: () {
                  _pushPage(context, _allPages.values.toList()[index], true);
                },
              ),
            ),
    );
  }

  Widget buildAccessTokenWarning() {
    return Container(
      color: Colors.red[900],
      child: SizedBox.expand(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            "Using MapView requires calling Nextbillion.initNextbillion(String accessKey) "
                "before inflating or creating NBMap Widget. ",
          ]
              .map((text) => Padding(
                    padding: const EdgeInsets.all(8),
                    child: Text(text,
                        textAlign: TextAlign.center,
                        style: const TextStyle(
                            fontSize: 14,
                            fontWeight: FontWeight.bold,
                            color: Colors.white)),
                  ))
              .toList(),
        ),
      ),
    );
  }
}

void main() {
  runApp(const MaterialApp(home: NavigationDemo()));
}

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

1 回复

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


当然,下面是一个关于如何使用nb_navigation_flutter插件进行Flutter导航管理的代码案例。nb_navigation_flutter是一个用于简化Flutter应用中的导航管理的插件。

首先,确保你已经在pubspec.yaml文件中添加了nb_navigation_flutter依赖:

dependencies:
  flutter:
    sdk: flutter
  nb_navigation_flutter: ^最新版本号  # 请替换为最新版本号

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

接下来,让我们看一下如何在Flutter应用中使用nb_navigation_flutter进行导航管理。

1. 设置导航服务

在你的应用入口文件(通常是main.dart)中,设置导航服务:

import 'package:flutter/material.dart';
import 'package:nb_navigation_flutter/nb_navigation_flutter.dart';
import 'routes.dart'; // 假设你有一个routes.dart文件来定义路由

void main() {
  // 初始化导航服务
  final navService = NavigationService();

  // 注册路由
  navService.registerRoutes(routes);

  runApp(
    MaterialApp.router(
      routeInformationParser: navService.routeInformationParser,
      routerDelegate: navService.routerDelegate,
    ),
  );
}

2. 定义路由

routes.dart文件中定义你的路由:

import 'package:nb_navigation_flutter/nb_navigation_flutter.dart';
import 'home_screen.dart';
import 'details_screen.dart';

final routes = <String, WidgetBuilder>{
  '/': (context) => HomeScreen(),
  '/details': (context) => DetailsScreen(),
};

3. 创建屏幕

创建两个简单的屏幕:home_screen.dartdetails_screen.dart

home_screen.dart:

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

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final navService = NavigationService.of(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('Home Screen'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            navService.navigateTo('/details');
          },
          child: Text('Go to Details'),
        ),
      ),
    );
  }
}

details_screen.dart:

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

class DetailsScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final navService = NavigationService.of(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('Details Screen'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            navService.navigateBack();
          },
          child: Text('Go Back'),
        ),
      ),
    );
  }
}

4. 使用导航服务

在上面的代码中,我们使用了NavigationService.of(context)来获取导航服务的实例,并通过navigateTonavigateBack方法来进行导航。

注意事项

  • 确保你已经正确导入了nb_navigation_flutter包。
  • NavigationService.of(context)需要在MaterialApp.router的上下文中使用。
  • 路由的路径和WidgetBuilder需要正确匹配。

这样,你就完成了使用nb_navigation_flutter插件进行Flutter导航管理的基本设置。这个插件使得管理复杂的导航变得更加简单和直观。

回到顶部