Using Huawei Map Kit on Flutter Applications
Sherlock
Posted on October 20, 2020
Introduction
Flutter is a mobile application development kit for crafting high-quality native experiences on iOS and Android platforms in record time. Even though Flutter is enough to build great mobile apps, interactivity, such as map integration, is needed in order to increase the user experience.
Huawei Map Kit
Huawei Map Kit is a development kit and map service developed by Huawei to easily integrate map-based functions into your apps. The kit currently covers map data of more than 200 countries and regions, supports 40+ languages, provides UI elements such as markers, shapes, and layers to customize your map, and also enables users to interact with the map in your app through gestures and buttons in different scenarios.
With the recently released Huawei Map Kit Flutter Plugin, Huawei developers now can use these features and integrate map-based functions to their Flutter projects. Hence, in this article, to explore the kit and Huawei Services, we will try to build a mobile app featuring Huawei Map using the plugin and Flutter SDK.
HMS Core Github:
https://github.com/HMS-Core/hms-flutter-plugin/tree/master/flutter-hms-map
Required Configurations
Before we get started, to use Huawei Map Kit, and also other Huawei Mobile Services, you should be a Huawei Developer Account holder. For more detailed information on Developer Accounts and how to apply for them, please refer to this link.
Creating an App
· Sign in to AppGallery Connect using your Huawei ID and create a new project to work with, by clicking My projects>Add Project button.
· Click Add App button to add a new application to your project by filling the required fields such as name, category, and default language.
· Map Kit APIs of your app is enabled by default, but just to be sure, you can check it from the Manage APIs tab of your project on AppGallery Connect. You can also refer to Enabling Services article if you need any help.
· Open your Android Studio and create a Flutter application. The package name of your Flutter application should be the same with the package name of your app which you created on AppGallery Connect.
· Android requires a Signing Certificate to verify the authenticity of apps. Thus, you need to generate a Signing Certificate for your app. If you don’t know how to generate a Signing Certificate please click here for related article. Copy your generated Keystore file to your android/app directory of your project.
A side note: Flutter project structure have folders, such as ios and android folders, which belongs to different platforms, by nature, yet Android Studio treats them as a Flutter project folders and throws errors on these files. For this reason, before changing anything related to Android platform, you should right click to your android folder on your project directory and select Flutter > Open Android module in Android Studio. You can easily modify the files and also generate Signing Certificates from the Android Studio window that opened after selection.
· After generating your Signing Certificate (Keystore) you should extract SHA-256 fingerprint using keytool, which provided by JDK, and add to the AppGallery Connect by navigating to the App Information section of your app. You may also refer to Generating Fingerprint from a Keystore and Add Fingerprint certificate to AppGallery Connect articles for further help.
Integrating HMS and Map Plugin to Your Flutter Project
You also need to configure your Flutter application in order to communicate with Huawei to use Map Kit.
· Add the Huawei Map Kit Flutter Plugin as a dependency to your project’s pubspec.yaml file and run flutter pub get command to integrate Map Plugin to your project.
dependencies:
flutter:
sdk: flutter
huawei_map: ^4.0.4+300
· Download the agconnect-services.json file from the App Information section of the AppGallery Connect and copy it to your android/app directory of your project.
Your project’s android directory should look like this after adding both agconnect-services.json and keystore file.
· Add the Maven repository address and AppGallery Connect plugin to the project level build.gradle (android/build.gradle) file.
buildscript {
repositories {
//other repositories
maven { url 'https://developer.huawei.com/repo/' }
}
dependencies {
//other dependencies
classpath 'com.huawei.agconnect:agcp:1.2.1.301'
}
}
allprojects {
repositories {
//other repositories
maven { url 'https://developer.huawei.com/repo/' }
}
}
· Open your app level build.gradle (android/app/build.gradle) file and add the AppGallery Connect plugin.
apply plugin: 'com.android.application'
apply plugin: 'com.huawei.agconnect' //Added Line
apply plugin: 'kotlin-android'
· In the same file (android/app/build.gradle), add the signing configurations, and change the minSdkVersion of your project as shown below.
android {
/*
* Other configurations
*/
defaultConfig {
applicationId "<package_name>" //Your unique package name
minSdkVersion 19 //Change minSdkVersion to 19
targetSdkVersion 28
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
signingConfigs {
config{
storeFile file('<keystore_file>')
storePassword '<keystore_password>'
keyAlias '<key_alias>'
keyPassword '<key_password>'
}
}
buildTypes {
debug {
signingConfig signingConfigs.config
}
release {
signingConfig signingConfigs.config
}
}
}
· Finally, to call capabilities of Huawei Map Kit, apply for the following permissions for your app in your AndroidManifest.xml file.
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
— To obtain the current device location following permissions are also needed to be declared in your AndroidManifest.xml file (on Android 6.0 and later versions, you need to apply for these permissions dynamically). But we won’t use the current location in our app, this step is optional.
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
Using Huawei Map Kit Flutter Plugin
Creating a Map
Now that we are ready to use Huawei’s Map features, let’s implement a simple map.
Huawei Map Kit Flutter Plugin provides a single widget, called HuaweiMap, for developers to easily create and manage map fragments. By using this widget, developers can enable or disable attributes or gestures of a map, set initial markers, circles, or other shapes, decide the map type, and also set an initial camera position to focus an area when the map is ready.
Let’s choose a random location from İstanbul as an initial camera position. While declaring the initial camera position, the zoom level, which indicates the value of magnification, and target, which indicates the latitude and longitude of the location, is required. You can find my target coordinates and zoom level below, which we will use while creating our map.
static const LatLng _center = const LatLng(41.027470, 28.999339);
static const double _zoom = 12;
Since now we have an initial position, we should implement the map itself. We will first create a simple Scaffold and set a simple AppBar, then create a HuaweiMap object as a Scaffold’s body. HuaweiMap object, as mentioned before, has different attributes which you can see below. The following code will create a HuaweiMap object that is full-screen, scrollable, tiltable, and also shows the buildings or traffic.
class HomeScreen extends StatefulWidget {
@override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
static const LatLng _center = const LatLng(41.027470, 28.999339);
static const double _zoom = 12;
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text("Map Demo"),
centerTitle: true,
backgroundColor: Colors.red,
),
body: HuaweiMap(
initialCameraPosition: CameraPosition(
target: _center,
zoom: _zoom,
),
mapType: MapType.normal,
tiltGesturesEnabled: true,
buildingsEnabled: true,
compassEnabled: true,
zoomControlsEnabled: false,
rotateGesturesEnabled: true,
myLocationButtonEnabled: false,
myLocationEnabled: false,
trafficEnabled: true,
),
);
}
}
Considering creating a ‘clean’ map, I disabled myLocationEnabled, myLocationButtonEnabled and zoomControlsEnabled attributes of the map, but do not forget to explore these attributes by trying yourself since they are great to boost your user experience of your app.
Resizing the Map
A full-screen map is not always useful in some scenarios, thus, since HuaweiMap is a standalone widget, we can resize the map by wrapping it to Container or ConstrainedBox widgets.
For this project, I will create a layout in Scaffold by using Expanded, Column, and Container widgets. The following code shows a HuaweiMap widget which fills only one-third of a screen.
class HomeScreen extends StatefulWidget {
@override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
static const LatLng _center = const LatLng(41.027470, 28.999339);
static const double _zoom = 12;
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text("Locations"),
centerTitle: true,
backgroundColor: Colors.red,
),
body: Column(
children: [
Expanded(
child: Padding(
padding: EdgeInsets.all(8),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.green, width: 2)),
child: HuaweiMap(
initialCameraPosition: CameraPosition(
target: _center,
zoom: _zoom,
),
mapType: MapType.normal,
tiltGesturesEnabled: true,
buildingsEnabled: true,
compassEnabled: true,
zoomControlsEnabled: false,
rotateGesturesEnabled: true,
myLocationButtonEnabled: false,
myLocationEnabled: false,
trafficEnabled: true,
),
),
),
),
Expanded(flex: 2, child: Container()),
],
),
);
}
}
Adding Interactivity by Using Markers and CameraUpdate
Let’s assume that we are building an app that shows different restaurant locations as markers on our HuaweiMap object. To do this, we will set some initial markers to our map using HuaweiMap widget’s markers field.
Markers field of the widget takes a Set of markers, hence we should first create a set of Marker objects. Then use them as initial markers for our HuaweiMap widget.
As you know, the two-third of our screen is empty, to fill the space we will create some Card widgets that hold the location’s name, motto, and address as a String. To reduce the redundant code blocks, I create a separate widget, called LocationCard, which returns a styled custom Card widget. To not lose the scope of this article, I will not share the steps of how to create a custom card widget but you may find its code from the project’s GitHub link.
class HomeScreen extends StatefulWidget {
@override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
static const LatLng _center = const LatLng(41.027470, 28.999339);
static const double _zoom = 12;
//Marker locations
static const LatLng _location1 = const LatLng(41.0329109, 28.9840904);
static const LatLng _location2 = const LatLng(41.0155957, 28.9827176);
static const LatLng _location3 = const LatLng(41.0217315, 29.0111898);
//Set of markers
Set<Marker> _markers = {
Marker(markerId: MarkerId("Location1"), position: _location1),
Marker(markerId: MarkerId("Location2"), position: _location2),
Marker(markerId: MarkerId("Location3"), position: _location3),
};
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text("Locations"),
centerTitle: true,
backgroundColor: Colors.red,
),
body: Column(
children: [
Expanded(
child: Padding(
padding: EdgeInsets.all(8),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.green, width: 2)),
child: HuaweiMap(
initialCameraPosition: CameraPosition(
target: _center,
zoom: _zoom,
),
mapType: MapType.normal,
tiltGesturesEnabled: true,
buildingsEnabled: true,
compassEnabled: true,
zoomControlsEnabled: false,
rotateGesturesEnabled: true,
myLocationButtonEnabled: false,
myLocationEnabled: false,
trafficEnabled: true,
markers: _markers, //Using the set
),
),
),
),
//Styled Card widgets
Expanded(flex: 2, child: Padding(
padding: EdgeInsets.all(8),
child: SingleChildScrollView(
child: Column(
children: [
LocationCard(
title: "Location 1",
motto: "A Fine Dining Restaurant",
address:
"Avrupa Yakası, Cihangir, 34433 Beyoğlu/İstanbul Türkiye",
),
LocationCard(
title: "Location 2",
motto: "A Restaurant with an Extraordinary View",
address:
"Avrupa Yakası, Hoca Paşa, 34110 Fatih/İstanbul Türkiye",
),
LocationCard(
title: "Location 3",
motto: "A Casual Dining Restaurant",
address:
"Anadolu Yakası, Aziz Mahmut Hüdayi, 34672 Üsküdar/İstanbul Türkiye",
)
],
),
),),
)],
),
);
}
}
Now we have some custom cards beneath the map object which also has some initial markers. We will use these custom cards as a button to zoom in the desired marker with a smooth camera animation. To do so, users can easily tab a card to see the zoomed-in location on a Huawei map and explore the surrounding without leaving the page.
Resized Map with Location Cards
Before turning cards into a button we should first set a HuaweiMapController object in order to provide a controller for HuaweiMap, then use this controller on HuaweiMap widgets onMapCreated field to pair map and its controller. Below, I created a controller, and with the help of a simple function, use it in our HuaweiMap object.
HuaweiMapController mapController;
void _onMapCreated(HuaweiMapController controller) {
mapController = controller;
}
/*
This section only shows the added line. Remaining code is not changed.
*/
child: HuaweiMap(
initialCameraPosition: CameraPosition(
target: _center,
zoom: _zoom,
),
onMapCreated: _onMapCreated, // Added Line
mapType: MapType.normal,
tiltGesturesEnabled: true,
/*
Rest of the code
*/
We now have a controller for non-user camera moves, so let’s use the controller. I wrapped the LocationCards with InkWell widget to provide an onTap functionality. There are several useful methods in plugins CameraUpdate class which enables us to zoom in, zoom out, or change camera position. We will use the newLatLngZoom method to zoom in the stated location then, by using the controller and animateCamera method, we will animate the camera move to our new camera location. You can find the wrapped LocationCard with the CameraUpdate and controller.
InkWell(
onTap: () {
CameraUpdate cameraUpdate =
CameraUpdate.newLatLngZoom(
_location1, _zoomMarker);
mapController.animateCamera(cameraUpdate);
},
child: LocationCard(
title: "Location 1",
motto: "A Fine Dining Restaurant",
address:
"Avrupa Yakası, Cihangir, 34433 Beyoğlu/İstanbul Türkiye",
)),
The used _zoomMarker variable is a constant double and has a value of 18. Also, the used _location1 variable is the variable we set while creating our markers.
After implementing these steps, tap a card and you will see a smooth camera move animation with the change of zoom level in your HuaweiMap widget. Voila! 🎉
As I mentioned before, you can also set some circles, polylines, or polygons similar to the markers. Furthermore, you can add some on-click actions both to your map and shapes or markers you set. Do not forget to explore other functionalities that Huawei Map Kit Flutter Plugin offers.
I am leaving the project’s GitHub link in case you want to check or try this example by yourself. You may also find LocationCard’s code and other minor adjustments that I made, from the link.
https://github.com/SerdarCanDev/FlutterHuaweiMapTutorial
Conclusion
Since Huawei created its own services, the demand for support to cross-platform frameworks such as Flutter, React Native, Cordova or Xamarin, is increased. To meet these demands Huawei continuously releasing plugins and updates in order to support its developers. We already learned how to use Huawei’s Map Kit in our Flutter projects, yet there are several more official plugins for Huawei Services to explore.
For further reading, I will provide some links in the “References and Further Reading” section including an article which showcases another service. You may also ask any question related to this article in the comments section.
https://developer.huawei.com/consumer/en/hms/huawei-MapKit
https://pub.dev/publishers/developer.huawei.com/packages
Sending Push Notifications on Flutter with Huawei Push Kit Plugin:
https://medium.com/huawei-developers/sending-push-notifications-on-flutter-with-huawei-push-kit-plugin-534787862b4d
For more details, you can go to:
l official website:https://developer.huawei.com/consumer/en/hms
l Documentation page:https://developer.huawei.com/consumer/en/doc/development
l Reddit to join our developer discussion: https://www.reddit.com/r/HMSCore/
l GitHub:https://github.com/HMS-Core
l Stack Overflow:https://stackoverflow.com/questions/tagged/huawei-mobile-services
Posted on October 20, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.