In this article, we can learn to get current and nearby places of user’s device using a Huawei Nearby Place Search API and also to implement it in Huawei Map.
If you want to provide a feature in your app that should display a list of places such as Restaurant, GYM, Banks, Hospitals, Business, Parks, Transport, etc. near the current location of user device, then you need to use Huawei Nearby place search API, MAP and Location Kit to implement it.
Environment Requirement
1) Node JS and Visual Studio.
2) The JDK version must be 1.8 or later.
3) React Native Location, Map and Site Plugin is not supported by Expo CLI. Use React Native CLI instead.
Project Setup
1) Creating New Project.
Code:
react-native init project name
2) Generating a Signing Certificate Fingerprint.
Use following command for generating certificate.
Code:
keytool -genkey -keystore <application_project_dir>\android\app\<signing_certificate_fingerprint_filename>.jks -storepass <store_password> -alias <alias> -keypass <key_password> -keysize 2048 -keyalg RSA -validity 36500
This command creates the keystore file in application_project_dir/android/app.
The next step is to obtain the SHA256 key.
To obtain it, enter the following command in terminal:
Code:
keytool -list -v -keystore <application_project_dir>\android\app\<signing_certificate_fingerprint_filename>.jks
After an authentication, user can see SHA256 in below picture
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
3) Create an app in the Huawei AppGallery Connect.
4) Provide the SHA256 Key in App Information section.
5) Enable Map and Site kit service under Manage APIs section.
6) Download and add the agconnect-services.json file in your project.
7) Copy and paste the below maven url inside the repositories of build script and all projects (project build.gradle file):
Java:
maven { url 'http://developer.huawei.com/repo/' }
8) Copy and paste the below AppGallery Connect plugin for the HMS SDK inside dependencies (project build.gradle file):
Java:
classpath 'com.huawei.agconnect:agcp:1.4.2.301'
9) Make sure that the minSdkVersion for your application project is 19 or higher.
10) Download the Huawei Location, Map and Site kit plugin using the following command.
Code:
npm i @hmscore/react-native-hms-location
npm i @hmscore/react-native-hms-map
npm i @hmscore/react-native-hms-site
11) Add Permissions in Android Manifest file.
XML:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
Nearby Places Example
Now let’s see how to use places API to get list of nearby places based on user’s current location, display them in Map and show the current location along with address on Huawei maps.
Step 1: Using the Places API requires ACCESS_FINE_LOCATION permission, so you need to request that in your JS file:
JavaScript:
HMSLocation.FusedLocation.Native.hasPermission()
.then((result) => setHasLocationPermission(result))
.catch(HMSLocation.FusedLocation.Native.requestPermission());
Step 2: Finding Current Location and Address:
JavaScript:
HMSLocation.FusedLocation.Native.getLastLocation()
.then((pos) => (position ? null : setPosition(pos)))
.catch((err) => console.log('Failed to get last location', err));
HMSLocation.FusedLocation.Native.getLastLocationWithAddress(locationRequest)
.then((pos) => (address ? null : setAddress(pos)))
.catch((err) => console.log('Failed to get last location address', err));
Step 3: Initialize the Site kit and get list of places such as Restaurant near the current location of user device:
JavaScript:
Position ? RNHMSSite.initializeService(config)
.then(() => {
nearbySearchReq = {
location: {
lat: position.latitude,
lng: position.longitude,
},
radius: 5000,
hwPoiType: RNHMSSite.HwLocationType.RESTAURANT,
poiType: RNHMSSite.LocationType.GYM,
countryCode: 'IN',
language: 'en',
pageIndex: 1,
pageSize: 20,
politicalView: 'en',
};
site.length === 0
? RNHMSSite.nearbySearch(nearbySearchReq)
.then((res) => {
setSite(res.sites);
console.log(JSON.stringify(res));
mapView.setCameraPosition({
target: {
latitude: site[0].location.lat,
longitude: site[0].location.lng,
},
zoom: 17,
});
})
.catch((err) => {
console.log(JSON.stringify(err));
})
: null;
})
.catch((err) => {
console.log('Error : ' + err);
})
Step 4: Display a list of places on Map such as Restaurant near the current location of user device:
JavaScript:
<MapView
style={{ height: 590 }}
camera={{
target: {
latitude: position.latitude,
longitude: position.longitude,
},
zoom: 15,
}}
ref={(e) => (mapView = e)}
myLocationEnabled={true}
markerClustering={true}
myLocationButtonEnabled={true}
rotateGesturesEnabled={true}
scrollGesturesEnabled={true}
tiltGesturesEnabled={true}
zoomGesturesEnabled={true}>
{site != null
? Object.keys(site).map(function (key, i) {
return (
<Marker
visible={true}
coordinate={{
latitude: site[i].location.lat,
longitude: site[i].location.lng,
}}
clusterable>
<InfoWindow
style={{
alignContent: 'center',
justifyContent: 'center',
borderRadius: 8,
}}>
<View style={style.markerSelectedHms}>
<Text
style={style.titleSelected}>{`${site[i].name}`}</Text>
</View>
</InfoWindow>
</Marker>
);
})
: null}
</MapView>
Step 5: Get Current location address:
JavaScript:
address ? (
<View style={style.bottomView,{ backgroundColor: "white"}}>
<View style={{ flexDirection: "row" ,marginStart: 10}}>
<Image source={require('../assets/home.png')} style={{ width: 40, height: 40 }}/>
<Text style={{ marginTop: 10 ,marginStart: 10 }}>My Address</Text>
</View>
<Text style={style.textStyle}> {address.featureName + ", " +address.city+", Postal Code = "+address.postalCode} </Text>
</View>
)
Nearby Places Example Output:
Complete Code:
Copy and paste following code in your App.js file:
JavaScript:
import 'react-native-gesture-handler';
import * as React from 'react';
import { StyleSheet} from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import MapComponent from './components/MapComponent';
const Stack = createStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home"
component={MapComponent}
options={{ title: 'Huawei Map' }}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
MainContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
text: {
textAlign: 'center',
margin: 12,
fontSize: 22,
fontWeight: "100",
},
});
export default App;
Copy and paste following code in your MapComponent.js file:
1. Create folder components inside components, create a file called MapComponent.js.
2. Add the generated API key.
JavaScript:
import React, { Component, useState, useEffect } from 'react';
import { ActivityIndicator, SafeAreaView, View, Text , Image} from 'react-native';
import RNHMSSite from '@hmscore/react-native-hms-site';
import HMSLocation from '@hmscore/react-native-hms-location';
import MapView, { Marker, InfoWindow } from '@hmscore/react-native-hms-map';
let mapView, nearbySearchReq;const
config = {
apiKey: 'YOUR API KEY',
};
const GetPermssions = () => {
const [hasLocationPermission, setHasLocationPermission] = useState(false);
const [position, setPosition] = useState();
const [site, setSite] = useState([]);
const [address, setAddress] = useState();
locationRequest = {
priority: HMSLocation.FusedLocation.PriorityConstants.PRIORITY_HIGH_ACCURACY,
interval: 3,
numUpdates: 10,
fastestInterval: 1000.0,
expirationTime: 1000.0,
expirationTimeDuration: 1000.0,
smallestDisplacement: 0.0,
maxWaitTime: 10000.0,
needAddress: true,
language: "en",
countryCode: "en",
};
useEffect(() => {
HMSLocation.FusedLocation.Native.hasPermission()
.then((result) => setHasLocationPermission(result))
.catch(HMSLocation.FusedLocation.Native.requestPermission());
}, []);
if (hasLocationPermission) {
HMSLocation.FusedLocation.Native.getLastLocation()
.then((pos) => (position ? null : setPosition(pos)))
.catch((err) => console.log('Failed to get last location', err));
HMSLocation.FusedLocation.Native.getLastLocationWithAddress(locationRequest)
.then((pos) => (address ? null : setAddress(pos)))
.catch((err) => console.log('Failed to get last location address', err));
position
? RNHMSSite.initializeService(config)
.then(() => {
nearbySearchReq = {
location: {
lat: position.latitude,
lng: position.longitude,
},
radius: 5000,
hwPoiType: RNHMSSite.HwLocationType.RESTAURANT,
poiType: RNHMSSite.LocationType.GYM,
countryCode: 'IN',
language: 'en',
pageIndex: 1,
pageSize: 20,
politicalView: 'en',
};
site.length === 0
? RNHMSSite.nearbySearch(nearbySearchReq)
.then((res) => {
setSite(res.sites);
console.log(JSON.stringify(res));
mapView.setCameraPosition({
target: {
latitude: site[0].location.lat,
longitude: site[0].location.lng,
},
zoom: 17,
});
})
.catch((err) => {
console.log(JSON.stringify(err));
})
: null;
})
.catch((err) => {
console.log('Error : ' + err);
})
: null;
} else {
HMSLocation.FusedLocation.Native.requestPermission();
}
return (
<SafeAreaView
style={{
flex: 1,
}}>
{position ? (
<View>
<MapView
style={{ height: 590 }}
camera={{
target: {
latitude: position.latitude,
longitude: position.longitude,
},
zoom: 15,
}}
ref={(e) => (mapView = e)}
myLocationEnabled={true}
markerClustering={true}
myLocationButtonEnabled={true}
rotateGesturesEnabled={true}
scrollGesturesEnabled={true}
tiltGesturesEnabled={true}
zoomGesturesEnabled={true}>
{site != null
? Object.keys(site).map(function (key, i) {
return (
<Marker
visible={true}
coordinate={{
latitude: site[i].location.lat,
longitude: site[i].location.lng,
}}
clusterable>
<InfoWindow
style={{
alignContent: 'center',
justifyContent: 'center',
borderRadius: 8,
}}>
<View style={style.markerSelectedHms}>
<Text
style={style.titleSelected}>{`${site[i].name}`}</Text>
</View>
</InfoWindow>
</Marker>
);
})
: null}
</MapView>
</View>
) : (
<ActivityIndicator size="large" color="#0000ff" />
)}
{address ? (
<View style={style.bottomView,{ backgroundColor: "white"}}>
<View style={{ flexDirection: "row" ,marginStart: 10}}>
<Image source={require('../assets/home.png')} style={{ width: 40, height: 40 }}/>
<Text style={{ marginTop: 10 ,marginStart: 10 }}>My Address</Text>
</View>
<Text style={style.textStyle}> {address.featureName + ", " +address.city+", Postal Code = "+address.postalCode} </Text>
</View>
) : (
<ActivityIndicator s size="large" color="#0000ff" />
)}
</SafeAreaView>
);
};
export default class MapComponent extends Component {
render() {
return <GetPermssions />;
}
}
const style = (base) => ({
markerSelectedHms: {
flexDirection: 'row',
height: 50,
borderRadius: base.radius.default,
overflow: 'hidden',
alignSelf: 'center',
alignItems: 'center',
alignContent: 'center',
justifyContent: 'space-between',
},
bottomView: {
width: '100%',
height: 50,
backgroundColor: 'white',
position: 'absolute', //Here is the trick
bottom: 0, //Here is the trick
},
textStyle: {
color: '#000',
fontSize: 18,
}
});
Run the application:
Code:
react-native run-android
Tips and Tricks:
1) Do not forget to add API key in MapComponent.JS file.
2) Do not forget to enable Map and Site kit service in AGC console APP Gallery connect > Manage APIs section.
Conclusion:
In this article, you have learned to setting up your react native project for Huawei Nearby place API, getting list of places near current location of device, displaying list of places in Map with markers and displaying current location on Map with address.
Reference:
1) React native Plugin
2) Site Kit
3) Location Kit
4) Map Kit
Read In Forum
Can we search multiple POI in one requrest?
Related
This is originally from HUAWEI Developer Forum
Forum link: https://forums.developer.huawei.com/forumPortal/en/home
Do you want to test a web page whether is functional or not before providing solutions to CPs you can use Quick App IDE for testing just in a few steps:
1) Create a new project by using File > New Project > New QuickApp Project... direction. Enter App Name, Package Name and select "Hello World" template.
2) Open "hello.ux" file folder and paste following script. You only need to change loadUrl adress that you want to test e.g. https://developer.huawei.com/consumer/en/doc/
Code:
template>
<div class="doc-page">
<!-- Replace the link to the HTML5 app -->
<web class="web-page" src="{{loadUrl}}" trustedurl="{{list}}" onpagestart="onPageStart" onpagefinish="onPageFinish"
onmessage="onMessage" ontitlereceive="onTitleReceive"
onerror="onError" id="web"
supportzoom="{{supportZoom}}"
wideviewport="{{wideViewport}}}"
overviewmodeinload="{{overViewModeLoad}}"
useragent="{{ua}}"
allowthirdpartycookies="{{allowThirdPartyCookies}}">
</web>
</div>
</template>
<style>
.doc-page {
flex-direction: column;
flex-direction: column;
justify-content: center;
align-content: center;
align-items: center;
width: 100%;
height: 100%;
position: fixed;
}
.web-page {
width: 100%;
height: 100%;
}
</style>
<script>
import router from "@system.router";
export default {
props: ['websrc'],
data: {
title: "",
// TODO Replace the link to the H5 app
loadUrl: "https://developer.huawei.com/consumer/en/doc/",
// Attribute allowthirdpartycookies, indicates whether cookies can be delivered in cross-domain mode.
// If you need login Google Account or Other Account, Please set TRUE.
allowThirdPartyCookies: true,
//Attribute supportzoom, indicates whether the H5 page can be zoomed with gestures.
supportZoom:true,
wideViewport:true,
overViewModeLoad:true,
ua:"default",
// Here the whitelist settings, when the loading page has multiple addresses, such as the successful loading of the login address and the inconsistent entry address, it needs to set the whitelist to do so.
list: ["new RegExp('https?.*')"]
},
onInit() {
console.info('onInit: ');
},
onPageStart(e) {
console.info('pagestart: ' + e.url)
},
// Each page switch triggers
onPageFinish(e) {
console.info('pagefinish: ' + e.url, e.canBack, e.canForward);
},
onTitleReceive(e) {
this.title = e.title;
},
onError(e) {
console.info('pageError : ' + e.errorMsg)
},
onMessage(e) {
console.info('onmessage e = ' + e.message + ", url = " + e.url);
},
onShow: function () {
console.info(" onshow");
},
onHide: function () {
console.info(" onHide");
},
onBackPress() {
console.log('onBackPress')
this.$element('web').canBack({
callback: function (e) {
if (e) {
console.log('web back')
this.$element('web').back()
} else {
console.log('router back')
router.back()
}
}.bind(this)
})
return true
},
}
</script>
3) Put your app icon under Common folder. E.g. developer.png
4) Open manifest.json file and paste follwing script and change icon name with the file that you uploaded under Common folder.
Code:
{
"package": "com.testapp.huwei",
"name": "TestApp",
"versionName": "1.0.0",
"versionCode": 1,
"icon": "/Common/developer.png",
"minPlatformVersion": 1060,
"features": [
{
"name": "system.configuration"
},
{
"name": "system.calendar"
},
{
"name": "system.prompt"
},
{
"name": "system.router"
}
],
"permissions": [
{
"origin": "*"
}
],
"config": {},
"router": {
"entry": "Hello",
"pages": {
"Hello": {
"component": "hello"
}
}
},
"display": {
"titleBar":false
}
}
4) Create .rpk package by using Build > Run Release direction.
5) Accept IDE to create a signature file for your app.
6) com.testapp.huawei_release_1.0.0.rpk package has been generated and ready for test.
More information like this, you can visit HUAWEI Developer Forum
I have written series of article on Quick App. If you are new to Quick App refer my previous articles.
Quick App set up
A Novice Journey Towards Quick App ( Part 2 )
A Novice Journey Towards Quick App ( Part 3 )
In this article we will learn how to use Location Kit in Quick App.
Introduction
Geolocation is the ability to track a device’s using GPS, cell phone towers, Wi-Fi access points or a combination of these. Huawei Location Kit combines the GPS, Wi-Fi, and base station locations to help you quickly obtain precise user locations.
Should have
1. To integrate the location kit in Quick App you should have developer account. Register here.
2. Android version 4.4 or later.
3. HMS core version 3.0.0.300 later has to be installed.
4. Enable HMS Core Location permission, Setting >Apps > HMS Core > Permission.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
5. Enable location permission for application to be tested.
6. Location service should be enabled in the notification panel.
7. Test application in good network signal to get the more accurate location information.
Steps to be followed.
1. Create project (refer Quick App Part 1).
2. Add the feature attribute in manifest.json.
Code:
{
"name": "system.geolocation"
}
3. Import the geolocation in the script
Code:
import geolocation from '@system.geolocation'
4. Design Location kit screen.
1) geolocation.getLocation(cordType, timeout, success, fail, complete)
· cordType: wgs84: This is the default value. It indicates that the geographical location will be returned by invoking the system location ability.
· Timeout: timeout duration in milliseconds. Default value is 30000. It is mandatory
· Success: It is callback function used when interface is successfully called.
· Fail: Callback function used when the interface fails to be called.
· Complete: Callback function used when the interface call is completed.
2) geolocation.subscribe(cordType, callback, fail)
· cordType: wgs84: This is the default value. It indicates that the geographical location will be returned by invoking the system location ability.
· Callback: It is called whenever location information changed.
· Fail: Callback function used when the interface fails to be called.
3) gelocation.unsubscribe() This is cancel the monitoring the geographical location.
4) geolocation.getLocationType(success, fail, complete): To get the location types currently supported by the system.
5) geolocation.openLocation: This is to view the location on the built in map. Currently it is Supported only in Chinese mainland.
6) geolocation.chooseLocation: This it open map and select location. Currently it is supported only in Chinese mainland.
Code:
<template>
<div class="container">
<div class="page-title-wrap">
<text class="page-title">{{componentName}}</text>
</div>
<div class="item-content">
<text class="txt">{{$t('Current Location')}}</text>
<text class="txt">latitude: {{geolocationGetData.latitude}}</text>
<text class="txt">longitude: {{geolocationGetData.longitude}}</text>
<text class="txt">altitude: {{geolocationGetData.altitude}}</text>
<text class="txt">accuracy: {{geolocationGetData.accuracy}}</text>
<text class="txt">heading: {{geolocationGetData.heading}}</text>
<text class="txt">speed: {{geolocationGetData.speed}}</text>
<text class="txt">time: {{geolocationGetData.time}}</text>
</div>
<input type="button" class="btn" onclick="getGeolocation" value="{{$t('Get My Current Location')}}" />
<div class="item-content">
<text class="txt">{{$t('Location')}}</text>
<text class="txt">latitude: {{geolocationListenData.latitude}}</text>
<text class="txt">longitude: {{geolocationListenData.longitude}}</text>
<text class="txt">accuracy: {{geolocationListenData.accuracy}}</text>
<text class="txt">time: {{geolocationListenData.time}}</text>
</div>
<input type="button" class="btn" onclick="listenGeolocation" value="{{$t('Monitor Location')}}" />
<input type="button" class="btn" onclick="cancelGeolocation" value="{{$t('Cancel Monitoring Location')}}" />
<div class="item-content">
<text class="txt">{{$t('Location Type: ')}}{{typeVaule}}</text>
</div>
<input type="button" class="btn" onclick="getLocationType" value="{{$t('Location type')}}" />
<input type="button" class="btn" onclick="openLocation" value="Open Location in Map" />
<input type="button" class="btn" onclick="chooseLocation" value="Choose location from Map" />
</div>
</div>
</template>
<style>
@import "../../../common/css/common.css";
.item-container {
margin-bottom: 30px;
margin-right: 60px;
margin-left: 60px;
flex-direction: column;
color: #ffffff;
}
.item-content {
flex-direction: column;
padding: 30px;
margin-bottom: 50px;
align-items: flex-start;
color: #ffffff;
}
.txt{
color: #ffffff;
}
</style>
<script>
import geolocation from '@system.geolocation'
import prompt from '@system.prompt'
export default {
data: {
componentName: 'Location Kit',
componentData: {},
deviceInfo: '',
isHuawei: false,
time: '',
geolocationGetData: {
latitude: '',
longitude: '',
altitude: '',
accuracy: '',
heading: '',
speed: '',
time: ''
},
geolocationListenData: {
latitude: '',
longitude: '',
time: '',
accuracy: ''
},
typeVaule: ''
},
onInit: function () {
this.$page.setTitleBar({ text: 'Location Kit' })
this.componentData = this.$t('message.interface.system.geolocation');
},
getGeolocation: function () {
var that = this;
if (that.isHuawei) {
prompt.showToast({
message: this.componentData.baiduMap
})
geolocation.getLocation({
coordType: "gcj02",
timeout: 2000,
success: function (ret) {
that.geolocationGetData = ret;
console.log(that.geolocationGetData.time);
var date = new Date(that.geolocationGetData.time);
that.geolocationGetData.time = date;
},
fail: function (erromsg, errocode) {
console.log('geolocation.getLocation----------' + errocode + ': ' + erromsg)
},
complete: function () {
console.log('geolocation complete----------')
}
})
} else {
prompt.showToast({
message: this.componentData.systemMap
})
geolocation.getLocation({
timeout: 2000,
success: function (ret) {
that.geolocationGetData = ret;
var time = new Date().getTime();
console.log(that.geolocationGetData.time);
var date = new Date(that.geolocationGetData.time);
that.geolocationGetData.time = date;
},
fail: function (erromsg, errocode) {
console.log('geolocation.getLocation----------' + errocode + ': ' + erromsg)
},
complete: function () {
console.log('geolocation complete----------')
}
})
}
},
listenGeolocation: function () {
var that = this;
geolocation.subscribe({
callback: function (ret) {
that.geolocationListenData = ret;
console.log(that.geolocationListenData.time);
var date = new Date(ret.time);
that.geolocationListenData.time = date;
},
fail: function (erromsg, errocode) {
console.log('geolocation.subscribe----------' + errocode + ': ' + erromsg)
}
})
},
cancelGeolocation: function () {
geolocation.unsubscribe();
},
getLocationType: function () {
var that = this;
geolocation.getLocationType({
success: function (data) {
that.typeVaule = data.types;
console.log("ret - " + data.types)
}
})
},
openLocation: function(){
geolocation.openLocation({
latitude: 12.972442,
longitude: 77.580643,
coordType:"gcj02",
name: "Bangalore",
address: "Bangalore",
scale: 18,
success: function () {
console.log('openLocation success .');
},
fail: function (erromsg, errocode) {
console.log('geolocation.openLocation----------' + errocode + ': ' + erromsg)
},
complete: function () {
console.log('openLocation complete.');
}
})
},
chooseLocation: function(){
console.log("chooseLocation");
geolocation.chooseLocation({
latitude: 12.972442,
longitude: 77.580643,
coordType:"gcj02",
success: function (data) {
console.log('chooseLocation success �� ' + JSON.stringify(data));
},
fail: function (error) {
console.log('chooseLocation fail : ' + error.message);
},
complete: function () {
console.log('chooseLocation complete.');
}
})
}
}
</script>
Result
Conclusion
In this article, we have learnt how to integrate the Location kit in Quick App. In upcoming article I will come up with new concept.
Reference
Location kit official document
In this article, I am going to use 3 Huawei kits in one project:
· Map Kit, for personalizing how your map displays and interact with your users, also making location-based services work better for your users.
· Location Kit, for getting the user’s current location with fused location function, also creating geofences.
· Site Kit, for searching and exploring the nearby places with their addresses.
What is a Geo-fence?
Geofence literally means a virtual border around a geographic area. Geofencing technology is the name of the technology used to trigger an automatic alert when an active device enters a defined geographic area (geofence).
As technology developed, brands started to reach customers. Of course, at this point, with digital developments, multiple new marketing terms started to emerge. Geofencing, a new term that emerged with this development, entered the lives of marketers.
Project Setup
HMS Integration
Firstly, you need a Huawei Developer account and add an app in Projects in AppGallery Connect console. So that you can activate the Map, Location and Site kits and use them in your app. If you don’t have an Huawei Developer account and don’t know the steps please follow the links below.
· Register Huawei developer website
· Configuring app information in AppGallery Connect
· Integrating Map Kit Flutter Plugin
· Integrating Location Kit Flutter Plugin
· Integrating Site Kit Flutter Plugin
Important: While adding app, the package name you enter should be the same as your Flutter project’s package name.
Note: Before you install agconnect-services.json file, make sure the required kits are enabled.
Permissions
In order to make your kits work perfectly, you need to add the permissions below in AndroidManifest.xml file.
Code:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
Creating Flutter Application
Add Dependencies to ‘pubspec.yaml’
After completing all the steps above, you need to add the required kits’ Flutter plugins as dependencies to pubspec.yaml file. You can find all the plugins in pub.dev with the latest versions. You can follow the steps in installing section of the following links.
· Map Kit Plugin for Flutter
· Location Kit Plugin for Flutter
· Site Kit Plugin for Flutter
Code:
dependencies:
flutter:
sdk: flutter
huawei_location: ^5.0.0+301
huawei_site: ^5.0.1+300
huawei_map: ^4.0.4+300
After adding them, run flutter pub get command.
All the plugins are ready to use!
Request Location Permission and Get Current Location
Create a PermissionHandler instance and initialize it in initState to ask for permission. Also, follow the same steps for FusedLocationProviderClient. With locationService object, we can get the user’s current location by calling getLastLocation() method.
Code:
LatLng center;
PermissionHandler permissionHandler;
FusedLocationProviderClient locationService;
@override
void initState() {
permissionHandler = PermissionHandler();
locationService = FusedLocationProviderClient();
getCurrentLatLng();
super.initState();
}
getCurrentLatLng() async {
await requestPermission();
Location currentLocation = await locationService.getLastLocation();
LatLng latLng = LatLng(currentLocation.latitude, currentLocation.longitude);
setState(() {
center = latLng;
});
}
In requestPermission() method, you can find both Location Permission and Background Location Permission.
Code:
requestPermission() async {
bool hasPermission = await permissionHandler.hasLocationPermission();
if (!hasPermission) {
try {
bool status = await permissionHandler.requestLocationPermission();
print("Is permission granted $status");
} catch (e) {
print(e.toString());
}
}
bool backgroundPermission =
await permissionHandler.hasBackgroundLocationPermission();
if (!backgroundPermission) {
try {
bool backStatus =
await permissionHandler.requestBackgroundLocationPermission();
print("Is background permission granted $backStatus");
} catch (e) {
print(e.toString);
}
}
}
When you launch the app for the first time, the location permission screen will appear.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Add HuaweiMap
Huawei Map is the main layout of this project. It will cover all the screen and also we will add some buttons on it, so we should put HuaweiMap and other widgets into a Stack widget. Do not forget to create a Huawei map controller.
Code:
static const double _zoom = 16;
Set<Marker> _markers = {};
int _markerId = 1;
Set<Circle> _circles = {};
int _circleId = 1;
_onMapCreated(HuaweiMapController controller) {
mapController = controller;
}
Stack(
fit: StackFit.expand,
children: <Widget>[
HuaweiMap(
onMapCreated: _onMapCreated,
initialCameraPosition:
CameraPosition(target: center, zoom: _zoom),
mapType: MapType.normal,
onClick: (LatLng latLng) {
placeSearch(latLng);
selectedCoordinates = latLng;
_getScreenCoordinates(latLng);
setState(() {
clicked = true;
addMarker(latLng);
addCircle(latLng);
});
},
markers: _markers,
circles: _circles,
tiltGesturesEnabled: true,
buildingsEnabled: true,
compassEnabled: true,
zoomControlsEnabled: true,
rotateGesturesEnabled: true,
myLocationButtonEnabled: true,
myLocationEnabled: true,
trafficEnabled: false,
),
],
)
We have got the current location with Location service’s getLastLocation() method and assigned it to center variables as longitude and latitude. While creating the HuaweiMap widget, assign that center variable to HuaweiMap’s target property, so that the app opens with a map showing the user’s current location.
Code:
placeSearch(LatLng latLng) async {
NearbySearchRequest request = NearbySearchRequest();
request.location = Coordinate(lat: latLng.lat, lng: latLng.lng);
request.language = "en";
request.poiType = LocationType.ADDRESS;
request.pageIndex = 1;
request.pageSize = 1;
request.radius = 100;
NearbySearchResponse response = await searchService.nearbySearch(request);
try {
print(response.sites);
site = response.sites[0];
} catch (e) {
print(e.toString());
}
}
When onClick method of HuaweiMap is triggered, call placeSearch using the Site Kit’s nearbySearch method. Thus, you will get a Site object to assign to the new geofence you will add.
Create Geofence
When the user touch somewhere on the map; a marker, a circle around the marker, a Slider widget to adjust the radius of the circle, and a button named “Add Geofence” will show up on the screen. So we will use a boolean variable called clicked and if it’s true, the widgets I have mentioned in the last sentence will be shown.
Code:
addMarker(LatLng latLng) {
if (marker != null) marker = null;
marker = Marker(
markerId: MarkerId(_markerId.toString()), //_markerId is set to 1
position: latLng,
clickable: true,
icon: BitmapDescriptor.defaultMarker,
);
setState(() {
_markers.add(marker);
});
selectedCoordinates = latLng;
_markerId++; //after a new marker is added, increase _markerId for the next marker
}
_drawCircle(Geofence geofence) {
this.geofence = geofence;
if (circle != null) circle = null;
circle = Circle(
circleId: CircleId(_circleId.toString()),
fillColor: Colors.grey[400],
strokeColor: Colors.red,
center: selectedCoordinates,
clickable: false,
radius: radius,
);
setState(() {
_circles.add(circle);
});
_circleId++;
}
Create a Slider widget wrapped with a Positioned widget and put them into Stack widget as shown below.
Code:
if (clicked)
Positioned(
bottom: 10,
right: 10,
left: 10,
child: Slider(
min: 50,
max: 200,
value: radius,
onChanged: (newValue) {
setState(() {
radius = newValue;
_drawCircle(geofence);
});
},
),
),
After implementing addMarker and drawCircle methods and adding Slider widget, now we will create AddGeofence Screen and it will appear as a ModalBottomSheet when AddGeofence button is clicked.
Code:
RaisedButton(
child: Text("Add Geofence"),
onPressed: () async {
geofence.uniqueId = _fenceId.toString();
geofence.radius = radius;
geofence.latitude = selectedCoordinates.lat;
geofence.longitude = selectedCoordinates.lng;
_fenceId++;
final clickValue = await showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) => SingleChildScrollView(
child: Container(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom),
child: AddGeofenceScreen(
geofence: geofence,
site: site,
),
),
),
);
updateClicked(clickValue);
//When ModalBottomSheet is closed, pass a bool value in Navigator
//like Navigator.pop(context, false) so that clicked variable will be
//updated in home screen with updateClicked method.
},
),
void updateClicked(bool newValue) {
setState(() {
clicked = newValue;
});
}
In the new stateful AddGeofenceScreen widget’s state class, create GeofenceService and SearchService instances and initialize them in initState.
Code:
GeofenceService geofenceService;
int selectedConType = Geofence.GEOFENCE_NEVER_EXPIRE;
SearchService searchService;
@override
void initState() {
geofenceService = GeofenceService();
searchService = SearchService();
super.initState();
}
To monitor address, radius and also to select conversion type of the geofence, we will show a ModalBottomSheet with the widgets shown below.
Code:
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text(
"Address",
style: boldStyle,
),
Text(site.formatAddress),
Text(
"\nRadius",
style: boldStyle,
),
Text(geofence.radius.toInt().toString()),
Text(
"\nSelect Conversion Type",
style: boldStyle,
),
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
RadioListTile<int>(
dense: true,
title: Text(
"Enter",
style: TextStyle(fontSize: 14),
),
value: Geofence.ENTER_GEOFENCE_CONVERSION,
groupValue: selectedConType,
onChanged: (int value) {
setState(() {
selectedConType = value;
});
},
),
RadioListTile<int>(
dense: true,
title: Text("Exit"),
value: Geofence.EXIT_GEOFENCE_CONVERSION,
groupValue: selectedConType,
onChanged: (int value) {
setState(() {
selectedConType = value;
});
},
),
RadioListTile<int>(
dense: true,
title: Text("Stay"),
value: Geofence.DWELL_GEOFENCE_CONVERSION,
groupValue: selectedConType,
onChanged: (int value) {
setState(() {
selectedConType = value;
});
},
),
RadioListTile<int>(
dense: true,
title: Text("Never Expire"),
value: Geofence.GEOFENCE_NEVER_EXPIRE,
groupValue: selectedConType,
onChanged: (int value) {
setState(() {
selectedConType = value;
});
},
),
],
),
Align(
alignment: Alignment.bottomRight,
child: FlatButton(
child: Text(
"SAVE",
style: TextStyle(
color: Colors.blue, fontWeight: FontWeight.bold),
),
onPressed: () {
geofence.conversions = selectedConType;
addGeofence(geofence);
Navigator.pop(context, false);
},
),
)
],
),
For each conversion type, add a RadioListTile widget.
When you click SAVE button, addGeofence method will be called to add new Geofence to the list of Geofences, then return to the Home screen with false value to update clicked variable.
In addGeofence, do not forget to call createGeofenceList method with the list you have just added the geofence in.
Code:
void addGeofence(Geofence geofence) {
geofence.dwellDelayTime = 10000;
geofence.notificationInterval = 100;
geofenceList.add(geofence);
GeofenceRequest geofenceRequest = GeofenceRequest(geofenceList:
geofenceList);
try {
int requestCode = await geofenceService.createGeofenceList
(geofenceRequest);
print(requestCode);
} catch (e) {
print(e.toString());
}
}
To listen to the geofence events, you need to use onGeofenceData method in your code.
Code:
GeofenceService geofenceService;
StreamSubscription<GeofenceData> geofenceStreamSub;
@override
void initState() {
geofenceService = GeofenceService();
geofenceStreamSub = geofenceService.onGeofenceData.listen((data) {
infoText = data.toString(); //you can use this infoText to show a toast message to the user.
print(data.toString);
});
super.initState();
}
Search Nearby Places
In home screen, place a button onto the map to search nearby places with a keyword and when it is clicked a new alertDialog page will show up.
Code:
void _showAlertDialog() {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Search Location"),
content: Container(
height: 150,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
TextField(
controller: searchQueryController,
),
MaterialButton(
color: Colors.blue,
child: Text(
"Search",
style: TextStyle(color: Colors.white),
),
onPressed: () async {
Navigator.pop(context);
_markers =
await nearbySearch(center, searchQueryController.text);
setState(() {});
},
)
],
),
),
actions: [
FlatButton(
child: Text("Close"),
onPressed: () {
Navigator.pop(context);
},
),
],
);
},
);
}
After you enter the keyword and click Search button, there will be markers related to the keyword will appear on the map.
Conclusion
In this article you have learnt how to use some of the features of Huawei Map, Location and Site kits in your projects. Also, you have learnt the geofencing concept. Now you can add geofences to your app and with geofencing, you can define an audience based on a customer’s behavior in a specific location. With location information, you can show suitable ads to the right people simultaneously, wherever they are.
Thank you for reading this article, I hope it was useful and you enjoyed it!
Huawei is the best Android smartphone devices making company. I don't know why Android creating a so much of issues. I feel bad
Can we show GIF image on huawei map at predefined locaation?
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Introduction
Flutter Analytics Plugin provides wider range of predefined analytics models to get more insight into your application users, products, and content. With this insight, you can prepare data-driven approach to market your apps and optimize your products based on the analytics.
With Analytics Kit's on-device data collection SDK, you can:
Collect and report custom events.
Set a maximum of 25 user attributes.
Automate event collection and session calculation.
Pre-set event IDs and parameters.
Restrictions
1. Devices:
a. Analytics Kit depends on HMS Core (APK) to automatically collect the following events: INSTALLAPP (app installation), UNINSTALLAPP (app uninstallation), CLEARNOTIFICATION (data deletion), INAPPPURCHASE (in-app purchase), RequestAd (ad request), DisplayAd (ad display), ClickAd (ad tapping), ObtainAdAward (ad award claiming), SIGNIN (sign-in), and SIGNOUT (sign-out). These events cannot be automatically collected on third-party devices where HMS Core (APK) is not installed (including but not limited to OPPO, vivo, Xiaomi, Samsung, and OnePlus).
b. Analytics Kit does not work on iOS devices.
2. Number of events:
A maximum of 500 events are supported.
3. Number of event parameters:
You can define a maximum of 25 parameters for each event, and a maximum of 100 event parameters for each project.
4. Supported countries/regions
The service is now available only in the countries/regions listed in Supported Countries/Regions.
Integration process
1. Create flutter project
Step 2: Add the App level gradle dependencies. Choose inside project Android > app > build.gradle
Java:
apply plugin: 'com.android.application'
apply plugin: 'com.huawei.agconnect'
Add root level gradle dependencies
Java:
maven {url 'https://developer.huawei.com/repo/'}
classpath 'com.huawei.agconnect:agcp:1.4.1.300'
Add app level gradle dependencies
Java:
implementation 'com.huawei.hms:hianalytics:5.1.0.300'
Step 3: Add the below permissions in Android Manifest file.
XML:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="com.huawei.appmarket.service.commondata.permission.GET_COMMON_DATA"/>
Step 4: Flutter plugin for Huawei analytics kit.
Unzip downloaded plugin in the parent directory of the project.
Step 5: Declare plugin path in pubspec.yaml file under dependencies.
Step 5 : Create a project in AppGallery Connect.
pubspec.yaml
YAML:
<p style="margin-top: 20.0px;white-space: normal;">name: flutter_app
</p><p style="margin-top: 20.0px;white-space: normal;">description: A new Flutter application.
# The following line prevents the package from being accidentally published to
# pub.dev using `pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1
environment:
sdk: ">=2.7.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
huawei_analytics:
path: ../huawei_analytics/
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
dev_dependencies:
flutter_test:
sdk: flutter
# The following section is specific to Flutter.
flutter:</p>
main.dart
Code:
import 'package:flutter/material.dart';
import 'package:flutter_app/result.dart';
import 'package:huawei_analytics/huawei_analytics.dart';
import './quiz.dart';
import './result.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _MyAppState();
}
}
class _MyAppState extends State<MyApp> {
var _questionIndex = 0;
int _totalScore = 0;
final HMSAnalytics _hmsAnalytics = new HMSAnalytics();
@override
void initState() {
_enableLog();
_predefinedEvent();
super.initState();
}
Future<void> _enableLog() async {
_hmsAnalytics.setUserId("TestUser123");
await _hmsAnalytics.enableLog();
}
void _restartQuiz() {
setState(() {
_questionIndex = 0;
_totalScore = 0;
});
}
void _predefinedEvent() async {
String name = HAEventType.SIGNIN;
dynamic value = {HAParamType.ENTRY: 06534797};
await _hmsAnalytics.onEvent(name, value);
print("Event posted");
}
void _customEvent(int index, int score) async {
String name = "Question$index";
dynamic value = {'Score': score};
await _hmsAnalytics.onEvent(name, value);
print("Event posted");
}
static const _questions = [
{
'questionText': 'What\'s you favorite color?',
'answers': [
{'text': 'Black', 'Score': 10},
{'text': 'White', 'Score': 1},
{'text': 'Green', 'Score': 3},
{'text': 'Red', 'Score': 5},
]
},
{
'questionText': 'What\'s your favorite place?',
'answers': [
{'text': 'India', 'Score': 1},
{'text': 'Rassia', 'Score': 5},
{'text': 'US', 'Score': 4},
{'text': 'Singapore', 'Score': 7},
]
},
{
'questionText': 'What\'s your childwood nick name?',
'answers': [
{'text': 'Bunty', 'Score': 2},
{'text': 'Binto', 'Score': 1},
{'text': 'Tom', 'Score': 5},
{'text': 'Ruby', 'Score': 3},
]
},
{
'questionText': 'What\'s your favorite subject?',
'answers': [
{'text': 'Math', 'Score': 5},
{'text': 'Physics', 'Score': 1},
{'text': 'Chemistry', 'Score': 3},
{'text': 'Biology', 'Score': 2},
]
}
];
Future<void> _answerQuestion(int score) async {
_totalScore += score;
if (_questionIndex < _questions.length) {
print('Iside if $_questionIndex');
setState(() {
_questionIndex = _questionIndex + 1;
});
print('Current questionIndex $_questionIndex');
} else {
print('Inside else $_questionIndex');
}
_customEvent(_questionIndex, score);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('QuizApp'),
),
body: _questionIndex < _questions.length
? Quiz(
answerQuestion: _answerQuestion,
questionIndex: _questionIndex,
questions: _questions,
)
: Result(_totalScore, _restartQuiz),
));
}
}
question.dart
Code:
import 'package:flutter/material.dart';
class Question extends StatelessWidget {
final String questionText;
Question(this.questionText);
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
margin: EdgeInsets.all(30.0),
child: Text(
questionText,
style: TextStyle(
fontSize: 28,
),
textAlign: TextAlign.center,
),
);
}
}
answer.dart
Code:
import 'package:flutter/material.dart';
class Answer extends StatelessWidget {
final Function selectHandler;
final String answerText;
Answer(this.selectHandler, this.answerText);
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
margin: EdgeInsets.fromLTRB(20, 10, 20, 10),
child: RaisedButton(
child: Text(answerText),
color: Colors.blue,
textColor: Colors.white,
onPressed: selectHandler,
),
);
}
}
quiz.dart
Code:
import 'package:flutter/material.dart';
import './answer.dart';
import './question.dart';
class Quiz extends StatelessWidget {
final List<Map<String, Object>> questions;
final int questionIndex;
final Function answerQuestion;
Quiz({
@required this.answerQuestion,
@required this.questions,
@required this.questionIndex,
});
@override
Widget build(BuildContext context) {
return Column(
children: [
Question(
questions[questionIndex]['questionText'],
),
...(questions[questionIndex]['answers'] as List<Map<String, Object>>)
.map((answer) {
return Answer(() => answerQuestion(answer['Score']), answer['text']);
}).toList()
],
);
}
}
result.dart
Code:
import 'package:flutter/material.dart';
class Result extends StatelessWidget {
final int resulScore;
final Function restarthandler;
Result(this.resulScore, this.restarthandler);
String get resultPhrase {
String resultText;
if (resulScore <= 8) {
resultText = 'You are awesome and innocent!.';
} else if (resulScore <= 12) {
resultText = 'Pretty likable!.';
} else if (resulScore <= 12) {
resultText = 'You are .. strange!.';
} else {
resultText = 'You are so bad!';
}
return resultText;
}
@override
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
Text(
resultPhrase,
style: TextStyle(fontSize: 36, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
FlatButton(
child: Text('Restart again', style: TextStyle(fontSize: 22)),
textColor: Colors.blue,
onPressed: restarthandler,
),
],
),
);
}
}
Result
Tricks and Tips
Make sure that downloaded plugin is added in specified directory.
Makes sure that agconnect-services.json file added.
Make sure dependencies are added yaml file.
Run flutter pug get after adding dependencies.
Generating SHA-256 certificate fingerprint in android studio and configure in Ag-connect.
Enable debug mode using following command
Code:
adb shell setprop debug.huawei.hms.analytics.app package_name
Conclusion
In this article, we have learnt how to integrate Huawei Analytics Kit into Flutter QuizApp, which lets you to app analytics like users, predefined events and Custom events in the Ag-connect.
Thank you so much for reading, I hope this article helps you to understand the Huawei Analytics Kit in flutter.
Reference
Official plugin guide for flutter :
Document
developer.huawei.com
Flutter plugin :
Document
developer.huawei.com
HMS Core :
Document
developer.huawei.com
Read In Forum
Does it supports real time analytics?
IntroductionIn this article, we will be integrating Huawei Map kit and Location kit in Food Delivery application. Huawei Map kit currently allows developer to create map, interactions with map and drawing on a map.
We will be covering all three aspects as the delivery application we need to create map and we need to draw polyline from delivery agent location to user location and on interaction also we are providing i.e. on click the marker we are show popup on the map with details as shown in the result section below.
Development OverviewYou need to install Flutter and Dart plugin in IDE and I assume that you have prior knowledge about the Flutter and Dart.
Hardware Requirements
A computer (desktop or laptop) running Windows 10.
A Huawei phone (with the USB cable), which is used for debugging.
Software Requirements
Java JDK 1.7 or later.
Android studio software or Visual Studio or Code installed.
HMS Core (APK) 4.X or later.
Integration processStep 1. Create flutter project
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Step 2. Add the App level gradle dependencies. Choose inside project Android > app > build.gradle.
Code:
apply plugin:'com.huawei.agconnect'
Add root level gradle dependencies.
Code:
maven {url 'https://developer.huawei.com/repo/'}
classpath 'com.huawei.agconnect:agcp:1.4.1.300'
Add app level gradle dependencies.
Code:
implementation 'com.huawei.hms:maps:5.0.3.302'
implementation 'com.huawei.hms:location:5.0.0.301'
Step 3: Add the below permissions in Android Manifest file.
Code:
<uses-permission android:name="android.permission.INTERNET " />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="com.huawei.hms.permission.ACTIVITY_RECOGNITION"/>
Step 4: Add below path in pubspec.yaml file under dependencies.
Step 5 : Create a project in AppGallery Connect.pubspec.yaml
Code:
name: sample_one
description: A new Flutter application.
# The following line prevents the package from being accidentally published to
# pub.dev using `pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+1
environment:
sdk: ">=2.7.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
huawei_map:
path: ../huawei_map/
huawei_location:
path: ../huawei_location/
http: ^0.12.2
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
dev_dependencies:
flutter_test:
sdk: flutter
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages
How to check required permissions are granted or not?
Code:
void hasPermission() async {
try {
bool status = await permissionHandler.hasLocationPermission();
setState(() {
message = "Has permission: $status";
if (status) {
getLastLocationWithAddress();
//requestLocationUpdatesByCallback();
} else {
requestPermission();
}
});
} catch (e) {
setState(() {
message = e.toString();
});
}
}
How do I request permission?
Code:
void requestPermission() async {
try {
bool status = await permissionHandler.requestLocationPermission();
setState(() {
message = "Is permission granted $status";
});
} catch (e) {
setState(() {
message = e.toString();
});
}
}
How do I get location data?
Code:
void getLastLocationWithAddress() async {
try {
HWLocation location =
await locationService.getLastLocationWithAddress(locationRequest);
setState(() {
message = location.street +
" " +
location.city +
" " +
location.state +
" " +
location.countryName +
" " +
location.postalCode;
print("Location: " + message);
});
} catch (e) {
setState(() {
message = e.toString();
print(message);
});
}
}
main.dart
Code:
import 'package:flutter/material.dart';
import 'package:huawei_map/map.dart';
import 'package:sample_one/mapscreen2.dart';
import 'package:sample_one/order.dart';
void main() => runApp(App());
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Orders'),
),
body: MyApp(),
),
debugShowCheckedModeBanner: false,
);
}
}
class MyApp extends StatefulWidget {
MyApp({Key key}) : super(key: key);
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
List orders = [
Order(
imageUrl:
"https://www.namesnack.com/images/namesnack-pizza-business-names-5184x3456-20200915.jpeg",
name: "Veg Pizza Special",
username: "Naresh K",
location: new LatLng(12.9698, 77.7500)),
Order(
imageUrl:
"https://www.pizzahutcouponcode.com/wp-content/uploads/2020/12/10.jpg",
name: "Pretzel Rolls ",
username: "Ramesh",
location: new LatLng(12.9698, 77.7500)),
Order(
imageUrl:
"https://www.manusmenu.com/wp-content/uploads/2015/01/1-Chicken-Spring-Rolls-9-1-of-1.jpg",
name: "Special Veg Rolls",
username: "Mahesh N",
location: new LatLng(12.9598, 77.7540)),
Order(
imageUrl:
"https://www.thespruceeats.com/thmb/axBJnjZ_30_-iHgjGzP1tS4ssGA=/4494x2528/smart/filters:no_upscale()/thai-fresh-rolls-with-vegetarian-option-3217706_form-rolls-step-07-f2d1c96942b04dd0830026702e697f17.jpg",
name: "The Great Wall of China",
username: "Chinmay M",
location: new LatLng(12.9098, 77.7550)),
Order(
imageUrl:
"https://cdn.leitesculinaria.com/wp-content/uploads/2021/02/pretzel-rolls-fp.jpg.optimal.jpg",
name: "Pretzel Rolls",
username: "Ramesh",
location: new LatLng(12.9658, 77.7400)),
Order(
imageUrl:
"https://dinnerthendessert.com/wp-content/uploads/2019/01/Egg-Rolls-3.jpg",
name: "Egg Rolls",
username: "Preeti",
location: new LatLng(12.9618, 77.7700)),
Order(
imageUrl:
"https://images.immediate.co.uk/production/volatile/sites/30/2020/08/recipe-image-legacy-id-1081476_12-9367fea.jpg",
name: "Easy Spring Rolls",
username: "Nithin ",
location: new LatLng(12.9218, 77.7100)),
];
@override
void initState() {
// TODO: implement initState
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white60,
body: SingleChildScrollView(
child: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: Stack(
children: <Widget>[
Container(
padding: EdgeInsets.only(top: 1),
height: MediaQuery.of(context).size.height,
width: double.infinity,
child: ListView.builder(
itemCount: orders.length,
itemBuilder: (context, index) {
return ListTile(
leading: Image.network(orders[index].imageUrl),
title: Text(orders[index].name),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => MapPage(
orders[index].name, orders[index].location)));
},
subtitle: Text(orders[index].username),
);
},
),
),
],
),
),
),
);
}
}
mapscreen.dart
Code:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:huawei_map/map.dart';
import 'package:sample_one/directionapiutil.dart';
import 'package:sample_one/routerequest.dart';
import 'package:sample_one/routeresponse.dart';
class MapPage extends StatefulWidget {
String name;
LatLng location;
MapPage(this.name, this.location);
@override
_MapPageState createState() => _MapPageState(name, location);
}
class _MapPageState extends State<MapPage> {
String name, dist = '';
LatLng location, dest_location = new LatLng(12.9709, 77.7257);
_MapPageState(this.name, this.location);
HuaweiMapController _mapController;
final Set<Marker> _markers = {};
final Set<Polyline> _polyLines = {};
final List<LatLng> _points = [];
BitmapDescriptor _markerIcon;
List<LatLng> polyList = [
LatLng(12.9970, 77.6690),
LatLng(12.9569, 77.7011),
LatLng(12.9177, 77.6238)
];
@override
void initState() {
super.initState();
_loadMarkers(location);
showDirection();
}
@override
Widget build(BuildContext context) {
//_customMarker(context);
return new Scaffold(
appBar: null,
body: Stack(
children: [
_buildMap(),
Positioned(
top: 10,
right: 40,
left: 40,
child: ButtonBar(
buttonPadding: EdgeInsets.all(15),
alignment: MainAxisAlignment.center,
children: <Widget>[
/* new RaisedButton(
onPressed: showDirection,
child: new Text("Show direction",
style: TextStyle(fontSize: 20.0)),
color: Colors.green,
),*/
Center(
child: new Text(
"$dist",
style:
TextStyle(fontSize: 20.0, backgroundColor: Colors.cyan),
),
),
/* new RaisedButton(
onPressed: _showPolygone,
child: new Text("Polygon",
style: TextStyle(fontSize: 20.0, color: Colors.white)),
color: Colors.lightBlueAccent,
),*/
],
),
)
],
),
);
}
_buildMap() {
return HuaweiMap(
initialCameraPosition: CameraPosition(
target: location,
zoom: 12.0,
bearing: 30,
),
onMapCreated: (HuaweiMapController controller) {
_mapController = controller;
},
mapType: MapType.normal,
tiltGesturesEnabled: true,
buildingsEnabled: true,
compassEnabled: true,
zoomControlsEnabled: true,
rotateGesturesEnabled: true,
myLocationButtonEnabled: true,
myLocationEnabled: true,
trafficEnabled: true,
markers: _markers,
polylines: _polyLines,
onClick: (LatLng latlong) {
setState(() {
//createMarker(latlong);
});
},
);
}
void showRouteBetweenSourceAndDestination(
LatLng sourceLocation, LatLng destinationLocation) async {
RouteRequest request = RouteRequest(
origin: LocationModel(
lat: sourceLocation.lat,
lng: sourceLocation.lng,
),
destination: LocationModel(
lat: destinationLocation.lat,
lng: destinationLocation.lng,
),
);
try {
RouteResponse response = await DirectionUtils.getDirections(request);
setState(() {
drawRoute(response);
dist = response.routes[0].paths[0].distanceText;
});
} catch (Exception) {
print('Exception: Failed to load direction response');
}
}
drawRoute(RouteResponse response) {
if (_polyLines.isNotEmpty) _polyLines.clear();
if (_points.isNotEmpty) _points.clear();
var steps = response.routes[0].paths[0].steps;
for (int i = 0; i < steps.length; i++) {
for (int j = 0; j < steps[i].polyline.length; j++) {
_points.add(steps[i].polyline[j].toLatLng());
}
}
setState(() {
_polyLines.add(
Polyline(
width: 2,
polylineId: PolylineId("route"),
points: _points,
color: Colors.blueGrey),
);
/*for (int i = 0; i < _points.length - 1; i++) {
totalDistance = totalDistance +
calculateDistance(
_points[i].lat,
_points[i].lng,
_points[i + 1].lat,
_points[i + 1].lng,
);
}*/
});
}
void _loadMarkers(LatLng location) {
if (_markers.length > 0) {
setState(() {
_markers.clear();
});
} else {
setState(() {
_markers.add(Marker(
markerId: MarkerId('marker_id_1'),
position: location,
icon: _markerIcon,
infoWindow: InfoWindow(
title: 'Delivery agent',
snippet: 'location',
),
rotation: 5));
_markers.add(Marker(
markerId: MarkerId('marker_id_2'),
position: dest_location,
draggable: true,
icon: _markerIcon,
clickable: true,
infoWindow: InfoWindow(
title: 'User',
snippet: 'location',
),
rotation: 5));
});
}
}
void _customMarker(BuildContext context) async {
if (_markerIcon == null) {
final ImageConfiguration imageConfiguration =
createLocalImageConfiguration(context);
BitmapDescriptor.fromAssetImage(
imageConfiguration, 'assets/images/icon.png')
.then(_updateBitmap);
}
}
void _updateBitmap(BitmapDescriptor bitmap) {
setState(() {
_markerIcon = bitmap;
});
}
void createMarker(LatLng latLng) {
Marker marker;
marker = new Marker(
markerId: MarkerId('Welcome'),
position: LatLng(latLng.lat, latLng.lng),
icon: BitmapDescriptor.defaultMarker);
setState(() {
_markers.add(marker);
});
}
void remove() {
setState(() {
_markers.clear();
});
}
showDirection() {
Future.delayed(const Duration(seconds: 1), () {
//setState(() {
showRouteBetweenSourceAndDestination(location, dest_location);
//});
});
}
}
Result
Tips and Tricks
Make sure you have downloaded latest plugin.
Make sure that updated plugin path in yaml.
Make sure that plugin unzipped in parent directory of project.
Makes sure that agconnect-services.json file added.
Make sure dependencies are added build file.
Run flutter pug get after adding dependencies.
Generating SHA-256 certificate fingerprint in android studio and configure in Ag-connect.
ConclusionIn this article, we have learnt how to integrate Huawei Map kit and Location kit in Flutter for the DeliveryApp, where application gets the list of orders and delivery agent click on the order to navigate to map. Similar way you can use Huawei Map kit as per user requirement in your application.
Thank you so much for reading, I hope this article helps you to understand the Huawei Map kit and Location kit in flutter.
ReferencesFlutter map
Flutter plugin
Location Kit
Original Source
What are all the different types of maps it will supports?
can we implement start navigation feature like google map feature?