Flutter室内地图导航插件mapsindoors_googlemaps的使用
Flutter室内地图导航插件mapsindoors_googlemaps的使用
简介
mapsindoors_googlemaps
是一个Flutter插件,用于集成原生的MapsIndoors SDK,支持在Android和iOS平台上展示室内地图和导航。
支持平台
平台 | Android | iOS |
---|---|---|
支持 | SDK 21+ | 14.0+ |
功能
- 展示室内地图和导航。
- 执行实时路径规划。
- 查看位置的实时更新。
该插件基于MapsIndoors V4 SDK for Android和iOS。
快速开始
添加依赖
在 pubspec.yaml
文件中添加 mapsindoors_googlemaps
:
dependencies:
mapsindoors_googlemaps: ^4.1.0
Android设置
Google Maps设置
为了使底层的Google Map正常工作,请执行以下步骤:
- 导航到
android/app/src/main/res/values
。 - 在此文件夹中创建一个名为
google_maps_api_key.xml
的文件。 - 复制并粘贴以下代码片段,并将
YOUR_KEY_HERE
替换为您的Google Maps API密钥。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="google_maps_key">YOUR_KEY_HERE</string>
</resources>
Gradle设置
确保插件能够解析其MapsIndoors Gradle依赖项,请执行以下操作:
- 导航到应用程序项目的项目级
build.gradle
。 - 将
maven { url 'https://maven.mapsindoors.com/' }
添加到allprojects/repositories
中,位于mavenCentral()
之后。
allprojects {
repositories {
google()
mavenCentral()
maven { url 'https://maven.mapsindoors.com/' }
}
}
iOS设置
Google Maps设置
为了使底层的Google Map正常工作,请执行以下步骤:
- 获取Google Maps的API密钥:获取API密钥。
- 导航到
iOS/Runner/Info.plist
。 - 添加一个新的键
GoogleMapsAPIKey
,内容为您的Google Maps API密钥。
使用方法
以下部分提供了代码示例,展示了如何完成以下任务:
显示地图
以下代码片段展示了如何在Flutter应用程序中设置 MapsIndoors
。首先,将 MapsIndoorsWidget
添加到应用程序的构建树中。
可选地,我们可以添加一个 MPFloorSelector
到地图中。这里我们使用 MPDefaultFloorSelector
,因为它包含在MapsIndoors包中。选择器必须同时添加到构建树以及 MapControl
中才能正常工作。
一旦调用 initState()
,MapsIndoors
开始初始化,成功完成后,MapControl
开始初始化。
初始化完成后,可以调用 goTo
方法将相机移动到默认场地。
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, 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: $err");
}
}
}
显示路线
以下代码片段初始化了 MPDirectionsService
和 MPDirectionsRenderer
类,并使用已经初始化的 _mapControl
。
showRouteToLocation
函数用于查询从用户当前位置到指定位置的路线。
如果成功,则使用 directionsRenderer.setRoute(route)
在地图上显示路线。
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: _userPosition, destination: location.point).then((route) {
directionsRenderer.setRoute(route);
}).catchError((err) => print("An error occured: $err"));
}
搜索位置
以下代码片段展示了如何使用 MapsIndoors
搜索匹配查询字符串 "parking"
的位置。
它会在位置的描述、名称和外部ID中进行匹配。搜索完成后,可以从这些位置获取信息(未在代码片段中指定)。
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 near the point: ${locations?.length}");
// do something with the locations
});
}
更改外观
以下代码片段展示了三种方式来操作显示规则:
hideLocationsByDefault()
方法隐藏所有未明确可见的标记。showLocationsByDefault()
方法确保所有标记都显示。changeTypePolygonColor(String type, Color color)
方法更改特定类型的多边形填充颜色。
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);
}
示例Demo
以下是一个完整的示例,展示了如何在Flutter应用中使用 mapsindoors_googlemaps
插件:
import 'package:flutter/material.dart';
import 'package:mapsindoors_googlemaps/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: <Widget>[
_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));
}
}
以上是关于 mapsindoors_googlemaps
插件的基本使用方法和完整示例代码。希望对您有所帮助!
更多关于Flutter室内地图导航插件mapsindoors_googlemaps的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html