Flutter室内地图导航插件mapsindoors_mapbox的使用
Flutter室内地图导航插件mapsindoors_mapbox的使用
介绍
mapsindoors_mapbox
是一个用于在 Flutter 应用中集成原生 MapsIndoors SDK 的联邦插件。通过该插件,你可以实现室内地图显示、实时路径导航和位置实时更新等功能。
支持平台
平台 | Android | iOS |
---|---|---|
支持 | SDK 21+ | 14.0+ |
特性
使用此插件可以:
- 显示室内地图和导航。
- 实现实时路径导航。
- 查看位置实时更新。
此插件基于 MapsIndoors V4 SDK for Android 和 iOS。
入门指南
添加依赖
在你的 pubspec.yaml
文件中添加 mapsindoors_mapbox
依赖:
dependencies:
mapsindoors_mapbox: ^4.1.0
Android 配置
设置 Mapbox API Key
- 导航到
android/app/src/main/res/value
- 在此文件夹中创建一个名为
mapbox_api_key.xml
的文件 - 复制并粘贴以下代码片段,并将
YOUR_KEY_HERE
替换为你的 Mapbox API Key
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="mapbox_api_key" translatable="false">YOUR_KEY_HERE</string>
<string name="mapbox_access_token" translatable="false">YOUR_KEY_HERE</string>
</resources>
MapsIndoors Gradle 配置
- 导航到应用的项目级别
build.gradle
文件 - 在
allprojects/repositories
中添加以下内容,放在mavenCentral()
之后
maven { url 'https://maven.mapsindoors.com/' }
maven {
url 'https://api.mapbox.com/downloads/v2/releases/maven'
authentication {
basic(BasicAuthentication)
}
credentials {
// 不要更改用户名,始终应为 `mapbox`(而不是你的用户名)
username = "mapbox"
// 使用你在 `gradle.properties` 中存储的秘密令牌作为密码
password = project.properties['MAPBOX_DOWNLOADS_TOKEN'] ?: ""
}
}
- 将你的 Mapbox 下载令牌添加到顶级
gradle.properties
文件中
MAPBOX_DOWNLOADS_TOKEN=YOUR_KEY_HERE
iOS 配置
确保你的 Podfile 配置为 iOS 14,并在应用目标中添加 use_frameworks!
:
platform :ios, '14.0'
target 'MyApp' do
use_frameworks!
...
end
提供 API Key
导航到你的应用设置,并将你的 Mapbox 公共访问令牌添加到 Info.plist 中,键为 MBXAccessToken
。同时,设置你的秘密访问令牌以下载 SDK。详情请参阅 配置凭证。
使用示例
显示地图
以下代码展示了如何在 Flutter 应用中设置 MapsIndoors
。首先,将 MapsIndoorsWidget
添加到应用的构建树中。
import 'package:flutter/material.dart';
import 'package:mapsindoors/mapsindoors.dart' as MapsIndoors;
import 'package:mapsindoors/mapsindoors_library.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: MapWidget("demo"),
);
}
}
class MapWidget extends StatefulWidget {
final String apiKey;
const MapWidget({Key? key, required this.apiKey}) : super(key: key);
@override
MapWidgetState createState() => MapWidgetState();
}
class MapWidgetState extends State<MapWidget> {
final _floorSelectorWidget = MPDefaultFloorSelector();
late final MapsIndoorsWidget _mapController;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: _mapController = MapsIndoorsWidget(
floorSelector: _floorSelectorWidget,
initialCameraPosition: MPCameraPosition(zoom: 7, point: MPPoint.withCoordinates(longitude: -118.0165, latitude: 33.9457)),
readyListener: _mapControlReadyListener,
),
),
);
}
@override
void initState() {
super.initState();
loadMapsIndoors(widget.apiKey);
}
void _mapControlReadyListener(MPError? error) async {
if (error == null) {
_mapController.goTo(await getDefaultVenue());
} else {
print("Creating mapcontrol faced an issue: $error");
}
}
}
显示路线
以下代码初始化了 MPDirectionsService
和 MPDirectionsRenderer
类,并使用已初始化的 _mapControl
查询从用户当前位置到指定位置的路线。
late final _mapControl;
var _userLocation = MPPoint.withCoordinates(longitude: -98.44, latitude: 35.16);
void showRouteToLocation(MPLocation location) async {
final directionsService = MPDirectionsService();
final directionsRenderer = MPDirectionsRenderer();
directionsService.getRoute(origin: _userLocation, destination: location.point).then((route) {
directionsRenderer.setRoute(route);
}).catchError((err) => print("An error occurred: $err"));
}
搜索地点
以下代码展示了如何搜索匹配查询字符串 “parking” 的地点。
void searchForParking(MPPoint point) {
final mpq = MPQuery.builder()
..setQuery("parking")
..setNear(point)
..setQueryProperties([MPLocationPropertyNames.description.name, MPLocationPropertyNames.name.name, MPLocationPropertyNames.externalId.name]);
getLocationsByQuery(query: mpq.build()).then((locations) {
print("Number of parking spots near the point: ${locations?.length}");
// 处理找到的地点
});
}
自定义显示规则
以下代码展示了如何操作显示规则,例如隐藏所有标记、显示所有标记以及更改特定类型的多边形颜色。
void hideLocationsByDefault() async {
final MPDisplayRule? main = await getMainDisplayRule();
main?.setVisible(false);
}
void showLocationsByDefault() async {
final MPDisplayRule? main = await getMainDisplayRule();
main?.setVisible(true);
}
void changeTypePolygonColor(String type, Color color) async {
final MPDisplayRule? rule = await getDisplayRuleByName(type);
rule?.setPolygonFillColor(color);
}
完整示例
以下是一个完整的示例,展示了如何在 Flutter 应用中使用 mapsindoors_mapbox
插件。
import 'package:flutter/material.dart';
import 'package:mapsindoors_mapbox/mapsindoors.dart';
import 'package:mapsindoors_example/route_handler.dart';
import 'package:mapsindoors_example/example_position_provider.dart';
void main() {
runApp(const MapsIndoorsDemoApp());
}
class MapsIndoorsDemoApp extends StatelessWidget {
const MapsIndoorsDemoApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter MapsIndoors Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const Map(apiKey: 'demo'),
);
}
}
class Map extends StatefulWidget {
const Map({super.key, required this.apiKey});
final String apiKey;
@override
State<Map> createState() => _MapState();
}
class _MapState extends State<Map> {
final _scaffoldKey = GlobalKey<ScaffoldState>();
PersistentBottomSheetController? _controller;
late MapsIndoorsWidget _mapControl;
final _positionProvider = ExamplePositionProvider();
List<MPLocation> _searchResults = [];
final _userPosition = MPPoint.withCoordinates(
longitude: -77.03740973527613,
latitude: 38.897389429704695,
floorIndex: 0);
RouteHandler? _routeHandler;
@override
void initState() {
super.initState();
loadMapsIndoors(widget.apiKey).then((error) {
if (error == null) {
setPositionProvider(_positionProvider);
}
});
}
void onMapControlReady(MPError? error) async {
if (error == null) {
_mapControl
..setOnLocationSelectedListener(onLocationSelected, false)
..goTo(await getDefaultVenue());
_positionProvider.updatePosition(_userPosition, accuracy: 25, bearing: 0.0, floorIndex: 0);
} else {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text("Map load failed: $error"),
backgroundColor: Colors.red,
));
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: SearchWidget(
onSubmitted: search,
),
),
drawer: Drawer(
child: Flex(
direction: Axis.vertical,
children: [
Expanded(
child: _searchResults.isNotEmpty
? ListView.builder(
itemBuilder: (ctx, i) {
return ListTile(
onTap: () {
_mapControl.selectLocation(_searchResults[i]);
_scaffoldKey.currentState?.closeDrawer();
},
title: Text(_searchResults[i].name),
);
},
itemCount: _searchResults.length,
)
: const Icon(Icons.search_off, color: Colors.black, size: 100.0),
),
],
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
_mapControl = MapsIndoorsWidget(
readyListener: onMapControlReady,
),
],
),
),
);
}
void search(String value) {
if (value.isEmpty) {
_mapControl.clearFilter();
setState(() {
_searchResults = [];
});
return;
}
MPQuery query = (MPQueryBuilder()..setQuery(value)).build();
MPFilter filter = (MPFilterBuilder()..setTake(30)).build();
final scaffold = ScaffoldMessenger.of(context);
getLocationsByQuery(query: query, filter: filter).then((locations) {
if (locations != null && locations.isNotEmpty) {
setState(() {
_searchResults = locations;
_scaffoldKey.currentState?.openDrawer();
});
_mapControl.setFilterWithLocations(locations, MPFilterBehavior.DEFAULT);
}
}).catchError((err) {
if (mounted) return;
scaffold.showSnackBar(SnackBar(
content: Text("Search failed: $err"),
backgroundColor: Colors.red,
));
});
}
void enableLiveData() {
_mapControl
..enableLiveData(LiveDataDomainTypes.availability.name)
..enableLiveData(LiveDataDomainTypes.occupancy.name)
..enableLiveData(LiveDataDomainTypes.position.name);
}
void onLocationSelected(MPLocation? location) {
if (location == null) {
_controller?.close();
_controller = null;
return;
}
_routeHandler?.removeRoute();
_controller = _scaffoldKey.currentState?.showBottomSheet((context) {
return Container(
color: Colors.white,
padding: const EdgeInsets.only(bottom: 50.0, left: 100, right: 100),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 30),
Text(location.name),
const SizedBox(height: 30),
Text("Description: ${location.description}"),
const SizedBox(height: 30),
Text("Building: ${location.buildingName} - ${location.floorName}"),
const SizedBox(height: 30),
ElevatedButton(
onPressed: () => _routeHandler = RouteHandler(
origin: _userPosition,
destination: location.point,
scaffold: _scaffoldKey.currentState!),
child: const Row(
children: [
Icon(Icons.keyboard_arrow_left_rounded),
SizedBox(width: 5),
Text("directions")
],
),
),
],
),
);
});
_controller?.closed.then((value) => _mapControl.selectLocation(null));
}
}
以上代码展示了如何在 Flutter 应用中使用 mapsindoors_mapbox
插件来实现室内地图显示、路径导航和地点搜索功能。希望这些示例对你有所帮助!
更多关于Flutter室内地图导航插件mapsindoors_mapbox的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter室内地图导航插件mapsindoors_mapbox的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter项目中使用mapsindoors_mapbox
插件来实现室内地图导航的示例代码。mapsindoors_mapbox
是一个用于在Flutter应用中集成室内地图导航功能的插件,基于Mapbox技术。
首先,确保你的Flutter项目已经创建并配置好。然后,按照以下步骤集成mapsindoors_mapbox
插件。
1. 添加依赖
在你的pubspec.yaml
文件中添加mapsindoors_mapbox
依赖:
dependencies:
flutter:
sdk: flutter
mapsindoors_mapbox: ^最新版本号 # 请替换为实际的最新版本号
然后运行flutter pub get
来安装依赖。
2. 配置Mapbox和MapsIndoors
你需要在Mapbox和MapsIndoors网站上注册并获取API密钥和配置信息。假设你已经完成了这些步骤,并获得了以下信息:
- Mapbox Access Token
- MapsIndoors Venue ID
- MapsIndoors Venue Key
3. 初始化插件
在你的Flutter应用的main.dart
文件中,初始化mapsindoors_mapbox
插件:
import 'package:flutter/material.dart';
import 'package:mapsindoors_mapbox/mapsindoors_mapbox.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter MapsIndoors Mapbox Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MapsIndoorsScreen(),
);
}
}
class MapsIndoorsScreen extends StatefulWidget {
@override
_MapsIndoorsScreenState createState() => _MapsIndoorsScreenState();
}
class _MapsIndoorsScreenState extends State<MapsIndoorsScreen> {
@override
void initState() {
super.initState();
// 初始化MapsIndoors
MapsIndoorsMapbox.init(
accessToken: '你的Mapbox Access Token',
venueId: '你的MapsIndoors Venue ID',
venueKey: '你的MapsIndoors Venue Key',
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Indoor Map Navigation'),
),
body: MapsIndoorsMapboxWidget(
// 可以添加其他配置参数
),
);
}
}
4. 运行应用
确保你已经正确配置了所有必要的API密钥和ID,然后运行你的Flutter应用:
flutter run
如果一切顺利,你应该能够在你的Flutter应用中看到一个室内地图。
5. 自定义和扩展
mapsindoors_mapbox
插件提供了丰富的API,你可以根据需要进行自定义和扩展。例如,你可以添加室内定位、路径规划、标注等功能。
示例:添加标注
以下是如何在地图上添加标注的示例代码:
import 'package:mapsindoors_mapbox/mapsindoors_mapbox.dart' show MapFeature;
// ...
class _MapsIndoorsScreenState extends State<MapsIndoorsScreen> {
// ...
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Indoor Map Navigation'),
),
body: MapsIndoorsMapboxWidget(
onMapReady: () {
// 添加标注
MapsIndoorsMapbox.addMapFeature(
MapFeature(
id: 'my-feature-id',
geometry: GeoJsonPoint(
coordinates: [经度, 纬度], // 替换为实际的经纬度
),
properties: {
'name': 'My Feature',
},
),
);
},
),
);
}
}
在这个示例中,我们使用了onMapReady
回调来确保地图已经加载完毕,然后添加了一个标注。
请注意,这只是一个基本的示例,实际项目中可能需要更多的配置和处理。建议查阅mapsindoors_mapbox
的官方文档以获取更多详细信息。