Nextbillion Maps Flutter
Add the following dependency to your project pubspec.yaml file to use the NB Maps Flutter Plugin add the dependency to the pubspec.yaml:
dependencies:
nb_maps_flutter: version
Import the maps plugin in your code
import 'package:nb_maps_flutter/nb_maps_flutter.dart';
To run the Maps Flutter Plugin you will need to configure the NB Maps Token at the beginning of your flutter app using NextBillion.initNextBillion(YOUR_ACCESS_KEY)
.
class _MapsDemoState extends State<MapsDemo> {
@override
void initState() {
super.initState();
NextBillion.initNextBillion(YOUR_ACCESS_KEY);
}
}
Create a NBMap Widget with initial camera position
NBMap(
onMapCreated: onMapCreated,
initialCameraPosition: const CameraPosition(
target: LatLng(0.0, 0.0),
zoom: 11.0,
),
)
You need to grant location permission in order to use the location component of the NB Maps Flutter Plugin, declare the permission for both platforms:
Add the following permissions to the manifest:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
Add the following to the Runner/Info.plist to explain why you need access to the location data:
<key>NSLocationWhenInUseUsageDescription</key>
<string>[Your explanation here]</string>
NBMap(
onMapCreated: _onMapCreated,
initialCameraPosition: const CameraPosition(
target: LatLng(0, 0),
zoom: 14.0,
),
trackCameraPosition: true,
myLocationEnabled: true,
myLocationTrackingMode: MyLocationTrackingMode.Tracking,
onUserLocationUpdated: (userLocation) {},
)
To operate the mapview, you need to get the map controller in the onMapReady(NextbillionMapController controller) callback
.
The Symbol annotation adds a symbol to the map. It is configured by the specified custom SymbolOptions. If you need to add an image symbol, you need to add the image source to the map style.
final ByteData bytes = await rootBundle.load("assets/image.png");
final Uint8List list = bytes.buffer.asUint8List();
await controller?.addImage("ic_marker", list);
var symbol = controller.addSymbol(
SymbolOptions(
geometry: LatLng(-33.894372606072309, 151.17576679759523),
iconImage: "ic_marker",
iconSize: 2),
);
//remove annotation
controller!.removeSymbol(symbol);
//update annotation
controller!.updateSymbol(symbol, updatedOptions)
The Line annotation adds a line to the map. It is configured by the specified custom LineOptions.
var line = controller.addLine(
LineOptions(
geometry: [
LatLng(-33.874867744475786, 151.170627211986584),
LatLng(-33.881979408447314, 151.171361438502117),
LatLng(-33.887058805548882, 151.175032571079726),
],
lineColor: "#0000FF",
lineWidth: 20,
),
);
//remove annotation
controller!.removeLine(line);
//update annotation
controller!.updateLine(line, updatedOptions)
The Fill annotation adds a fill to the map. It is configured using the specified custom FillOptions.
var fill = controller!.addFill(
FillOptions(
geometry: [
[
LatLng(-33.901517742631846, 151.178099204457737),
LatLng(-33.872845324482071, 151.179025547977773),
LatLng(-33.868230472039514, 151.147000529140399),
LatLng(-33.883172899638311, 151.150838238009328),
LatLng(-33.901517742631846, 151.178099204457737),
],
],
fillColor: "#FF0000",
fillOutlineColor: "#000000",
),
)
//remove annotation
controller!.removeFill(fill);
//update annotation
controller!.updateFill(fill, updatedOptions)
The Circle annotation adds a circle to the map. It is configured using the specified custom CircleOptions.
var addCircle = controller!.addCircle(
CircleOptions(
geometry: LatLng(-33.894372606072309, 151.17576679759523),
circleStrokeColor: "#00FF00",
circleStrokeWidth: 2,
circleRadius: 30,
),
);
The following code snippet shows how to add Callbacks to receive tap events, and how to place annotations on this map, and how to remove these callbacks in dispose() function.
void _onMapCreated(NextbillionMapController controller) {
this.controller = controller;
controller.onFillTapped.add(_onFillTapped);
controller.onCircleTapped.add(_onCircleTapped);
controller.onLineTapped.add(_onLineTapped);
controller.onSymbolTapped.add(_onSymbolTapped);
}
@override
void dispose() {
controller.onFillTapped.remove(_onFillTapped);
controller.onCircleTapped.remove(_onCircleTapped);
controller.onLineTapped.remove(_onLineTapped);
controller.onSymbolTapped.remove(_onSymbolTapped);
super.dispose();
}
Defines a camera move, supports absolute moves as well as moving relatively to a specified position.
A Camera update moves the camera to the specified CameraPosition with the camera's [target] geographical location, its [zoom] level, [tilt] angle and [bearing].
CameraUpdate.newCameraPosition(
const CameraPosition(
bearing: 270.0,
target: LatLng(51.5160895, -0.1294527),
tilt: 30.0,
zoom: 17.0,
),
)
controller.animateCamera(cameraUpdate)
The following code snippet shows how to move the camera target to a specified geographical location.
CameraUpdate.newLatLng(const LatLng(56.1725505, 10.1850512))
The animateCamera() function starts an animated change of the map camera position
controller.animateCamera(cameraUpdate, duration)
The moveCamera function will move the camera quickly, which can be visually jarring for a user. In real usage we should strongly consider using the animateCamera() methods instead of moveCamera() because it's less abrupt.
controller.moveCamera(cameraUpdate)
Run this command:
With Flutter:
$ flutter pub add nb_maps_flutter
This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get
):
dependencies:
nb_maps_flutter: ^0.1.3
Alternatively, your editor might support flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:nb_maps_flutter/nb_maps_flutter.dart';
import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:location/location.dart';
import 'package:nb_maps_flutter/nb_maps_flutter.dart';
import 'animate_camera.dart';
import 'annotation_order_maps.dart';
import 'click_annotations.dart';
import 'custom_marker.dart';
import 'full_map.dart';
import 'layer.dart';
import 'line.dart';
import 'local_style.dart';
import 'map_ui.dart';
import 'move_camera.dart';
import 'offline_regions.dart';
import 'page.dart';
import 'place_batch.dart';
import 'place_circle.dart';
import 'place_fill.dart';
import 'place_source.dart';
import 'place_symbol.dart';
import 'scrolling_map.dart';
import 'sources.dart';
import 'take_snapshot.dart';
final List<ExamplePage> _allPages = <ExamplePage>[
MapUiPage(),
FullMapPage(),
AnimateCameraPage(),
MoveCameraPage(),
PlaceSymbolPage(),
PlaceSourcePage(),
LinePage(),
LocalStylePage(),
LayerPage(),
PlaceCirclePage(),
PlaceFillPage(),
ScrollingMapPage(),
OfflineRegionsPage(),
AnnotationOrderPage(),
CustomMarkerPage(),
BatchAddPage(),
TakeSnapPage(),
ClickAnnotationPage(),
Sources()
];
class MapsDemo extends StatefulWidget {
static const String ACCESS_KEY = String.fromEnvironment("ACCESS_KEY");
@override
State<MapsDemo> createState() => _MapsDemoState();
}
class _MapsDemoState extends State<MapsDemo> {
@override
void initState() {
super.initState();
NextBillion.initNextBillion(MapsDemo.ACCESS_KEY);
}
/// Determine the android version of the phone and turn off HybridComposition
/// on older sdk versions to improve performance for these
///
/// !!! Hybrid composition is currently broken do no use !!!
Future<void> initHybridComposition() async {
if (!kIsWeb && Platform.isAndroid) {
final androidInfo = await DeviceInfoPlugin().androidInfo;
final sdkVersion = androidInfo.version.sdkInt;
if (sdkVersion != null && sdkVersion >= 29) {
NBMap.useHybridComposition = true;
} else {
NBMap.useHybridComposition = false;
}
}
}
void _pushPage(BuildContext context, ExamplePage page) async {
if (!kIsWeb) {
final location = Location();
final hasPermissions = await location.hasPermission();
if (hasPermissions != PermissionStatus.granted) {
await location.requestPermission();
}
}
Navigator.of(context).push(MaterialPageRoute<void>(
builder: (_) => Scaffold(
appBar: AppBar(title: Text(page.title)),
body: page,
)));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('NbMaps examples')),
body: MapsDemo.ACCESS_KEY.isEmpty ||
MapsDemo.ACCESS_KEY.contains("YOUR_TOKEN")
? buildAccessTokenWarning()
: ListView.separated(
itemCount: _allPages.length,
separatorBuilder: (BuildContext context, int index) =>
const Divider(height: 1),
itemBuilder: (_, int index) => ListTile(
leading: _allPages[index].leading,
title: Text(_allPages[index].title),
onTap: () => _pushPage(context, _allPages[index]),
),
),
);
}
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: EdgeInsets.all(8),
child: Text(text,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.white)),
))
.toList(),
),
),
);
}
}
void main() {
runApp(MaterialApp(home: MapsDemo()));
}
Download details:
Author: nextbillion-ai/