Get started with Woosmap for Flutter
Get access to Woosmap services for your native mobile developments on hybrid Flutter development.
- Creating your first Application
- Android Platform Settings
- How to display Map
- Accessing various map functions
- Customize loader
The Woosmap Flutter plugin is a library that embeds interactive maps directly into your application. You can also add a stores overlay to the map to call out points of interest and get relevant information.
The SDK offers an interface to manage the Indoor Mapview and subscribe to events on the map.
Android | iOS | |
---|---|---|
Support SDK | 33+ | 13.0+ |
Creating your first Application
Create your new flutter application as
flutter create --platforms=ios,android woosmap
cd woosmap
flutter pub add woosmap_flutter
flutter pub get
Create your API Key
In order to use our service from a mobile, you will need to create a Private API Key with the proper restriction in the console. If you don’t know how, you can follow the directions specified in the API Key documentation
Android Platform Settings
This plugin uses Platform Views to embed the Android’s WebView within the Flutter app.
You should however make sure to set the correct minSdkVersion
in android/app/build.gradle
if it was previously lower than 19:
android {
compileSdkVersion 33
defaultConfig {
minSdkVersion 19
targetSdkVersion 33
}
}
Add the following permission inside the manifest tag in the AndroidManifest.xml
file located at android/app/src/main
.
<uses-permission android:name="android.permission.INTERNET"/>
Once you create the application and install the woosmap_flutter
plugin, you are ready to use the Woosmap feature within the application.
How to display Map
- Instantiating a WoosmapMapViewWidget.
WoosmapMapViewWidget.create(
wooskey: Platform.isAndroid ? "<<YOUR ANDROID PRIVATE KEY>>" : "<<YOUR IOS PRIVATE KEY>>",
onRef: (p0) async {
_controller = p0;
},
mapOptions: MapOptions(
center: LatLng(lat: 19.115932, lng: 72.907852),
zoom: 10
),
click: (message) {
debugPrint("idle");
},
bounds_changed: () {
debugPrint("idle");
},
center_changed: () {
debugPrint("idle");
},
dblclick: (m) {
debugPrint("idle");
},
drag: () {
debugPrint("idle");
},
dragend: () {
debugPrint("idle");
},
dragstart: () {
debugPrint("idle");
},
idle: () {
debugPrint("idle");
},
mousemove: (x) {
debugPrint("idle");
},
mouseout: (x) {
debugPrint("idle");
},
mouseover: (x) {
debugPrint("idle");
},
rightclick: (x) {
debugPrint("idle");
},
zoom_changed: () {
debugPrint("idle");
},
)
import 'package:flutter/widgets.dart';
class AppConstants extends InheritedWidget {
static AppConstants? of(BuildContext context) =>
context.dependOnInheritedWidgetOfExactType<AppConstants>();
const AppConstants({required super.child, super.key});
final String privateKeyiOS = "<<Your private iOS woosmap key>>";
final String privateKeyAndroid = "<<Your private Android woosmap key>>";
@override
bool updateShouldNotify(AppConstants oldWidget) => false;
}
import 'dart:convert';
import 'dart:core';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:woosmap_flutter/woosmap_flutter.dart';
import './constants.dart';
class MapEventsSnippet extends StatefulWidget {
const MapEventsSnippet({super.key});
@override
State<MapEventsSnippet> createState() => _MapEventsSnippetState();
}
class _MapEventsSnippetState extends State<MapEventsSnippet> {
WoosmapController? _controller;
TextEditingController? txtLogController;
@override
void initState() {
super.initState();
txtLogController = TextEditingController();
if (_controller != null) {
debugPrint("info ===> Indoor controller not initialize");
}
}
@override
void dispose() {
txtLogController?.dispose();
super.dispose();
}
Future<void> reloadMenu() async {
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Woosmap Map (Events)'),
actions: <Widget>[
MapEventsMenu(webViewController: _controller),
],
),
body: SafeArea(
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: Align(
alignment: const AlignmentDirectional(-1, -1),
child: WoosmapMapViewWidget.create(
wooskey: Platform.isAndroid
? AppConstants.of(context)?.privateKeyAndroid ?? ""
: AppConstants.of(context)?.privateKeyiOS ?? "",
onRef: (p0) async {
_controller = p0;
reloadMenu();
},
mapOptions: MapOptions(
center: LatLng(lat: 19.115932, lng: 72.907852),
zoom: 10),
click: (message) {
txtLogController?.text =
"Click, ${txtLogController?.text}";
},
bounds_changed: () {
txtLogController?.text =
"bounds_changed, ${txtLogController?.text}";
},
center_changed: () {
txtLogController?.text =
"center_changed, ${txtLogController?.text}";
},
dblclick: (m) {
txtLogController?.text =
"dblclick, ${txtLogController?.text}";
},
drag: () {
txtLogController?.text =
"drag, ${txtLogController?.text}";
},
dragend: () {
txtLogController?.text =
"dragend, ${txtLogController?.text}";
},
dragstart: () {
txtLogController?.text =
"dragstart, ${txtLogController?.text}";
},
idle: () {
debugPrint("idle");
/*txtLogController?.text =
"idle, ${txtLogController?.text}";*/
},
mousemove: (x) {
txtLogController?.text =
"mousemove, ${txtLogController?.text}";
},
mouseout: (x) {
txtLogController?.text =
"mouseout, ${txtLogController?.text}";
},
mouseover: (x) {
txtLogController?.text =
"mouseover, ${txtLogController?.text}";
},
rightclick: (x) {
txtLogController?.text =
"rightclick, ${txtLogController?.text}";
},
zoom_changed: () {
txtLogController?.text =
"zoom_changed, ${txtLogController?.text}";
},
))),
Container(
width: 100,
height: 100,
decoration: const BoxDecoration(
color: Color(0xFFDEE6E6),
),
child: Column(mainAxisSize: MainAxisSize.max, children: [
Expanded(
child: Padding(
padding: const EdgeInsetsDirectional.fromSTEB(10, 5, 10, 5),
child: TextFormField(
controller: txtLogController,
minLines: null,
maxLines: null,
autofocus: true,
enabled: false,
obscureText: false,
decoration: const InputDecoration(
labelText: 'Events Log\n',
hintText: 'Event fired',
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Color(0x00000000),
width: 1,
),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(4.0),
topRight: Radius.circular(4.0),
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Color(0x00000000),
width: 1,
),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(4.0),
topRight: Radius.circular(4.0),
),
),
errorBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Color(0x00000000),
width: 1,
),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(4.0),
topRight: Radius.circular(4.0),
),
),
focusedErrorBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Color(0x00000000),
width: 1,
),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(4.0),
topRight: Radius.circular(4.0),
),
),
),
),
))
]))
],
),
),
);
}
}
enum EventMenuOptions {
fitBounds,
getBounds,
getCenter,
getHeading,
getTilt,
getZoom,
panBy,
panTo,
panToBounds,
setCenter,
setHeading,
setTilt,
setZoom,
}
class MapEventsMenu extends StatelessWidget {
const MapEventsMenu({
super.key,
required this.webViewController,
});
final WoosmapController? webViewController;
@override
Widget build(BuildContext context) {
return PopupMenuButton<EventMenuOptions>(
key: const ValueKey<String>('ShowPopupMenu'),
onSelected: (EventMenuOptions value) {
switch (value) {
case EventMenuOptions.fitBounds:
_onFitBounds();
break;
case EventMenuOptions.getBounds:
_onGetBounds().then((value) {
if (value != null) {
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(jsonEncode(value.toJson()))),
);
}
});
break;
case EventMenuOptions.getCenter:
_onGetCenter().then((value) {
if (value != null) {
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(jsonEncode(value.toJson()))),
);
}
});
break;
case EventMenuOptions.getHeading:
_onHeading().then((value) {
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(jsonEncode(value))),
);
});
break;
case EventMenuOptions.getTilt:
_onTilt().then((value) {
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(jsonEncode(value))),
);
});
break;
case EventMenuOptions.getZoom:
_onZoom().then((value) {
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Zoom ${jsonEncode(value)}')),
);
});
break;
case EventMenuOptions.panBy:
_onPanBy();
break;
case EventMenuOptions.panTo:
_onPanTo();
break;
case EventMenuOptions.panToBounds:
_onPanToBounds();
break;
case EventMenuOptions.setCenter:
_onSetCenter();
break;
case EventMenuOptions.setHeading:
_onSetHeading();
break;
case EventMenuOptions.setTilt:
_onSetTilt();
break;
case EventMenuOptions.setZoom:
_onSetZoom();
break;
}
},
itemBuilder: (BuildContext context) => <PopupMenuItem<EventMenuOptions>>[
const PopupMenuItem<EventMenuOptions>(
value: EventMenuOptions.fitBounds,
child: Text('fitBounds'),
),
const PopupMenuItem<EventMenuOptions>(
value: EventMenuOptions.getBounds,
child: Text('getBounds'),
),
const PopupMenuItem<EventMenuOptions>(
value: EventMenuOptions.getCenter,
child: Text('getCenter'),
),
const PopupMenuItem<EventMenuOptions>(
value: EventMenuOptions.getHeading,
child: Text('getHeading'),
),
const PopupMenuItem<EventMenuOptions>(
value: EventMenuOptions.getTilt,
child: Text('getTilt'),
),
const PopupMenuItem<EventMenuOptions>(
value: EventMenuOptions.getZoom,
child: Text('getZoom'),
),
const PopupMenuItem<EventMenuOptions>(
value: EventMenuOptions.panBy,
child: Text('panBy'),
),
const PopupMenuItem<EventMenuOptions>(
value: EventMenuOptions.panTo,
child: Text('panTo'),
),
const PopupMenuItem<EventMenuOptions>(
value: EventMenuOptions.panToBounds,
child: Text('panToBounds'),
),
const PopupMenuItem<EventMenuOptions>(
value: EventMenuOptions.setCenter,
child: Text('setCenter'),
),
const PopupMenuItem<EventMenuOptions>(
value: EventMenuOptions.setHeading,
child: Text('setHeading'),
),
const PopupMenuItem<EventMenuOptions>(
value: EventMenuOptions.setTilt,
child: Text('setTilt'),
),
const PopupMenuItem<EventMenuOptions>(
value: EventMenuOptions.setZoom,
child: Text('setZoom'),
),
],
);
}
Future<void> _onFitBounds() async {
await webViewController?.fitBounds(
LatLngBounds(
ne: LatLng(lat: 48.844437932920535, lng: 2.3743880269761393),
sw: LatLng(lat: 48.854437932920535, lng: 2.3843880269761393)),
WoosPadding(top: 2, left: 2, right: 3, bottom: 3));
return;
}
Future<LatLng?> _onGetCenter() async {
LatLng? result;
result = await webViewController?.getCenter();
return result;
}
Future<LatLngBounds?> _onGetBounds() async {
LatLngBounds? result;
result = await webViewController?.getBounds(
WoosPadding(top: 2, left: 2, right: 3, bottom: 3));
return result;
}
Future<double> _onHeading() async {
double result;
result = (await webViewController?.getHeading()) as double;
return result;
}
Future<double> _onTilt() async {
double result;
result = (await webViewController?.getTilt()) as double;
return result;
}
Future<double> _onZoom() async {
double result;
result = (await webViewController?.getZoom()) as double;
return result;
}
Future<void> _onPanBy() async {
await webViewController?.panBy(20, 10);
return;
}
Future<void> _onPanTo() async {
await webViewController?.panTo(
LatLng(lat: 48.844437932920535, lng: 2.3743880269761393),
WoosPadding(top: 2, left: 2, right: 3, bottom: 3));
return;
}
Future<void> _onPanToBounds() async {
await webViewController?.panToBounds(
LatLngBounds(
ne: LatLng(lat: 48.844437932920535, lng: 2.3743880269761393),
sw: LatLng(lat: 48.854437932920535, lng: 2.3843880269761393)
),
WoosPadding(top: 2, left: 2, right: 3, bottom: 3));
return;
}
Future<void> _onSetCenter() async {
await webViewController?.setCenter(
LatLng(lat: 48.844437932920535, lng: 2.3743880269761393),
WoosPadding(top: 2, left: 2, right: 3, bottom: 3));
return;
}
Future<void> _onSetHeading() async {
await webViewController?.setHeading(20);
return;
}
Future<void> _onSetTilt() async {
await webViewController?.setTilt(5);
return;
}
Future<void> _onSetZoom() async {
await webViewController?.setZoom(20);
return;
}
}
Accessing various map functions
- fitBounds: Sets the viewport to contain the given bounds.
await _controller.fitBounds(
LatLngBounds(
ne: LatLng(lat: 48.844437932920535, lng: 2.3743880269761393),
sw: LatLng(lat: 48.854437932920535, lng: 2.3843880269761393)
),
WoosPadding(top: 2, left: 2, right: 3, bottom: 3));
- getCenter: Returns the position displayed at the center of the map.
LatLng result = await _controller.getCenter();
- getBounds: Returns the lat/lng bounds of the current viewport. Optionally takes a padding parameter.
LatLngBounds result = await _controller.getBounds(
WoosPadding(top: 2, left: 2, right: 3, bottom: 3));
- getHeading: Returns the compass heading. The heading value is measured in degrees (clockwise) from cardinal direction North.
double result = (await _controller.getHeading()) as double;
- getTilt: Returns the current angle of incidence of the map, in degrees from the viewport plane to the map plane
double result = (await _controller.getTilt()) as double;
- getZoom: Returns the current angle of incidence of the map, in degrees from the viewport plane to the map plane.
double result = (await _controller.getZoom()) as double;
- panBy: Changes the center of the map by the given distance in pixels
await _controller.panBy(20, 10);
- panTo: Changes the center of the map to the given LatLng.
await _controller.panTo(
LatLng(lat: 48.844437932920535, lng: 2.3743880269761393),
WoosPadding(top: 2, left: 2, right: 3, bottom: 3));
- panToBounds: Pans the map by the minimum amount necessary to contain the given LatLngBounds. It makes no guarantee where on the map the bounds will be, except that the map will be panned to show as much of the bounds as possible inside {currentMapSizeInPx} - {padding}.
await _controller.panToBounds(
LatLngBounds(
ne: LatLng(lat: 48.844437932920535, lng: 2.3743880269761393),
sw: LatLng(lat: 48.854437932920535, lng: 2.3843880269761393)
),
WoosPadding(top: 2, left: 2, right: 3, bottom: 3));
- setCenter: Sets the map center
await _controller.setCenter(
LatLng(lat: 48.844437932920535, lng: 2.3743880269761393),
WoosPadding(top: 2, left: 2, right: 3, bottom: 3));
- setHeading: Sets the compass heading for map measured in degrees from cardinal direction North.
await _controller.setHeading(20);
- setTilt: Sets tilt of map
await _controller.setTilt(5);
- setZoom: Set zoom level of map
await _controller.setZoom(20);
Customize loader
Plugin allows customization of the loader on the map. To change the loader, you need to add a gif image to the assets or images folder and update the loader setting in the widget as shown below.
WoosmapMapViewWidget.create(
wooskey: "<<YOUR WOOSMAP KEY>>",
onRef: (p0) async {
_controller = p0;
},
loader: const AssetImage("assets/spinner.gif"),
....
....
....
)