Exploring Chart UI component in Lite-Wearable Harmony OS - Huawei Developers

Article Introduction
In this article, I have explained how to use chart UI component to develop a health application for Huawei Lite Wearable device using Huawei DevEco Studio and using JS language in Harmony OS. Chart Component has basically two major styles line graph and bar graph. In case of Health applications like pedometer, steps tracker or any fitness application, the results are well presented if they are represented in charts daily/weekly/monthly. Here I explore how to show the accumulated health data in UI chart components.
{
"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"
}
Huawei Lite Wearable
Requirements
1) DevEco IDE
2) Lite wearable watch (Can use simulator also)
New Project (Lite Wearable)
After installation of DevEco Studio, make new project.
Select Lite Wearable in Device and select Empty Feature Ability in Template.
After the project is created, its directory as shown in below image.
Purpose of charts, why should we use it?
Charts are important UI component of the modern UI design. There are many ways to use them in watch UIs. Charts can provide information to user more clear and precise, thereby enriches the user experience. Due to space constraint of the watch UI design, charts are the most sought component for representing data visually.
Use case scenarios
We will create sample health application which will record steps and heart rate, and we will focus more on how to use the charts UI.
Show steps count per hour in a day
Show steps covered day-wise for entire week.
Show heart rate bpm per hour in a day
Step 1: Create entry page for health application using index.html
Create and add the background image for index screen using stack component.
index.html
Code:
<stack class="stack" onswipe="touchMove">
<image src='/common/wearablebackground.png' class="background"></image>
<div class="container">
<div class="pedoColumn">
<text class="data-steps">
</text>
</div>
<text class="app-title">STEPS METER</text>
<div class="pedoColumn" onclick="clickSteps">
<text class="content-title">
Today's Steps
</text>
<div class= "pedoRow">
<image src='/common/steps.png' class="stepsbackground"></image>
<text class="container-steps" >
{{stepCounterValue}}
</text>
<text class="data-steps">
Steps
</text>
</div>
</div>
<div class="pedoColumn" onclick="clickHeart">
<text class="content-title">
Current Heart rate
</text>
<div class= "pedoRow">
<image src='/common/heart.png' class="stepsbackground"></image>
<text class="container-steps">
{{hearRateValue}}
</text>
<text class="data-steps">
BPM
</text>
</div>
</div>
<div class="pedoColumn">
</div>
</div>
</stack>
index.css
Code:
.pedoColumn {
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
width: 380px;
height: 150px;
margin: 5px;
background-color: #3b3b3c;
border-radius: 10px;
}
.pedoRow {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
width: 380px;
height: 100px;
margin: 5px;
background-color: #3b3b3c;
border-radius: 10px;
}
.container {
flex-direction: column;
justify-content: center;
align-items: center;
left: 0px;
top: 0px;
width: 454px;
height: 454px;
background-color: transparent;
}
.data-steps{
text-align: left;
width: 190px;
height: 52px;
padding-left: 10px;
color: #c73d3d;
padding-top: 10px;
border-radius: 10px;
background-color: transparent;
}
.content-title{
text-align: left;
width: 290px;
height: 52px;
padding-left: 10px;
color: #d4d4d4;
padding-top: 10px;
border-radius: 10px;
background-color: transparent;
}
.container-steps{
text-align: left;
width: 70px;
height: 52px;
padding-left: 10px;
color: #d4d4d4;
padding-top: 10px;
border-radius: 10px;
margin-left: 20px;
background-color: transparent;
}
.app-title{
text-align: center;
width: 290px;
height: 52px;
color: #c73d3d;
padding-top: 10px;
border-radius: 10px;
background-color: transparent;
}
.stepsbackground {
width:76px;
height:76px;
}
Types of chart
1. Bar chart : To express large variations in data, how individual data points relate to a whole, comparisons and ranking
2. Line chart : To express minor variations in data.
Desired chart can be create with field “type” in chart class as show below.
Code:
<chart class="chartContainer" type="bar" options="{{chartOptions}}"
datasets="{{arrayData}}"></chart>
Chart class takes two important attributes options and data sets. Line graph provides with more flexibility with options section when compared to bar charts.
Step 2: Show steps count per hour in a day
The average person accumulates 3,000 to 4,000 steps per day. Regular physical activity has been shown to reduce the risk of certain chronic diseases, including high blood pressure, stroke and heart diseases. Most performed physical activity by an average human is walking. There is always eagerness to keep track of steps thereby keep track of our activeness in day.
In general steps count shows always a pattern in day, like more number of steps in morning 8.00-10.00 am hours and evening 5.00-7.00 pm hours, this readings mostly depends on the profession you are in. We can use the bar charts to track then hourly in a day or compare hourly among the days in week. Bar charts is great tool to have it in your wrist accessible at any time.
Code:
<stack class="stack" onswipe="touchMove">
<image src='/common/werablebackground.png' class="background"></image>
<div class="container">
<div class="chartRow">
<text class="date-title">{{dateString}}</text>
<image src='/common/steps.png' class="stepsbackground"></image>
</div>
<div class="chartAxisRow">
<text class="x-title">{{yAxisMaxRange}}</text>
<chart class="chartContainer" type="bar" options="{{chartOptions}}"
datasets="{{arrayData}}"></chart>
</div>
<div class="calcRow">
<text class="item-title">6am</text>
<text class="item-title" ></text>
<text class="item-title" >6pm</text>
</div>
<div class="chartBottomRow">
<text class="date-title">{{totalSteps}}</text>
<text class="item-title">{{distanceKms}} </text>
</div>
</div>
</stack>
Here we track the steps count hourly in day starting from 6.00 am hours to 9.00 pm hours. To visually represent we need x axis and y axis information. X axis will have the time range from 6.00 am hours to 9.00 pm hours. Y axis will range from 0 to maximum number steps (it is around 150 here).
Chart class has options attribute with paramters xAxis and yAxis.
xAxis - X-axis parameters. You can set the minimum value, maximum value, and scale of the x-axis, and whether to display the x-axis
yAxis - Y-axis parameters. You can set the minimum value, maximum value, and scale of the y-axis, and whether to display the y-axis
Code:
chartOptions: {
xAxis: {
min: 0,
max: 25,
axisTick: 18,
display: true,
color: "#26d9fd",
},
yAxis: {
min: 0,
max: 200,
axisTick: 10,
display: true,
color: "#26d9fd",
}
},
min - corresponds to minimum value of the axis should start
max - corresponds to minimum value of the axis should end
color – color of the axis
display – true/false visibility of the x/y axis line
axisTick - Number of scales displayed on the x-axis. The value ranges from 1 to 20. The display effect depends on the calculation result of Number of pixels occupied by the image width/(max-min).
Lite wearables support integer calculation, and an error may occur in the case of inexhaustible division. Specifically, a segment of space may be left at the end of the x-axis.
In the bar chart, the number of bars in each group of data is the same as the number of scales, and the bars are displayed at the scales.
Chart class has datasets which takes the data required for graph. It has parameters like data, gradient, strokeColor and fillColor
Code:
arrayData: [{
data: [10, 0, 20, 101, 44, 56, 1, 10, 153, 41, 111, 30, 39, 0, 0, 10, 0, 13],
gradient: true,
strokeColor: "#266571",
fillColor: "#26d9fd",
},
],
Step 3: Show steps covered day-wise for entire week.
To compare steps count across the days in a week, we can use bar charts. This will let us know which days in the week the user is more active. In general the data in weekends and weekdays will have common pattern.
If the user has set steps goals for week, then we can show progress of the steps count with a progress bar too.
Code:
<progress class="progressHorizontal" type="horizontal" percent="{{progressNumber}}"></progress>
As we have seen before we can use chart class with different options. Since here the comparison is among the days in a week. The X Axis will have days Monday to Sunday.
Code:
<stack class="stack" onswipe="touchMove">
<image src='/common/wearablebackground.png' class="background"></image>
<div class="container">
<text class="date-title">
Weekly summary
</text>
<text class="date-title">
{{totalSteps}}
</text>
<div class="chartRow">
<text class="date-content">
{{goalString}}
</text>
</div>
<progress class="progressHorizontal" type="horizontal" percent="{{progressNumber}}"></progress>
<chart class="chartContainer" type="bar" options="{{chartOptions}}" datasets="{{arrayData}}"></chart>
<div class="calcRow">
<text class="item-title">
M
</text>
<text class="item-title">
T
</text>
<text class="item-title">
W
</text>
<text class="item-title">
T
</text>
<text class="item-title">
F
</text>
<text class="item-title">
S
</text>
<text class="item-title">
S
</text>
</div>
</div>
</stack>
Chart class has options attribute with paramters xAxis and yAxis.
Code:
chartOptions: {
xAxis: {
min: 0,
max: 10,
axisTick: 7,
display: true,
color: "#8781b4",
},
yAxis: {
min: 0,
max: 1200,
axisTick: 10,
display: false,
color: "#8781b4",
},
},
Chart class has datasets which takes the data required for graph
Code:
arrayData: [{
data: [1078, 209, 109, 1011, 147, 560, 709],
gradient: true,
strokeColor: "#266571",
fillColor: "#8781b4",
},
],
Step 3: Show heart rate in bpm per hour in a day
Heart rate can be recorded from wrist watch sensor APIs and saved to watch storage. Then this weekly or daily data can be visually represented in line graph.
We can use line graph for heart rate visuals, because the heart rate has less variations in an entire day.
Code:
<chart class="chartContainer" type="line" options="{{chartOptions}}"
datasets="{{arrayData}}"></chart>
We are taking the x axis as the time ranging from 6.00am hours to 12.00 pm hours. Y axis has the heart rate bpm.
Code:
<stack class="stack">
<image src='/common/werablebackground.png' class="background"></image>
<div class="container" onswipe="touchMove">
<text class="item-title">
{{todayString}} Wednesday
</text>
<div class="pedoColumn" onclick="clickHeart">
<div class="pedoRow">
<text class="x-title">
{{yAxislabel}}
</text>
<chart class="chartContainer" type="line " options="{{chartOptions}}" datasets="{{arrayData}}"></chart>
</div>
</div>
<text class="content-title">
Avg bpm {{averagebpm}}
</text>
</div>
</stack>
Chart class has options attribute with paramters xAxis and yAxis.
Code:
chartOptions: {
xAxis: {
min: 0,
max: 18,
axisTick: 18,
display: true,
color: "#26d9fd",
},
yAxis:{
min: 50,
max: 110,
axisTick: 10,
display: false,
color: "#26d9fd",
},
We have attribute called series which is specific to line graph series has parameters like lineStyle, headPoint, topPoint, bottomPoint and loop.
Code:
series: {
lineStyle: {
width: 5,
smooth: true
},
headPoint:{
shape: "circle",
size: 5,
strokeWidth:2,
strokeColor:"#ff0000",
fillColor:"#26d9fd",
display:true
},
topPoint:{
shape: "circle",
size: 5,
strokeWidth:3,
strokeColor:"#ff0000",
fillColor:"#ffffff",
display:true
},
bottomPoint:{
shape: "circle",
size: 5,
strokeWidth:3,
strokeColor:"#ff0000",
fillColor:"#000100",
display:true
}
},
lineStyle- Line style, such as the line width and whether the line is smooth.
headPoint - Style and size of the white point at the start of the line.
topPoint - Style and size of the top point.
bottomPoint - Style and size of the bottom point.
Loop - Whether to start drawing again when the screen is looped
Append attribute can be adding data dynamically. Data is dynamically added to an existing data sequence. The target sequence is specified based on serial, which is the subscript of the datasets array and starts from 0. datasets[index].data is not updated. The value is incremented by 1 based on the horizontal coordinate and is related to the xAxis min/max setting.
Tips and Tricks
In this article the data for the chart UI is hardcoded in the javascript object. To create full-fledged application, the data has to come from the accumulated storage, which is updated by the sensor APIs from the watch or the mobile devices connected to the watch could be the source of the data.
Conclusion
In this article, we have learnt how to use chart UI for any health or fitness application in Harmony OS. We have seen how the graph type selection can done based on the use-case and requirement. We have seen basic UI enriching options in the chart.
References
Document
developer.harmonyos.com

Related

Using Huawei Map Kit on Flutter Applications

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.
Code:
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.
{
"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"
}
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.
Code:
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.
Code:
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.
Code:
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.
Code:
<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.
Code:
<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.
Code:
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.
Code:
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.
Huawei Map
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.
Code:
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.
Code:
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.
Code:
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.
Code:
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

Pedometer implementation using Harmony OS Lite wearable

Introduction
HarmonyOS is a future-proof distributed operating system open to you as part of the initiatives for the all-scenario strategy, adaptable to mobile office, fitness and health, social communication, and media entertainment, to name a few. Unlike a legacy operating system that runs on a standalone device, HarmonyOS is built on a distributed architecture designed based on a set of system capabilities. It is able to run on a wide range of device forms, including smartphones, tablets, wearables, smart TVs and head units.
In this article, we will create a simple pedometer application for lite wearable which will count each step user takes, distance covered and Heart rate. Also, for every 1000 steps completion, wearable device will vibrate.
Pedometer app will have 2 UI screens, first screen will have start button. Once user clicks on Start button, app will route to second screen which will show STEPS, BPM (heart rate) and METER/KM (total distance) covered.
{
"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"
}
Requirements
1) DevEco IDE
2) Lite wearable simulator (or lite wearable watch)
Implementation
First page, index.hml contains start button.
Code:
<div class="container">
<text class="title">Pedometer</text>
<text class="subtitle">Step Counter</text>
<image class= "image" src='/common/pedometer.png' ></image>
<input class="button" type="button" value="Start" onclick="start"></input>
</div>
index.css has style defined for first page.
Code:
.container {
display: flex;
justify-content: center;
align-items: center;
left: 0px;
background-color: #192841;
top: 0px;
flex-direction: column;
width: 454px;
height: 454px;
}
.title {
font-size:38px;
font-family: HYQiHei-65S;
justify-content: center;
}
.subtitle {
font-size:30px;
justify-content: center;
}
.button {
width: 200px;
height: 50px;
font-size: 30px;
margin-top: 15px;
background-color: indigo;
}
.image {
width: 128px;
height: 143px;
justify-content: center;
margin-bottom: 15px;
margin-left: 10px;
margin-top: 20px;
}
index.js contains the implementation of start button. Once user clicks on start button, app will route to second page stepcounter.hml
Code:
import router from '@system.router'
export default {
data: {
title: 'World'
},
start() {
router.replace({
uri: 'pages/stepcounter/stepcounter'
});
}
}
Second page, stepcounter.hml contains UI design for displaying step count, heart beat count and total distance covered.
Code:
<div class="container" onswipe="touchMove">
<image class= "image" src='/common/stepscount.png' ></image>
<text class="subtitle">{{stepcount}}</text>
<div class="seperator" ></div>
<image class= "image" src='/common/heartbeat.png' ></image>
<text class="subtitle">{{heartbeatcount}}</text>
<div class="seperator" ></div>
<image class= "image1" src='/common/jogging.png' ></image>
<text class="subtitle">{{distance}}</text>
</div>
stepcounter.css has style defined for second page.
Code:
.container {
flex-direction: column;
justify-content: center;
align-items: center;
left: 0px;
top: 0px;
width: 454px;
height: 454px;
background-color: #192841;
}
.title {
font-size:38px;
font-family: HYQiHei-65S;
justify-content: center;
}
.subtitle {
font-size:30px;
color: lightgrey;
margin: 15px;
justify-content: center;
}
.seperator {
height: 1px;
width: 454px;
margin-bottom: 5px;
margin-top: 5px;
background-color: white;
}
.image {
width: 48px;
height: 48px;
justify-content: center;
margin-bottom: 5px;
}
.image1 {
width: 48px;
height: 48px;
justify-content: center;
margin-bottom: 5px;
margin-top: 15px;
}
stepcounter.js has implementation of various APIs.
1. Body state API
This API is used to listen for changes of the sensor wearing state. We will use this API to know if user is wearing wearable device or not.
Code:
// to listen the sensor wearing state, returns true if wear is in wrist
sensor.subscribeOnBodyState({
success: function(response) {
console.log('get on-body state value:' + response.value);
if(response.value === true) {
// get the heart rate
_this.getHeartBeatCount();
}
},
fail: function(data, code) {
console.log('fail to get on body state, code:' + code + ', data: ' + data);
},
});
For more, you can check https://forums.developer.huawei.com/forumPortal/en/topic/0202458325873420042

Beginner: Develop Tic-Tac-Toe application for Lite-Wearable in Harmony OS

Article Introduction
In this article, I have explained to develop a Tic-Tac-Toe application for Huawei Lite wearable device using Huawei DevEco studio and using JS language in Harmony OS. Tic-Tac-Toe is a game for two players, X and O, who take turns marking the spaces in a 3×3 grid. The player who succeeds in placing three of their marks in a diagonal, horizontal, or vertical row will be a winner.
{
"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"
}
Huawei Lite Wearable
Requirements
1) DevEco IDE
2) Lite wearable watch (Can use simulator also)
New Project (Lite Wearable)
After installation of DevEco Studio, make new project.
Select Lite Wearable in Device and select Empty Feature Ability in Template.
After the project is created, its directory as shown in below displayed image.
hml files describe the page layout.
css files describe the page style.
js files process the interactions between pages and users.
The app.js file manages global JavaScript logics and application lifecycle.
The pages directory stores all component pages.
The common directory stores public resource files, such as media resources and .js files.
Integration process
Design the UI
Step 1: Add background image.
As the first step, we can create a UI that contains tictactoe cell boxes which will be filled by the user entries. Create and add the background image for tictactoe screen using stack component.
index.html
HTML:
<stack class="stack">
<image src='/common/wearablebackground.png' class="background"></image>
index.css
CSS:
.background {
width:454px;
height:454px;
}
.stack {
width: 454px;
height: 454px;
justify-content: center;
}
Step 2: Add title for game. Add the display text for the current player.
Add the storage text for player and gameOver string to display that is game over after the game is completed. Here we use conditional UI rendering that when the Boolean gameOver is true, then display the gameOverString.
index.html
HTML:
<text class="app-title">{{title}} </text>
<text class="sub-title">{{playerString}}
</text>
<div class="uiRow"if="{{gameOver}}" >
<text if="{{gameOver}}" class="app-title">{{gameOverString}}</text>
</div>
index.css
CSS:
.app-title{
text-align: center;
width: 290px;
height: 52px;
color: #c73d3d;
padding-top: 10px;
margin-bottom: 30px;
border-radius: 10px;
background-color: transparent;
}
.sub-title{
text-align: center;
width: 290px;
height: 52px;
color: #26d9fd;
padding-top: 10px;
border-radius: 10px;
background-color: transparent;
}
index.js
JavaScript:
title: "Tic Tac Toe",
playerString: "Player One - O",
Step 3: Add UI 3x3 grid call for application.
We need 3x3 matrix of text boxes. Use loop rendering for the boxes since all are similar boxes. I have added animation for the boxes to make it more appealing.
HTML:
<div class="boxRow" for="{{cellValue in gameEntries}}" tid="id" else>
<text class="cell" onclick="handleCellClick($idx, 0)" >{{cellValue[0]}}</text>
<text class="cell" onclick="handleCellClick($idx, 1)">{{cellValue[1]}}</text>
<text class="cell" onclick="handleCellClick($idx, 2)">{{cellValue[2]}}</text>
</div>
CSS:
.boxRow {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
width: 247px;
height: 64px;
background-color: #000000;
animation-name: Go;
animation-duration: 2s;
animation-delay: 0;
animation-timing-function: linear;
animation-iteration-count: infinite;
border-radius: 5px;
}
.cell {
display: flex;
text-align: center;
width: 75px;
height: 50px;
border-width: 1px;
color: #414343;
background-color: #FFD700;
border-color: #414343;
border-radius: 5px;
margin: 5px;
}
So to check the condition iterate through the entries in 3x3 array. We are converting 3x3 array element location to index of the grid using modulo and math functions.
Read full article
Result
Tips and Tricks
You can use Lite-wearable simulator for development. We can extend 3x3 grid for higher order Tic-Tac-Toe just by increasing game entry matrix to 5x5 or 7x7.
Conclusion
In this article, we have learnt how to create simple game app Tic-Tac-Toe using various Harmony OS UI components of course, there are a lot more things we could do here, like make the game actually multiplayer, so you can play with a friend.
References
https://developer.harmonyos.com/en/...ces/lite-wearable-syntax-hml-0000001060407093
Read full article
Well explained, what are all the limitations in Harmony OS?

Case Study for Card Development Using Pseudo-Classes

Symptom
While developing a card, I used a pseudo-class for a component to implement a color changing effect. However, the color cannot be restored.
For example, the original background color was as follows.
{
"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"
}
This figure shows the new background color after using the pseudo-class.
In normal cases, the card’s background color is changed upon tapping, and will return to the original color when you lift your finger.
This problem is caused by incomplete pseudo-classes for cards supported by Huawei Quick App Loader. Even so, there’s still an easy solution.
Solution
Simply add a tap event to the component using the pseudo-class. No logic processing is needed.
Sample code:
CSS:
<div class="sitetype_box" widgetid="8e4bf1ca-f716-46f8-8614-16d1b35002c5" onclick="test">
</div>
CSS style:
.sitetype_box {
flex-direction: column;
background-color:#FFBF00;
padding: dpConvert(0) dpConvert($elementsMarginHorizontalL) dpConvert(0) dpConvert($elementsMarginHorizontalL);
}
/** Pseudo-class */
.sitetype_box :active{
background-color: #E40078;
}
Method:
JavaScript:
test(){
console.log("message");
}
Reference:
Pseudo-classes for quick apps:
https://developer.huawei.com/consumer/en/doc/development/quickApp-References/quickapp-style#h1-1578402521212
New member here
nice
wonderful sharing!!
Thanks
Nice
good
omg, I thought I am the only one with this problem, thanks for providing the solution
Thanks
Hello from pakistan
JessicajK said:
omg, I thought I am the only one with this problem, thanks for providing the solution
Click to expand...
Click to collapse
It's an honor to solve your problem.
Mayism said:
Symptom
While developing a card, I used a pseudo-class for a component to implement a color changing effect. However, the color cannot be restored.
For example, the original background color was as follows.
This figure shows the new background color after using the pseudo-class.
In normal cases, the card’s background color is changed upon tapping, and will return to the original color when you lift your finger.
This problem is caused by incomplete pseudo-classes for cards supported by Huawei Quick App Loader. Even so, there’s still an easy solution.
Solution
Simply add a tap event to the component using the pseudo-class. No logic processing is needed.
Sample code:
CSS:
<div class="sitetype_box" widgetid="8e4bf1ca-f716-46f8-8614-16d1b35002c5" onclick="test">
</div>
CSS style:
.sitetype_box {
flex-direction: column;
background-color:#FFBF00;
padding: dpConvert(0) dpConvert($elementsMarginHorizontalL) dpConvert(0) dpConvert($elementsMarginHorizontalL);
}
/** Pseudo-class */
.sitetype_box :active{
background-color: #E40078;
}
Method:
JavaScript:
test(){
console.log("message");
}
Reference:
Pseudo-classes for quick apps:
https://developer.huawei.com/consumer/en/doc/development/quickApp-References/quickapp-style#h1-1578402521212
Click to expand...
Click to collapse
Mayism said:
Symptom
While developing a card, I used a pseudo-class for a component to implement a color changing effect. However, the color cannot be restored.
For example, the original background color was as follows.
This figure shows the new background color after using the pseudo-class.
In normal cases, the card’s background color is changed upon tapping, and will return to the original color when you lift your finger.
This problem is caused by incomplete pseudo-classes for cards supported by Huawei Quick App Loader. Even so, there’s still an easy solution.
Solution
Simply add a tap event to the component using the pseudo-class. No logic processing is needed.
Sample code:
CSS:
<div class="sitetype_box" widgetid="8e4bf1ca-f716-46f8-8614-16d1b35002c5" onclick="test">
</div>
CSS style:
.sitetype_box {
flex-direction: column;
background-color:#FFBF00;
padding: dpConvert(0) dpConvert($elementsMarginHorizontalL) dpConvert(0) dpConvert($elementsMarginHorizontalL);
}
/** Pseudo-class */
.sitetype_box :active{
background-color: #E40078;
}
Method:
JavaScript:
test(){
console.log("message");
}
Reference:
Pseudo-classes for quick apps:
https://developer.huawei.com/consumer/en/doc/development/quickApp-References/quickapp-style#h1-1578402521212
Click to expand...
Click to collapse
Mayism said:
Symptom
While developing a card, I used a pseudo-class for a component to implement a color changing effect. However, the color cannot be restored.
For example, the original background color was as follows.
This figure shows the new background color after using the pseudo-class.
In normal cases, the card’s background color is changed upon tapping, and will return to the original color when you lift your finger.
This problem is caused by incomplete pseudo-classes for cards supported by Huawei Quick App Loader. Even so, there’s still an easy solution.
Solution
Simply add a tap event to the component using the pseudo-class. No logic processing is needed.
Sample code:
CSS:
<div class="sitetype_box" widgetid="8e4bf1ca-f716-46f8-8614-16d1b35002c5" onclick="test">
</div>
CSS style:
.sitetype_box {
flex-direction: column;
background-color:#FFBF00;
padding: dpConvert(0) dpConvert($elementsMarginHorizontalL) dpConvert(0) dpConvert($elementsMarginHorizontalL);
}
/** Pseudo-class */
.sitetype_box :active{
background-color: #E40078;
}
Method:
JavaScript:
test(){
console.log("message");
}
Reference:
Pseudo-classes for quick apps:
https://developer.huawei.com/consumer/en/doc/development/quickApp-References/quickapp-style#h1-1578402521212
Click to expand...
Click to collapse

A Button Implemented by the Input Component Does Not Take Effect After Being Tapped

Symptom​After type of the input component is set to button and the border-radius attribute is set, the button does not take effect after being tapped. After the border-radius attribute is deleted, the button can work properly.
The code where the exception occurs is as follows:
Code:
<template>
<div class="page-wrapper">
<input type="button" class="button" value="Animation" />
</div>
</template>
<script>
</script>
<style>
.page-wrapper {
flex-direction: column;
justify-content: center;
align-items: center;
}
.button {
color: #20a0ff;
background-color: red;
padding: 10px 20px;
border-radius: 40px;
}
</style>
Cause Analysis​After the border-radius attribute is set, the bottom layer of the quick app engine is restricted. As a result, the tap effect cannot be automatically implemented.
Solution​You can use the pseudo-class of the quick app to implement the button tap effect.
Optimized code:
Code:
<template>
<div class="page-wrapper">
<input type="button" value="Animation" />
</div>
</template>
<script>
</script>
<style>
.page-wrapper {
flex-direction: column;
justify-content: center;
align-items: center;
}
.button {
color: #20a0ff;
background-color: red;
padding: 10px 20px;
border-radius: 40px;
}
.button:active{
background-color: green;
}
</style>
Checkout in forum

Categories

Resources