Intermediate: Securing Taxi booking application using Huawei Safety detect in flutter - Huawei Developers

{
"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
In this article, we will learn about Huawei Safety detect kit.
What is Safety detect?
Safety Detect builds robust security capabilities, including system integrity check (SysIntegrity), app security check (AppsCheck), malicious URL check (URLCheck), fake user detection (UserDetect), and malicious Wi-Fi detection (WifiDetect), into your app, effectively protecting it against security threats.
Why do we need to use safety detect?
Mobile applications capture almost 90% of people’s time on mobile devices, while the rest of the time is spent browsing the web. So basically now a day’s mobile usage is more than the web. Since all the users are using smart mobile phones for daily needs like, reading news, email, online shopping, booking taxi, and wallets to pay, educational apps etc. Even for banking transaction there was time people use to stand in the queue to deposit or withdraw, but now everything can be done in mobile application with just 2 to 3 clicks. Since everything happening over the phone definitely we should provide security to mobile apps.
Now let’s see what all are the security features provided by Huawei Safety detect kit.
SysIntegrity
AppsCheck
URLCheck
UserDetect
WifiDetect
Integration of Safety detect
1. Configure application on the AGC.
2. Client application development process.
Configure application on the AGC
Follow the steps.
Step 1: We need to register as a developer account in AppGallery Connect. If you are already developer ignore this step.
Step 2: Create an app by referring to Creating a Project and Creating an App in the Project
Step 3: Set the data storage location based on current location.
Step 4: Enabling Safety detect. Open AppGallery connect, choose Manage API > Safety Detect
Step 5: Generating a Signing Certificate Fingerprint.
Step 6: Configuring the Signing Certificate Fingerprint.
Step 7: Download your agconnect-services.json file, paste it into the app root directory.
Client application development process
Follow the steps
Step 1: Create flutter application in the Android studio (Any IDE which is your favorite).
Step 2: Add the App level gradle dependencies. Choose inside project Android > app > build.gradle.
1
2apply plugin: 'com.android.application'
apply plugin: 'com.huawei.agconnect'
Root level gradle dependencies.
1
2maven { url 'https://developer.huawei.com/repo/' }
classpath 'com.huawei.agconnect:agcp:1.4.1.300'
Step 3: Download plugin from here
Step 4: Add downloaded file into outside project directory. Declare plugin path in pubspec.yaml file under dependencies.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30dependencies:
flutter:
sdk: flutter
huawei_account:
path: ../huawei_account/
huawei_location:
path: ../huawei_location/
huawei_map:
path: ../huawei_map/
huawei_analytics:
path: ../huawei_analytics/
huawei_site:
path: ../huawei_site/
huawei_push:
path: ../huawei_push/
huawei_dtm:
path: ../huawei_dtm/
huawei_ml:
path: ../huawei_ml/
huawei_safetydetect:
path: ../huawei_safetydetect/
agconnect_crash: ^1.0.0
agconnect_remote_config: ^1.0.0
http: ^0.12.2
camera:
path_provider:
path:
image_picker:
fluttertoast: ^7.1.6
shared_preferences: ^0.5.12+4
Step 5: Build flutter application
In this example, I’m checking user detection in the taxi booking application. Whenever user sign up with Huawei, I’m checking user detection success, then it will be navigating user to home screen to book a cab.
SysIntegrity: Checks whether the device running your app is secure, for example, whether it is rooted. The SysIntegrity API is called to check the system integrity of a device. If the device is not safe, appropriate measures are taken.
AppsCheck: Obtains a list of malicious apps. You can obtain all malicious applications and evaluate whether you can restrict the behavior of your application based on the risk.
You can directly call the getMaliciousAppsList() method to get all the malicious apps.
URLCheck: Determines the threat type of a specific URL. You can determine the dangerous URLs using URL Check API. Currently UrlSafety API provide determinate Malware and Phishing threats. When you visit a URL, this API checks whether the URL is a malicious one. If so, you can evaluate the risk and alert the user about the risk or block the URL.
User Detect: Checks whether your app is interacting with a fake user. This API can help your app to prevent batch registration, credential stuffing attacks, activity bonus hunting, and content crawling. If a user is a suspicious one or risky one, a verification code is sent to the user for secondary verification. If the detection result indicates that the user is a real one, the user can sign in to my app. Otherwise, the user is not allowed to Home page.
WifiDetect: Checks whether the Wi-Fi to be connected is secure. This API checks characteristics of the Wi-Fi and router to be connected, analyzes the Wi-Fi information, and returns the Wi-Fi detection results after classification, helping you to prevent possible attacks to your app from malicious Wi-Fi. If attacks are detected, app can interrupt the user operation or it will asks user permission.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505import 'dart:convert';
import 'dart:math';
import 'dart:typed_data';
import 'package:agconnect_crash/agconnect_crash.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:huawei_account/huawei_account.dart';
import 'package:huawei_safetydetect/huawei_safetydetect.dart';
import 'package:taxibooking/analytics/analyticsutil.dart';
import 'package:taxibooking/constants/constants.dart';
import 'package:taxibooking/models/accountmodel.dart';
import 'package:taxibooking/ui/accountscreen.dart';
import 'package:taxibooking/ui/widgets/custom_shape.dart';
import 'package:taxibooking/ui/widgets/customappbar.dart';
import 'package:taxibooking/ui/widgets/responsive_ui.dart';
import 'package:taxibooking/ui/widgets/textformfield.dart';
import 'package:taxibooking/utils/validator.dart';
import 'home.dart';
class SignUpScreen extends StatefulWidget {
@override
_SignUpScreenState createState() => _SignUpScreenState();
}
class _SignUpScreenState extends State<SignUpScreen> {
bool checkBoxValue = false;
double _height;
double _width;
double _pixelRatio;
bool _large;
bool _medium;
String appId;
@override
Widget build(BuildContext context) {
_height = MediaQuery.of(context).size.height;
_width = MediaQuery.of(context).size.width;
_pixelRatio = MediaQuery.of(context).devicePixelRatio;
_large = ResponsiveWidget.isScreenLarge(_width, _pixelRatio);
_medium = ResponsiveWidget.isScreenMedium(_width, _pixelRatio);
return Material(
child: Scaffold(
body: Container(
height: _height,
width: _width,
margin: EdgeInsets.only(bottom: 5),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Opacity(opacity: 0.88, child: CustomAppBar()),
clipShape(),
form(),
acceptTermsTextRow(),
SizedBox(
height: _height / 35,
),
button(),
infoTextRow(),
socialIconsRow(),
signInTextRow(),
],
),
),
),
),
);
}
@override
void initState() {
super.initState();
getAppId();
//getWifiDetectStatus();
AnalyticsUtil.pageEnd("Sign up screen");
}
@override
void dispose() {
super.dispose();
AnalyticsUtil.pageEnd("Sign up screen");
}
Widget clipShape() {
return Stack(
children: <Widget>[
Opacity(
opacity: 0.75,
child: ClipPath(
clipper: CustomShapeClipper(),
child: Container(
height: _large
? _height / 8
: (_medium ? _height / 7 : _height / 6.5),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.orange[200], Colors.pinkAccent],
),
),
),
),
),
Opacity(
opacity: 0.5,
child: ClipPath(
clipper: CustomShapeClipper2(),
child: Container(
height: _large
? _height / 12
: (_medium ? _height / 11 : _height / 10),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.orange[200], Colors.pinkAccent],
),
),
),
),
),
Container(
height: _height / 5.5,
alignment: Alignment.center,
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
spreadRadius: 0.0,
color: Colors.black26,
offset: Offset(1.0, 10.0),
blurRadius: 20.0),
],
color: Colors.white,
shape: BoxShape.circle,
),
child: GestureDetector(
onTap: () {
print('Adding photo');
},
child: Icon(
Icons.add_a_photo,
size: _large ? 40 : (_medium ? 33 : 31),
color: Colors.red[500],
)),
),
],
);
}
Widget form() {
return Container(
margin: EdgeInsets.only(
left: _width / 12.0, right: _width / 12.0, top: _height / 20.0),
child: Form(
child: Column(
children: <Widget>[
firstNameTextFormField(),
SizedBox(height: _height / 60.0),
lastNameTextFormField(),
SizedBox(height: _height / 60.0),
emailTextFormField(),
SizedBox(height: _height / 60.0),
phoneTextFormField(),
SizedBox(height: _height / 60.0),
passwordTextFormField(),
],
),
),
);
}
Widget firstNameTextFormField() {
return CustomTextField(
keyboardType: TextInputType.text,
icon: Icons.person,
hint: "First Name",
);
}
Widget lastNameTextFormField() {
return CustomTextField(
keyboardType: TextInputType.text,
icon: Icons.person,
hint: "Last Name",
);
}
Widget emailTextFormField() {
return CustomTextField(
keyboardType: TextInputType.emailAddress,
icon: Icons.email,
hint: "Email ID",
);
}
Widget phoneTextFormField() {
return CustomTextField(
keyboardType: TextInputType.number,
icon: Icons.phone,
hint: "Mobile Number",
);
}
Widget passwordTextFormField() {
return CustomTextField(
keyboardType: TextInputType.text,
obscureText: true,
icon: Icons.lock,
hint: "Password",
);
}
Widget acceptTermsTextRow() {
return Container(
margin: EdgeInsets.only(top: _height / 100.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Checkbox(
activeColor: Colors.red[500],
value: checkBoxValue,
onChanged: (bool newValue) {
setState(() {
checkBoxValue = newValue;
});
}),
Text(
"I accept all terms and conditions",
style: TextStyle(
fontWeight: FontWeight.w400,
fontSize: _large ? 12 : (_medium ? 11 : 10)),
),
],
),
);
}
Widget button() {
return RaisedButton(
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30.0)),
onPressed: () {
print("Routing to your account");
},
textColor: Colors.white,
padding: EdgeInsets.all(0.0),
child: Container(
alignment: Alignment.center,
// height: _height / 20,
width: _large ? _width / 4 : (_medium ? _width / 3.75 : _width / 3.5),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20.0)),
gradient: LinearGradient(
colors: <Color>[Colors.yellow[800], Colors.red],
),
),
padding: const EdgeInsets.all(12.0),
child: Text(
'SIGN UP',
style: TextStyle(fontSize: _large ? 14 : (_medium ? 12 : 10)),
),
),
);
}
Widget infoTextRow() {
return Container(
margin: EdgeInsets.only(top: _height / 40.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"Or create using social media",
style: TextStyle(
fontWeight: FontWeight.w400,
fontSize: _large ? 12 : (_medium ? 11 : 10)),
),
],
),
);
}
Widget socialIconsRow() {
return Container(
margin: EdgeInsets.only(top: _height / 80.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
GestureDetector(
onTap: () {
print("Huawei sign in clicked");
signInWithHuaweiAccount();
},
child: CircleAvatar(
radius: 25.0,
backgroundImage:
AssetImage("assets/images/huaweilogo_288x288.png"),
),
),
SizedBox(
width: 20,
),
CircleAvatar(
radius: 25,
backgroundImage: AssetImage("assets/images/fblogo.jpg"),
),
SizedBox(
width: 20,
),
CircleAvatar(
radius: 25,
backgroundImage: AssetImage("assets/images/twitterlogo.jpg"),
),
],
),
);
}
Widget signInTextRow() {
return Container(
margin: EdgeInsets.only(top: _height / 20.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"Already have an account?",
style: TextStyle(fontWeight: FontWeight.w400),
),
SizedBox(
width: 5,
),
GestureDetector(
onTap: () {
Navigator.of(context).pop(SIGN_IN);
print("Routing to Sign up screen");
},
child: Text(
"Sign in",
style: TextStyle(
fontWeight: FontWeight.w800,
color: Colors.red[800],
fontSize: 19),
),
)
],
),
);
}
void signInWithHuaweiAccount() async {
HmsAuthParamHelper authParamHelper = new HmsAuthParamHelper();
authParamHelper
..setIdToken()
..setAuthorizationCode()
..setAccessToken()
..setProfile()
..setEmail()
..setScopeList([HmsScope.openId, HmsScope.email, HmsScope.profile])
..setRequestCode(8888);
try {
final HmsAuthHuaweiId accountInfo =
await HmsAuthService.signIn(authParamHelper: authParamHelper);
setState(() {
var accountDetails = AccountInformation(
accountInfo.displayName,
accountInfo.email,
accountInfo.givenName,
accountInfo.familyName,
accountInfo.avatarUriString);
AnalyticsUtil.addCustomEvent(
accountInfo.displayName,
accountInfo.email,
accountInfo.givenName,
accountInfo.familyName,
accountInfo.avatarUriString);
AnalyticsUtil.setUserId("ABC123456789");
AnalyticsUtil.setUserProfile(accountInfo.displayName);
AnalyticsUtil.getAnalyticsClient()
.setUserProfile("Email", accountInfo.email);
AnalyticsUtil.getAnalyticsClient()
.setUserProfile("Family Name", accountInfo.familyName);
AnalyticsUtil.getAnalyticsClient()
.setUserProfile("Profile pic", accountInfo.avatarUriString);
/* AGCCrash.instance.setUserId("ABC123456789");
AGCCrash.instance.setCustomKey("Email", accountInfo.email);
AGCCrash.instance.setCustomKey("Family Name", accountInfo.familyName);
AGCCrash.instance
.setCustomKey("Profile pic", accountInfo.avatarUriString);
AGCCrash.instance.log(
level: LogLevel.info,
message: "Mr: " +
accountInfo.displayName +
"has successfully logged in");
AGCCrash.instance.testIt();*/
/*Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Homepage(
accountInformation: accountDetails,
),
));*/
detectUser(accountDetails);
print("account name: " + accountInfo.displayName);
});
} on Exception catch (exception) {
print(exception.toString());
print("error: " + exception.toString());
/* AGCCrash.instance
.log(level: LogLevel.error, message: exception.toString());
AGCCrash.instance.testIt();*/
}
}
//safety detect
getAppId() async {
String appID = await SafetyDetect.getAppID;
setState(() {
appId = appID;
Validator().showToast("App Id: "+appID);
});
}
checkSysIntegrity() async {
Random secureRandom = Random.secure();
List randomIntegers = List<int>();
for (var i = 0; i < 24; i++) {
randomIntegers.add(secureRandom.nextInt(255));
}
Uint8List nonce = Uint8List.fromList(randomIntegers);
try {
String result = await SafetyDetect.sysIntegrity(nonce, appId);
List<String> jwsSplit = result.split(".");
String decodedText = utf8.decode(base64Url.decode(jwsSplit[1]));
Validator().showToast("SysIntegrityCheck result is: $decodedText");
} on PlatformException catch (e) {
Validator().showToast("Error occurred while getting SysIntegrityResult. Error is : $e");
}
}
void getMaliciousAppsList() async {
List<MaliciousAppData> maliciousApps = List();
maliciousApps = await SafetyDetect.getMaliciousAppsList();
setState(() {
Validator().showToast("malicious apps: ${maliciousApps.toString()}");
});
}
detectUser(AccountInformation accountDetails) async {
try {
String token = await SafetyDetect.userDetection(appId);
print("User verification succeeded, user token: $token");
if(token!=null){
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Homepage(
accountInformation: accountDetails,
),
));
}else{
Validator().showToast("user detection failed");
}
} on PlatformException catch (e) {
print(
"Error occurred: " + e.code + ":" + SafetyDetectStatusCodes[e.code]);
}
}
void loadUrl(String url) async {
Future.delayed(const Duration(seconds: 5), () async {
urlCheck(url);
});
}
void urlCheck(String url) async {
List<UrlThreatType> threatTypes = [
UrlThreatType.malware,
UrlThreatType.phishing
];
List<UrlCheckThreat> urlCheckResults =
await SafetyDetect.urlCheck(url, appId, threatTypes);
if (urlCheckResults.length == 0) {
Validator().showToast("No threat is detected for the URL");
} else {
urlCheckResults.forEach((element) {
print("${element.getUrlThreatType} is detected on the URL");
});
}
}
getWifiDetectStatus() async {
try {
WifiDetectResponse wifiDetectStatus =
await SafetyDetect.getWifiDetectStatus();
Validator().showToast('Wifi detect status is: ${wifiDetectStatus.getWifiDetectType.toString()}');
} on PlatformException catch (e) {
if (e.code.toString() == "19003") {
Validator().showToast(' The WifiDetect API is unavailable in this region');
}
}
}
}
Result
Tips and Tricks
Download latest HMS Flutter plugin.
Check dependencies downloaded properly.
Latest HMS Core APK is required.
Set minSDK 19 or later.
WifiDetect function available only in Chinese mainland.
UserDetect function not available in Chinese mainland.
Conclusion
In this article, we have learnt integration of Huawei safety detect and types of security provided by Huawei safety detect kit. Nowadays everything is happening over the phone, so as developer it is our responsibility to provide security to application. Otherwise users do not use the application. Thank Huawei for giving such great Safety detect kit to build secure application.
Reference
Safety detect
Happy coding​

Related

Using Map Kit with Flutter

{
"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"
}
Hello everyone,
In this article, I will talk about how to use HMS Map Kit in Flutter applications and I will share sample codes for all features of Map Kit.
Today, Maps are the basis of many mobile applications. Unfortunately, finding resources for the integration of maps into applications developed with Flutter is more difficult than native applications. I hope this post will be a good resource for seamlessly integrating HMS Map Kit into your Flutter applications.
What is Map Kit ?
HMS Map Kit currently include all map data of more than 200 countries and regions and supports more than 100 languages.
HMS Map Kit is a Huawei Service that is easy to integrate, has a wide range of use and offers a variety of features. Moreover, Map Kit is constantly updated to enrich its data and reflect the differences on the map even at small scales.
To customize your maps, you can add markers, add rings, lines on the map. Map Kit offers us a wide range of uses to include everything you need on the map. You can see your your location live on the map, you can zoom and change the direction of the map. You can also see live traffic on the map. I think this is one of the most important features that should be in a map. I can say that Huawei has done a very successful job in reflecting traffic data on the map instantly. Finally, you can see the world’s most important locations in 3D thanks to Huawei Maps. I am sure that this feature will add a excitement to the map experience in your mobile application.
Note: HMS Map Kit works with EMUI 5.0 and above versions on Huawei devices and Android 7.0 and above on non-Huawei devices.
Development Steps
1. Create Your App in AppGallery Connect
Firstly you should be create a developer account in AppGallery Connect. After create your developer account, you can create a new project and new app. You can find a detail of these steps on the below.
2. Add Flutter Map Kit to Your Project
After creating your application on the AGC Console and activated Map Kit, the agconnect-services file should be added to the project first.
The agconnect-services.json configuration file should be added under the android/app directory in the Flutter project.
Next, the following dependencies for HMS usage need to be added to the build.gradle file under the android directory.
Code:
buildscript {
repositories {
google()
jcenter()
maven {url 'https://developer.huawei.com/repo/'}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
classpath 'com.huawei.agconnect:agcp:1.4.2.301'
}
}
allprojects {
repositories {
google()
jcenter()
maven {url 'https://developer.huawei.com/repo/'}
}
}
Then add the following line of code to the build.gradle file under the android/app directory.
Code:
apply plugin: 'com.huawei.agconnect'
Add the following permissions to use the map to the AndroidManifest.xml file.
Code:
<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"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
Finally, the Map Kit SDK should be added to the pubspec.yaml file. To do this, open the pubspec.yaml file and add the required dependency as follows.
Code:
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
huawei_map: ^5.0.3+302
And, by clicking “pub get”, the dependencies are added to Android Studio. After all these steps are completed, your app is ready to code.
3. Crate a Map
Firstly, create a HuaweiMapController object for create your map. Create a method called onMapCreated and set this object here for load the map when the application is opened.
Next, define a center coordinate and a zoom value for that coordinate. These values will use while map is opening.
Finally, after adding your map as a design, you will get a class coded as follows. For now, the screenshot of your application will also be as follows.
Code:
class MapPage extends StatefulWidget {
@override
_MapPageState createState() => _MapPageState();
}
class _MapPageState extends State<MapPage> {
HuaweiMapController _huaweiMapController;
static const LatLng _centerPoint = const LatLng(41.043982, 29.014333);
static const double _zoom = 12;
bool _cameraPosChanged = false;
bool _trafficEnabled = false;
@override
void initState() {
super.initState();
}
void onMapCreated(HuaweiMapController controller) {
_huaweiMapController = controller;
}
@override
Widget build(BuildContext context) {
final huaweiMap = HuaweiMap(
onMapCreated: onMapCreated,
mapType: MapType.normal,
tiltGesturesEnabled: true,
buildingsEnabled: true,
compassEnabled: true,
zoomControlsEnabled: true,
rotateGesturesEnabled: true,
myLocationButtonEnabled: true,
myLocationEnabled: true,
trafficEnabled: _trafficEnabled,
markers: _markers,
polylines: _polylines,
polygons: _polygons,
circles: _circles,
onClick: (LatLng latLng) {
log("Map Clicked at $latLng");
},
onLongPress: (LatLng latlng) {
log("Map LongClicked at $latlng");
},
initialCameraPosition: CameraPosition(
target: _centerPoint,
zoom: _zoom,
),
);
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Map Kit', style: TextStyle(
color: Colors.black
)),
backgroundColor: Color(0xFFF9C335),
),
body: Stack(
children: <Widget>[
huaweiMap
],
),
),
);
}
}
As you can see in the code above, we need some parameters while creating the map. The explanation and intended use of some of the most important and most used parameters are as follows.
mapType : It represents the type of map loaded. Currently there is only 2 map type support for Flutter. These are “normal” and “none”. If mapType is none, map will not be loaded. The normal type map is as seen in the image above.
zoomControlsEnabled : It represents the visibility of the zoom buttons on the right of the map. If you set this value as “true”, the buttons are automatically loaded and used on the map as above. If you set as “false”, you cannot zoom in on the map with these buttons.
myLocationEnabled : It represents whether you can see your own instant location on the map. If you set it to “true”, your location will appear as a blue point on the map. If you set it as “false”, the user location will not seen on the map.
myLocationButtonEnabled : It represents the button just below the zoom buttons at the bottom right of the map. If you have set the value of myLocationEnabled as true, when you click the button the map will automatically zoom to your location.
onClick : Here you can define the events you want to be triggered when tapped on the map. As seen in the example above, when I click on the map, I print the latitude and longitude information of the relevant point.
onLongPress : Events that will be triggered by a long tap on the map should be defined here. As you can see in the example, when I touch the map long, I print the latitude and longitude information of the relevant point.
initialCameraPosition : The starting position and zoom value to be displayed when the map is loaded must be defined here.
4. Show Traffic Data on the Map
When I was talking about the features of the Map Kit, I just mentioned that this is the feature that I like the most. It is both functional and easy to use.
To display live traffic data with a one touch, you can set the “trafficEnabled” value that we defined while creating the map to “true”.
To do this, design a small, round button on the left side of the map and prepare a method called trafficButtonOnClick. This method changes the trafficEnabled value to true and false each time the button is pressed.
Code:
void trafficButtonOnClick() {
if (_trafficEnabled) {
setState(() {
_trafficEnabled = false;
});
} else {
setState(() {
_trafficEnabled = true;
});
}
}
You can design the button as follows, create a Column under the return MaterialApp, and call all the buttons which we will create here one after another. I am sharing the button design and general design on the below. Each button that will be created from now on will be located under the trafficButton that we will add now.
Code:
@override
Widget build(BuildContext context) {
final huaweiMap = HuaweiMap(
onMapCreated: onMapCreated,
mapType: MapType.normal,
tiltGesturesEnabled: true,
buildingsEnabled: true,
compassEnabled: true,
zoomControlsEnabled: true,
rotateGesturesEnabled: true,
myLocationButtonEnabled: true,
myLocationEnabled: true,
trafficEnabled: _trafficEnabled,
markers: _markers,
polylines: _polylines,
polygons: _polygons,
circles: _circles,
onClick: (LatLng latLng) {
log("Map Clicked at $latLng");
},
onLongPress: (LatLng latlng) {
log("Map LongClicked at $latlng");
},
initialCameraPosition: CameraPosition(
target: _centerPoint,
zoom: _zoom,
),
);
final trafficButton = Padding(
padding: EdgeInsets.all(8.0),
child: FloatingActionButton(
onPressed: () => trafficButtonOnClick(),
materialTapTargetSize: MaterialTapTargetSize.padded,
backgroundColor: Color(0xFFF9C335),
tooltip: "Traffic",
child: const Icon(Icons.traffic, size: 36.0, color: Colors.black),
),
);
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Map Kit', style: TextStyle(
color: Colors.black
)),
backgroundColor: Color(0xFFF9C335),
),
body: Stack(
children: <Widget>[
huaweiMap,
Padding(
padding: const EdgeInsets.all(16.0),
child: Align(
alignment: Alignment.topLeft,
child: Column(
children: <Widget>[
trafficButton
//other buttons here
],
),
),
),
],
),
),
);
}
After the traffic button is added, the screen of the map will be as follows.
5. Create 3D Map
My another favorite feature is that. But Map Kit doesn’t support 3D maps for areas in Turkey. As I said, since this feature is not supported in Turkey, I entered the latitude and longitude information of Collesium and enabled the camera to move to this point and show it to me in 3D.
Likewise, as the button is clicked, we must ensure that this feature is active and deactivated respectively. When it is active, we will see the Collesium, and when we deactivate it, we must return to the center position we first defined. For this, we create a method named moveCameraButtonOnClick as follows.
Code:
void moveCameraButtonOnClick() {
if (!_cameraPosChanged) {
_huaweiMapController.animateCamera(
CameraUpdate.newCameraPosition(
const CameraPosition(
bearing: 270.0,
target: LatLng(41.889228, 12.491780),
tilt: 45.0,
zoom: 17.0,
),
),
);
_cameraPosChanged = !_cameraPosChanged;
} else {
_huaweiMapController.animateCamera(
CameraUpdate.newCameraPosition(
const CameraPosition(
bearing: 0.0,
target: _centerPoint,
tilt: 0.0,
zoom: 12.0,
),
),
);
_cameraPosChanged = !_cameraPosChanged;
}
}
While designing the button, we must located on the left side and one under the other. By making the button design as follows, we add it under the trafficButton with the name moveCamreButton, as I mentioned in fourth section. After adding the relevant code, the screenshot will be as follows.
Code:
final moveCamreButton = Padding(
padding: EdgeInsets.all(8.0),
child: FloatingActionButton(
onPressed: () => moveCameraButtonOnClick(),
materialTapTargetSize: MaterialTapTargetSize.padded,
backgroundColor: Color(0xFFF9C335),
tooltip: "CameraMove",
child:
const Icon(Icons.airplanemode_active, size: 36.0, color: Colors.black),
),
);
6. Add Markers to Your Map
Markers are indispensable for map services. Thanks to this feature, you can add markers in different colors and designs on the map according to your needs. With these markers, you can named a special address and highlight it on the map.
You need some data to add a marker. These are the markerId, position, title, snippet, icon, draggable, rotation values that you will specify when creating the marker.
The code on the below contains the values and sample code required to add a normal marker. With this code, you can add a classic marker as you see it on every map.
The second marker is draggable. You can move the marker anywhere you want by holding down on it. For this, you must set the draggable value to true.
The third marker is located on the map at an angle. If you want the marker to be located at any angle such as 45' or 60' rather than perpendicular, it will be sufficient to give the angle you want to the rotation value.
The fourth and last marker will look different and colorful, unlike the others.
You can create markers in any style you want using these four features. The codes required to create markers are as follows.
Code:
void markersButtonOnClick() {
if (_markers.length > 0) {
setState(() {
_markers.clear();
});
} else {
setState(() {
_markers.add(Marker(
markerId: MarkerId('normal_marker'),
position: LatLng(40.997802, 28.994978),
infoWindow: InfoWindow(
title: 'Normal Marker Title',
snippet: 'Description Here!',
onClick: () {
log("Normal Marker InfoWindow Clicked");
}),
onClick: () {
log('Normal Marker Clicked!');
},
icon: BitmapDescriptor.defaultMarker,
));
_markers.add(Marker(
markerId: MarkerId('draggable_marker'),
position: LatLng(41.027335, 29.002359),
draggable: true,
flat: true,
rotation: 0.0,
infoWindow: InfoWindow(
title: 'Draggable Marker Title',
snippet: 'Hi! Description Here!',
),
clickable: true,
onClick: () {
log('Draggable Marker Clicked!');
},
onDragEnd: (pos) {
log("Draggable onDragEnd position : ${pos.lat}:${pos.lng}");
},
icon: BitmapDescriptor.defaultMarker,
));
_markers.add(Marker(
markerId: MarkerId('angular_marker'),
rotation: 45,
position: LatLng(41.043974, 29.028881),
infoWindow: InfoWindow(
title: 'Angular Marker Title',
snippet: 'Hey! Why can not I stand up straight?',
onClick: () {
log("Angular marker infoWindow clicked");
}),
icon: BitmapDescriptor.defaultMarker,
));
});
_markers.add(Marker(
markerId: MarkerId('colorful_marker'),
position: LatLng(41.076009, 29.054630),
infoWindow: InfoWindow(
title: 'Colorful Marker Title',
snippet: 'Yeap, as you know, description here!',
onClick: () {
log("Colorful marker infoWindow clicked");
}),
onClick: () {
log('Colorful Marker Clicked');
},
icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueMagenta),
));
}
}
Again, you can create a new button to be located on the left side of the map and add it to the relevant place in the code. Don’t forget to call the above markersButtonOnClick method on the onPressed of the button you created. You can find the necessary codes and screenshot for button design below.
Code:
final markerButton = Padding(
padding: EdgeInsets.all(8.0),
child: FloatingActionButton(
onPressed: markersButtonOnClick,
materialTapTargetSize: MaterialTapTargetSize.padded,
backgroundColor: Color(0xFFF9C335),
child: const Icon(Icons.add_location, size: 36.0, color: Colors.black),
),
);
7. Add Circle to Your Map
To add a circle, create a method called circlesButtonOnClick and define circleId, center, radius, fillColor, strokeColor, strokeWidth, zIndex, clickable values for the circle that will be created within this method.
All of these values depending on which point on the map, what size and color you will add a circle.
As an example, I share the screenshot below with the circlesButtonOnClick method, which adds two circles when the button is pressed, and the circlesButton design that I call this method.
Code:
void circlesButtonOnClick() {
if (_circles.length > 0) {
setState(() {
_circles.clear();
});
} else {
LatLng point1 = LatLng(40.986595, 29.025362);
LatLng point2 = LatLng(41.023644, 29.014032);
setState(() {
_circles.add(Circle(
circleId: CircleId('firstCircle'),
center: point1,
radius: 1000,
fillColor: Color.fromARGB(100, 249, 195, 53),
strokeColor: Color(0xFFF9C335),
strokeWidth: 3,
zIndex: 2,
clickable: true,
onClick: () {
log("First Circle clicked");
}));
_circles.add(Circle(
circleId: CircleId('secondCircle'),
center: point2,
zIndex: 1,
clickable: true,
onClick: () {
log("Second Circle Clicked");
},
radius: 2000,
fillColor: Color.fromARGB(50, 230, 20, 50),
strokeColor: Color.fromARGB(50, 230, 20, 50),
));
});
}
}
Code:
final circlesButton = Padding(
padding: EdgeInsets.all(8.0),
child: FloatingActionButton(
onPressed: circlesButtonOnClick,
materialTapTargetSize: MaterialTapTargetSize.padded,
backgroundColor: Color(0xFFF9C335),
child: const Icon(Icons.adjust, size: 36.0, color: Colors.black),
),
);
8. Add Polylines to Your Map
The purpose of using polyline is to draw a straight line between 2 coordinates.
The parameters we need to draw a polyline are polylineId, points, color, zIndex, endCap, startCap, clickable values. Here you can set the start and end points with enCap and startCap values. For location values, you need to define two LatLng values as an array.
To create a polyline, create a method called polylinesButtonOnClick and set the above values according to your needs. For button design, create a method called polylinesButton and call the polylinesButtonOnClick method in onPress. The screenshot after adding all the codes and polyline is as follows.
Code:
void polylinesButtonOnClick() {
if (_polylines.length > 0) {
setState(() {
_polylines.clear();
});
} else {
List<LatLng> line1 = [
LatLng(41.068698, 29.030855),
LatLng(41.045916, 29.059351),
];
List<LatLng> line2 = [
LatLng(40.999551, 29.062441),
LatLng(41.025975, 29.069651),
];
setState(() {
_polylines.add(Polyline(
polylineId: PolylineId('firstLine'),
points: line1,
color: Colors.pink,
zIndex: 2,
endCap: Cap.roundCap,
startCap: Cap.squareCap,
clickable: true,
onClick: () {
log("First Line Clicked");
}));
_polylines.add(Polyline(
polylineId: PolylineId('secondLine'),
points: line2,
width: 2,
patterns: [PatternItem.dash(20)],
jointType: JointType.bevel,
endCap: Cap.roundCap,
startCap: Cap.roundCap,
color: Color(0x900072FF),
zIndex: 1,
clickable: true,
onClick: () {
log("Second Line Clicked");
}));
});
}
}
Code:
final polylinesButton = Padding(
padding: EdgeInsets.all(8.0),
child: FloatingActionButton(
onPressed: polylinesButtonOnClick,
materialTapTargetSize: MaterialTapTargetSize.padded,
backgroundColor: Color(0xFFF9C335),
child: const Icon(Icons.waterfall_chart, size: 36.0, color: Colors.black),
),
);
9. Add Polygon to Your Map
Polygon is exactly the same as polyline. The only difference is that when adding polygons, you can draw the shapes you want, such as triangles and pentagons, by specifying more than two points.
The parameters we need to draw a polygon are polygonId, points, fillColor, strokeColor, strokeWidth, zIndex, clickable values. For Points value, you need to define more than two LatLng values as an array.
To add polygons, create a method called polygonsButtonOnClick and set the above values according to your needs. For button design, create a method named polygonsButton and call the polygonsButtonOnClick method in onPress. After adding all the codes and polygon, the screenshot is as follows.
Code:
void polygonsButtonOnClick() {
if (_polygons.length > 0) {
setState(() {
_polygons.clear();
});
} else {
List<LatLng> points1 = [
LatLng(40.989306, 29.021242),
LatLng(40.980753, 29.024590),
LatLng(40.982632, 29.031885),
LatLng(40.991273, 29.024676)
];
List<LatLng> points2 = [
LatLng(41.090321, 29.025598),
LatLng(41.085146, 29.018045),
LatLng(41.077124, 29.016844),
LatLng(41.075441, 29.026285),
LatLng(41.079582, 29.036928),
LatLng(41.086828, 29.031435)
];
setState(() {
_polygons.add(Polygon(
polygonId: PolygonId('polygon1'),
points: points1,
fillColor: Color.fromARGB(100, 129, 95, 53),
strokeColor: Colors.brown[900],
strokeWidth: 1,
zIndex: 2,
clickable: true,
onClick: () {
log("Polygon 1 Clicked");
}));
_polygons.add(Polygon(
polygonId: PolygonId('polygon2'),
points: points2,
fillColor: Color.fromARGB(190, 242, 195, 99),
strokeColor: Colors.yellow[900],
strokeWidth: 1,
zIndex: 1,
clickable: true,
onClick: () {
log("Polygon 2 Clicked");
}));
});
}
}
Code:
final polygonsButton = Padding(
padding: EdgeInsets.all(8.0),
child: FloatingActionButton(
onPressed: polygonsButtonOnClick,
materialTapTargetSize: MaterialTapTargetSize.padded,
backgroundColor: Color(0xFFF9C335),
tooltip: "Polygons",
child: const Icon(Icons.crop_square, size: 36.0, color: Colors.black),
),
);
10. Clear Your Map
You can use all of the features on your map at the same time. You can combine the features you want according to the needs of your application and to increase the user experience to higher levels. After adding all these features at the same time, the final view of your map will be as follows.
To delete all the elements you added on the map with a single button, you can create a method called clearMap and clear the map in this method.
Code:
void clearMap() {
setState(() {
_markers.clear();
_polylines.clear();
_polygons.clear();
_circles.clear();
});
}
Code:
final clearButton = Padding(
padding: EdgeInsets.all(8.0),
child: FloatingActionButton(
onPressed: () => clearMap(),
materialTapTargetSize: MaterialTapTargetSize.padded,
backgroundColor: Color(0xFFF9C335),
tooltip: "Clear",
child: const Icon(Icons.refresh, size: 36.0, color: Colors.black),
),
);
After all the methods are added, your code structure should be as follows.
Code:
class MapPage extends StatefulWidget {
@override
_MapPageState createState() => _MapPageState();
}
class _MapPageState extends State<MapPage> {
HuaweiMapController _huaweiMapController;
static const LatLng _centerPoint = const LatLng(41.043982, 29.014333);
static const double _zoom = 12;
final Set<Marker> _markers = {};
final Set<Polyline> _polylines = {};
final Set<Polygon> _polygons = {};
final Set<Circle> _circles = {};
bool _cameraPosChanged = false;
bool _trafficEnabled = false;
@override
void initState() {
super.initState();
}
void onMapCreated(HuaweiMapController controller) {
_huaweiMapController = controller;
}
void clearMap() {
setState(() {
_markers.clear();
_polylines.clear();
_polygons.clear();
_circles.clear();
});
}
void log(msg) {
print(msg);
}
void markersButtonOnClick() {
if (_markers.length > 0) {
setState(() {
_markers.clear();
});
} else {
setState(() {
_markers.add(Marker(
markerId: MarkerId('normal_marker'),
position: LatLng(40.997802, 28.994978),
infoWindow: InfoWindow(
title: 'Normal Marker Title',
snippet: 'Description Here!',
onClick: () {
log("Normal Marker InfoWindow Clicked");
}),
onClick: () {
log('Normal Marker Clicked!');
},
icon: BitmapDescriptor.defaultMarker,
));
_markers.add(Marker(
markerId: MarkerId('draggable_marker'),
position: LatLng(41.027335, 29.002359),
draggable: true,
flat: true,
rotation: 0.0,
infoWindow: InfoWindow(
title: 'Draggable Marker Title',
snippet: 'Hi! Description Here!',
),
clickable: true,
onClick: () {
log('Draggable Marker Clicked!');
},
onDragEnd: (pos) {
log("Draggable onDragEnd position : ${pos.lat}:${pos.lng}");
},
icon: BitmapDescriptor.defaultMarker,
));
_markers.add(Marker(
markerId: MarkerId('angular_marker'),
rotation: 45,
position: LatLng(41.043974, 29.028881),
infoWindow: InfoWindow(
title: 'Angular Marker Title',
snippet: 'Hey! Why can not I stand up straight?',
onClick: () {
log("Angular marker infoWindow clicked");
}),
icon: BitmapDescriptor.defaultMarker,
));
});
_markers.add(Marker(
markerId: MarkerId('colorful_marker'),
position: LatLng(41.076009, 29.054630),
infoWindow: InfoWindow(
title: 'Colorful Marker Title',
snippet: 'Yeap, as you know, description here!',
onClick: () {
log("Colorful marker infoWindow clicked");
}),
onClick: () {
log('Colorful Marker Clicked');
},
icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueMagenta),
));
}
}
void polygonsButtonOnClick() {
if (_polygons.length > 0) {
setState(() {
_polygons.clear();
});
} else {
List<LatLng> points1 = [
LatLng(40.989306, 29.021242),
LatLng(40.980753, 29.024590),
LatLng(40.982632, 29.031885),
LatLng(40.991273, 29.024676)
];
List<LatLng> points2 = [
LatLng(41.090321, 29.025598),
LatLng(41.085146, 29.018045),
LatLng(41.077124, 29.016844),
LatLng(41.075441, 29.026285),
LatLng(41.079582, 29.036928),
LatLng(41.086828, 29.031435)
];
setState(() {
_polygons.add(Polygon(
polygonId: PolygonId('polygon1'),
points: points1,
fillColor: Color.fromARGB(100, 129, 95, 53),
strokeColor: Colors.brown[900],
strokeWidth: 1,
zIndex: 2,
clickable: true,
onClick: () {
log("Polygon 1 Clicked");
}));
_polygons.add(Polygon(
polygonId: PolygonId('polygon2'),
points: points2,
fillColor: Color.fromARGB(190, 242, 195, 99),
strokeColor: Colors.yellow[900],
strokeWidth: 1,
zIndex: 1,
clickable: true,
onClick: () {
log("Polygon 2 Clicked");
}));
});
}
}
void polylinesButtonOnClick() {
if (_polylines.length > 0) {
setState(() {
_polylines.clear();
});
} else {
List<LatLng> line1 = [
LatLng(41.068698, 29.030855),
LatLng(41.045916, 29.059351),
];
List<LatLng> line2 = [
LatLng(40.999551, 29.062441),
LatLng(41.025975, 29.069651),
];
setState(() {
_polylines.add(Polyline(
polylineId: PolylineId('firstLine'),
points: line1,
color: Colors.pink,
zIndex: 2,
endCap: Cap.roundCap,
startCap: Cap.squareCap,
clickable: true,
onClick: () {
log("First Line Clicked");
}));
_polylines.add(Polyline(
polylineId: PolylineId('secondLine'),
points: line2,
width: 2,
patterns: [PatternItem.dash(20)],
jointType: JointType.bevel,
endCap: Cap.roundCap,
startCap: Cap.roundCap,
color: Color(0x900072FF),
zIndex: 1,
clickable: true,
onClick: () {
log("Second Line Clicked");
}));
});
}
}
void circlesButtonOnClick() {
if (_circles.length > 0) {
setState(() {
_circles.clear();
});
} else {
LatLng point1 = LatLng(40.986595, 29.025362);
LatLng point2 = LatLng(41.023644, 29.014032);
setState(() {
_circles.add(Circle(
circleId: CircleId('firstCircle'),
center: point1,
radius: 1000,
fillColor: Color.fromARGB(100, 249, 195, 53),
strokeColor: Color(0xFFF9C335),
strokeWidth: 3,
zIndex: 2,
clickable: true,
onClick: () {
log("First Circle clicked");
}));
_circles.add(Circle(
circleId: CircleId('secondCircle'),
center: point2,
zIndex: 1,
clickable: true,
onClick: () {
log("Second Circle Clicked");
},
radius: 2000,
fillColor: Color.fromARGB(50, 230, 20, 50),
strokeColor: Color.fromARGB(50, 230, 20, 50),
));
});
}
}
void moveCameraButtonOnClick() {
if (!_cameraPosChanged) {
_huaweiMapController.animateCamera(
CameraUpdate.newCameraPosition(
const CameraPosition(
bearing: 270.0,
target: LatLng(41.889228, 12.491780),
tilt: 45.0,
zoom: 17.0,
),
),
);
_cameraPosChanged = !_cameraPosChanged;
} else {
_huaweiMapController.animateCamera(
CameraUpdate.newCameraPosition(
const CameraPosition(
bearing: 0.0,
target: _centerPoint,
tilt: 0.0,
zoom: 12.0,
),
),
);
_cameraPosChanged = !_cameraPosChanged;
}
}
void trafficButtonOnClick() {
if (_trafficEnabled) {
setState(() {
_trafficEnabled = false;
});
} else {
setState(() {
_trafficEnabled = true;
});
}
}
@override
Widget build(BuildContext context) {
final huaweiMap = HuaweiMap(
onMapCreated: onMapCreated,
mapType: MapType.normal,
tiltGesturesEnabled: true,
buildingsEnabled: true,
compassEnabled: true,
zoomControlsEnabled: true,
rotateGesturesEnabled: true,
myLocationButtonEnabled: true,
myLocationEnabled: true,
trafficEnabled: _trafficEnabled,
markers: _markers,
polylines: _polylines,
polygons: _polygons,
circles: _circles,
onClick: (LatLng latLng) {
log("Map Clicked at $latLng");
},
onLongPress: (LatLng latlng) {
log("Map LongClicked at $latlng");
},
initialCameraPosition: CameraPosition(
target: _centerPoint,
zoom: _zoom,
),
);
final markerButton = Padding(
padding: EdgeInsets.all(8.0),
child: FloatingActionButton(
onPressed: markersButtonOnClick,
materialTapTargetSize: MaterialTapTargetSize.padded,
backgroundColor: Color(0xFFF9C335),
child: const Icon(Icons.add_location, size: 36.0, color: Colors.black),
),
);
final circlesButton = Padding(
padding: EdgeInsets.all(8.0),
child: FloatingActionButton(
onPressed: circlesButtonOnClick,
materialTapTargetSize: MaterialTapTargetSize.padded,
backgroundColor: Color(0xFFF9C335),
child: const Icon(Icons.adjust, size: 36.0, color: Colors.black),
),
);
final polylinesButton = Padding(
padding: EdgeInsets.all(8.0),
child: FloatingActionButton(
onPressed: polylinesButtonOnClick,
materialTapTargetSize: MaterialTapTargetSize.padded,
backgroundColor: Color(0xFFF9C335),
child: const Icon(Icons.waterfall_chart, size: 36.0, color: Colors.black),
),
);
final polygonsButton = Padding(
padding: EdgeInsets.all(8.0),
child: FloatingActionButton(
onPressed: polygonsButtonOnClick,
materialTapTargetSize: MaterialTapTargetSize.padded,
backgroundColor: Color(0xFFF9C335),
tooltip: "Polygons",
child: const Icon(Icons.crop_square, size: 36.0, color: Colors.black),
),
);
final clearButton = Padding(
padding: EdgeInsets.all(8.0),
child: FloatingActionButton(
onPressed: () => clearMap(),
materialTapTargetSize: MaterialTapTargetSize.padded,
backgroundColor: Color(0xFFF9C335),
tooltip: "Clear",
child: const Icon(Icons.refresh, size: 36.0, color: Colors.black),
),
);
final moveCamreButton = Padding(
padding: EdgeInsets.all(8.0),
child: FloatingActionButton(
onPressed: () => moveCameraButtonOnClick(),
materialTapTargetSize: MaterialTapTargetSize.padded,
backgroundColor: Color(0xFFF9C335),
tooltip: "CameraMove",
child:
const Icon(Icons.airplanemode_active, size: 36.0, color: Colors.black),
),
);
final trafficButton = Padding(
padding: EdgeInsets.all(8.0),
child: FloatingActionButton(
onPressed: () => trafficButtonOnClick(),
materialTapTargetSize: MaterialTapTargetSize.padded,
backgroundColor: Color(0xFFF9C335),
tooltip: "Traffic",
child: const Icon(Icons.traffic, size: 36.0, color: Colors.black),
),
);
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Map Kit', style: TextStyle(
color: Colors.black
)),
backgroundColor: Color(0xFFF9C335),
),
body: Stack(
children: <Widget>[
huaweiMap,
Padding(
padding: const EdgeInsets.all(16.0),
child: Align(
alignment: Alignment.topLeft,
child: Column(
children: <Widget>[
clearButton,
trafficButton,
moveCamreButton,
markerButton,
circlesButton,
polylinesButton,
polygonsButton
],
),
),
),
],
),
),
);
}
}

Huawei AppGallery Auth service using Mobile Number in flutter

Introduction
AppGallery Connect provides a cloud-based auth service and SDKs to help you quickly build a secure and reliable user authentication system for your apps to verify user identity. The AppGallery Connect auth service supports multiple authentication methods and is seamlessly integrated with other Serverless services to help you secure user data based on simple rules that you have defined.
In this article, we will cover just the mobile number authentication method in Flutter.
Auth Service supported accounts
Phone
Huawei ID
Huawei Game Service
WeChat
Weibo
QQ
Email
Integration of Crash service
1. Configure application on the AGC
2. Client application development process
Configure application on the AGC
This step involves a couple of steps, as follows.
Step 1: We need to register as a developer account in AppGallery Connect. If you are already a developer ignore this step.
Step 2: Create an app by referring to Creating a Project and Creating an App in the Project
Step 3: Set the data storage location based on the current location.
Step 4: Enabling Crash Kit. Open AppGallery connect, choose project settings > Build> Auth Service
Step 5: Generating a Signing Certificate Fingerprint.
Step 6: Configuring the Signing Certificate Fingerprint.
Step 7: Download your agconnect-services.json file, paste it into the app root directory.
Client application development process
This step involves the couple of steps as follows.
Step 1: Create flutter application in the Android studio (Any IDE which is your favorite).
Step 2: Add the App level gradle dependencies. Choose inside project Android > app > build.gradle
1
2apply plugin: 'com.android.application'
apply plugin: 'com.huawei.agconnect'
Root level gradle dependencies
1
2maven { url 'https://developer.huawei.com/repo/' }
classpath 'com.huawei.agconnect:agcp:1.4.1.300'
Add the below permissions in Android Manifest file.
1<uses-permission android:name="android.permission.INTERNET" />
Step 3: Add the agconnect_auth in pubspec.yaml
Step 4: Add downloaded file into outside project directory. Declare plugin path in pubspec.yaml file under dependencies.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25name: tic_tac_toe
description: A new Flutter project.
version: 1.0.0+1
environment:
sdk: ">=2.1.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^0.1.2
agconnect_auth: ^1.1.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
uses-material-design: true
assets:
- images/cross.png
- images/circle.png
- images/edit.png
- images/delete.png
- images/success.png
- images/otp-icon.png
To achieve Auth service example let’s follow the steps
1. AGC Configuration
2. Build Flutter application
Step 1: AGC Configuration
1. Sign in to AppGallery Connect and select My apps.
2. Select the app in which you want to integrate Crash Service.
3. Navigate to Project Setting > Build> Auth Service
{
"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: Build Flutter application
homepage.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131import 'package:agconnect_auth/agconnect_auth.dart';
import 'package:flutter/material.dart';
import 'package:tic_tac_toe/Constant/Constant.dart';
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
List<Widget> widgetList = [];
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.red,
title: Text(
"Login",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
backgroundColor: Color(0xFFeaeaea),
body: ListView(
shrinkWrap: true,
scrollDirection: Axis.vertical,
children: <Widget>[
Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(
left: 16.0, top: 20.0, right: 16.0),
child: Text(
"Enter your phone number",
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
color: Colors.black),
textAlign: TextAlign.center,
),
),
Padding(
padding: const EdgeInsets.only(top: 30.0),
child: Image(
image: AssetImage('images/otp-icon.png'),
height: 120.0,
width: 120.0,
),
),
Row(
children: <Widget>[
Flexible(
child: new Container(),
flex: 1,
),
Flexible(
child: new TextFormField(
textAlign: TextAlign.center,
autofocus: false,
enabled: false,
initialValue: "+91",
style: TextStyle(fontSize: 20.0, color: Colors.black),
),
flex: 3,
),
Flexible(
child: new Container(),
flex: 1,
),
Flexible(
child: new TextFormField(
textAlign: TextAlign.start,
autofocus: false,
enabled: true,
keyboardType: TextInputType.number,
textInputAction: TextInputAction.done,
style: TextStyle(fontSize: 20.0, color: Colors.black),
),
flex: 9,
),
Flexible(
child: new Container(),
flex: 1,
),
],
),
Padding(
padding: const EdgeInsets.only(top: 40.0, bottom: 40.0),
child: new Container(
width: 150.0,
height: 40.0,
child: new RaisedButton(
onPressed: () {
//Navigator.of(context).pushNamed(OTP_SCREEN);
checkIsUserSignedIn(context);
},
child: Text("Get OTP"),
textColor: Colors.white,
color: Colors.red,
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(30.0))),
),
)
])
],
));
}
void checkIsUserSignedIn(BuildContext context) {
AGCAuth.instance.currentUser.then((user) {
if (user != null) {
print("User is already signed in");
} else {
requestCode(context, '91', '9731276143');
}
});
}
void requestCode(
BuildContext context, String countryCode, String phoneNumber) {
VerifyCodeSettings settings =
VerifyCodeSettings(VerifyCodeAction.registerLogin, sendInterval: 30);
PhoneAuthProvider.requestVerifyCode(countryCode, phoneNumber, settings)
.then((result) {
print("Requested verification code");
Navigator.of(context).pushNamed(OTP_SCREEN);
});
}
}
otppage.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533import 'package:agconnect_auth/agconnect_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class OtpPage extends StatefulWidget {
@override
OtpPageState createState() => OtpPageState();
}
class OtpPageState extends State<OtpPage> {
TextEditingController controller1 = new TextEditingController();
TextEditingController controller2 = new TextEditingController();
TextEditingController controller3 = new TextEditingController();
TextEditingController controller4 = new TextEditingController();
TextEditingController controller5 = new TextEditingController();
TextEditingController controller6 = new TextEditingController();
TextEditingController currController = new TextEditingController();
@override
void dispose() {
super.dispose();
controller1.dispose();
controller2.dispose();
controller3.dispose();
controller4.dispose();
controller5.dispose();
controller6.dispose();
}
@override
void initState() {
// TODO: implement initState
super.initState();
currController = controller1;
}
@override
Widget build(BuildContext context) {
List<Widget> widgetList = [
Padding(
padding: EdgeInsets.only(left: 0.0, right: 2.0),
child: new Container(
color: Colors.transparent,
),
),
Padding(
padding: const EdgeInsets.only(right: 2.0, left: 2.0),
child: new Container(
alignment: Alignment.center,
decoration: new BoxDecoration(
color: Color.fromRGBO(0, 0, 0, 0.1),
border: new Border.all(
width: 1.0, color: Color.fromRGBO(0, 0, 0, 0.1)),
borderRadius: new BorderRadius.circular(4.0)),
child: new TextField(
inputFormatters: [
LengthLimitingTextInputFormatter(1),
],
enabled: false,
controller: controller1,
autofocus: false,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 24.0, color: Colors.black),
)),
),
Padding(
padding: const EdgeInsets.only(right: 2.0, left: 2.0),
child: new Container(
alignment: Alignment.center,
decoration: new BoxDecoration(
color: Color.fromRGBO(0, 0, 0, 0.1),
border: new Border.all(
width: 1.0, color: Color.fromRGBO(0, 0, 0, 0.1)),
borderRadius: new BorderRadius.circular(4.0)),
child: new TextField(
inputFormatters: [
LengthLimitingTextInputFormatter(1),
],
controller: controller2,
autofocus: false,
enabled: false,
keyboardType: TextInputType.number,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 24.0, color: Colors.black),
),
),
),
Padding(
padding: const EdgeInsets.only(right: 2.0, left: 2.0),
child: new Container(
alignment: Alignment.center,
decoration: new BoxDecoration(
color: Color.fromRGBO(0, 0, 0, 0.1),
border: new Border.all(
width: 1.0, color: Color.fromRGBO(0, 0, 0, 0.1)),
borderRadius: new BorderRadius.circular(4.0)),
child: new TextField(
inputFormatters: [
LengthLimitingTextInputFormatter(1),
],
keyboardType: TextInputType.number,
controller: controller3,
textAlign: TextAlign.center,
autofocus: false,
enabled: false,
style: TextStyle(fontSize: 24.0, color: Colors.black),
),
),
),
Padding(
padding: const EdgeInsets.only(right: 2.0, left: 2.0),
child: new Container(
alignment: Alignment.center,
decoration: new BoxDecoration(
color: Color.fromRGBO(0, 0, 0, 0.1),
border: new Border.all(
width: 1.0, color: Color.fromRGBO(0, 0, 0, 0.1)),
borderRadius: new BorderRadius.circular(4.0)),
child: new TextField(
inputFormatters: [
LengthLimitingTextInputFormatter(1),
],
textAlign: TextAlign.center,
controller: controller4,
autofocus: false,
enabled: false,
style: TextStyle(fontSize: 24.0, color: Colors.black),
),
),
),
Padding(
padding: const EdgeInsets.only(right: 2.0, left: 2.0),
child: new Container(
alignment: Alignment.center,
decoration: new BoxDecoration(
color: Color.fromRGBO(0, 0, 0, 0.1),
border: new Border.all(
width: 1.0, color: Color.fromRGBO(0, 0, 0, 0.1)),
borderRadius: new BorderRadius.circular(4.0)),
child: new TextField(
inputFormatters: [
LengthLimitingTextInputFormatter(1),
],
textAlign: TextAlign.center,
controller: controller5,
autofocus: false,
enabled: false,
style: TextStyle(fontSize: 24.0, color: Colors.black),
),
),
),
Padding(
padding: const EdgeInsets.only(right: 2.0, left: 2.0),
child: new Container(
alignment: Alignment.center,
decoration: new BoxDecoration(
color: Color.fromRGBO(0, 0, 0, 0.1),
border: new Border.all(
width: 1.0, color: Color.fromRGBO(0, 0, 0, 0.1)),
borderRadius: new BorderRadius.circular(4.0)),
child: new TextField(
inputFormatters: [
LengthLimitingTextInputFormatter(1),
],
textAlign: TextAlign.center,
controller: controller6,
autofocus: false,
enabled: false,
style: TextStyle(fontSize: 24.0, color: Colors.black),
),
),
),
Padding(
padding: EdgeInsets.only(left: 2.0, right: 0.0),
child: new Container(
color: Colors.transparent,
),
),
];
return new Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: Text("Enter OTP"),
backgroundColor: Colors.red,
),
backgroundColor: Color(0xFFeaeaea),
body: Container(
child: Column(
children: <Widget>[
Flexible(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
"Verifying your number!",
style: TextStyle(
fontSize: 18.0, fontWeight: FontWeight.bold),
),
),
Padding(
padding: const EdgeInsets.only(
left: 16.0, top: 4.0, right: 16.0),
child: Text(
"Please type the verification code sent to",
style: TextStyle(
fontSize: 15.0, fontWeight: FontWeight.normal),
textAlign: TextAlign.center,
),
),
Padding(
padding: const EdgeInsets.only(
left: 30.0, top: 2.0, right: 30.0),
child: Text(
"+91 9731276143",
style: TextStyle(
fontSize: 15.0,
fontWeight: FontWeight.bold,
color: Colors.red),
textAlign: TextAlign.center,
),
),
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: Image(
image: AssetImage('images/otp-icon.png'),
height: 120.0,
width: 120.0,
),
)
],
),
flex: 90,
),
Flexible(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
GridView.count(
crossAxisCount: 8,
mainAxisSpacing: 10.0,
shrinkWrap: true,
primary: false,
scrollDirection: Axis.vertical,
children: List<Container>.generate(
8,
(int index) =>
Container(child: widgetList[index]))),
]),
flex: 20,
),
Flexible(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Container(
child: Padding(
padding: const EdgeInsets.only(
left: 8.0, top: 16.0, right: 8.0, bottom: 0.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
MaterialButton(
onPressed: () {
inputTextToField("1");
},
child: Text("1",
style: TextStyle(
fontSize: 25.0,
fontWeight: FontWeight.w400),
textAlign: TextAlign.center),
),
MaterialButton(
onPressed: () {
inputTextToField("2");
},
child: Text("2",
style: TextStyle(
fontSize: 25.0,
fontWeight: FontWeight.w400),
textAlign: TextAlign.center),
),
MaterialButton(
onPressed: () {
inputTextToField("3");
},
child: Text("3",
style: TextStyle(
fontSize: 25.0,
fontWeight: FontWeight.w400),
textAlign: TextAlign.center),
),
],
),
),
),
new Container(
child: Padding(
padding: const EdgeInsets.only(
left: 8.0, top: 4.0, right: 8.0, bottom: 0.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
MaterialButton(
onPressed: () {
inputTextToField("4");
},
child: Text("4",
style: TextStyle(
fontSize: 25.0,
fontWeight: FontWeight.w400),
textAlign: TextAlign.center),
),
MaterialButton(
onPressed: () {
inputTextToField("5");
},
child: Text("5",
style: TextStyle(
fontSize: 25.0,
fontWeight: FontWeight.w400),
textAlign: TextAlign.center),
),
MaterialButton(
onPressed: () {
inputTextToField("6");
},
child: Text("6",
style: TextStyle(
fontSize: 25.0,
fontWeight: FontWeight.w400),
textAlign: TextAlign.center),
),
],
),
),
),
new Container(
child: Padding(
padding: const EdgeInsets.only(
left: 8.0, top: 4.0, right: 8.0, bottom: 0.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
MaterialButton(
onPressed: () {
inputTextToField("7");
},
child: Text("7",
style: TextStyle(
fontSize: 25.0,
fontWeight: FontWeight.w400),
textAlign: TextAlign.center),
),
MaterialButton(
onPressed: () {
inputTextToField("8");
},
child: Text("8",
style: TextStyle(
fontSize: 25.0,
fontWeight: FontWeight.w400),
textAlign: TextAlign.center),
),
MaterialButton(
onPressed: () {
inputTextToField("9");
},
child: Text("9",
style: TextStyle(
fontSize: 25.0,
fontWeight: FontWeight.w400),
textAlign: TextAlign.center),
),
],
),
),
),
new Container(
child: Padding(
padding: const EdgeInsets.only(
left: 8.0, top: 4.0, right: 8.0, bottom: 0.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
MaterialButton(
onPressed: () {
deleteText();
},
child: Image.asset('images/delete.png',
width: 25.0, height: 25.0)),
MaterialButton(
onPressed: () {
inputTextToField("0");
},
child: Text("0",
style: TextStyle(
fontSize: 25.0,
fontWeight: FontWeight.w400),
textAlign: TextAlign.center),
),
MaterialButton(
onPressed: () {
//matchOtp();
signIn('91', '9731276143', '385961', '!huawei143');
},
child: Image.asset('images/success.png',
width: 25.0, height: 25.0)),
],
),
),
),
],
),
flex: 90,
),
],
),
),
);
}
void inputTextToField(String str) {
//Edit first textField
if (currController == controller1) {
controller1.text = str;
currController = controller2;
}
//Edit second textField
else if (currController == controller2) {
controller2.text = str;
currController = controller3;
}
//Edit third textField
else if (currController == controller3) {
controller3.text = str;
currController = controller4;
}
//Edit fourth textField
else if (currController == controller4) {
controller4.text = str;
currController = controller5;
}
//Edit fifth textField
else if (currController == controller5) {
controller5.text = str;
currController = controller6;
}
//Edit sixth textField
else if (currController == controller6) {
controller6.text = str;
currController = controller6;
}
}
void deleteText() {
if (currController.text.length == 0) {
} else {
currController.text = "";
currController = controller5;
return;
}
if (currController == controller1) {
controller1.text = "";
} else if (currController == controller2) {
controller1.text = "";
currController = controller1;
} else if (currController == controller3) {
controller2.text = "";
currController = controller2;
} else if (currController == controller4) {
controller3.text = "";
currController = controller3;
} else if (currController == controller5) {
controller4.text = "";
currController = controller4;
} else if (currController == controller6) {
controller5.text = "";
currController = controller5;
}
}
void matchOtp() {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Successfully"),
content: Text("Otp matched successfully."),
actions: <Widget>[
IconButton(
icon: Icon(Icons.check),
onPressed: () {
Navigator.of(context).pop();
})
],
);
});
}
void signIn(String countryCode, String mobileNumber, String verificationCode,
String password) {
AGCAuthCredential credential = PhoneAuthProvider.credentialWithVerifyCode(
countryCode, mobileNumber, verificationCode,
password: password);
AGCAuth.instance.signIn(credential).then((signInResult) {
//get user info
AGCUser user = signInResult.user;
print("User Name: " + user.displayName);
print("User Email: " + user.email);
print("User Email: " + user.phone);
}).catchError((error) {
//fail
});
}
}
Result
Tips and Tricks
Always use the latest version of the library.
Add agconnect-services.json file without fail.
Add SHA-256 fingerprint without fail.
Make sure min SDK is 17 or higher
Make sure that you enabled the auth service in AG-Console.
Make sure that you enabled the Authentication mode in Auth Service.
Conclusion
In this article, we have learnt the integration of Huawei Auth Service-AGC mobile number sign in and mobile number verification through OTP in flutter application. Auth Service provides secure and reliable user authentication system to your application.
References
Flutter Auth Service

Implementing Huawei Analytics and Ads kit in Flutter StoryApp

{
"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
In this article, we will be integrating Huawei Account, Analytics and Ads kit in StoryApp. Flutter plugin provides simple and convenient way to experience authorization of users. Flutter Account Plugin allows users to connect to the Huawei ecosystem using their Huawei IDs from the different devices such as mobiles phones and tablets, added users can login quickly and conveniently sign in to apps with their Huawei IDs after granting initial access permission.
Huawei Ads kit provides access to range of development capabilities. You can promote your apps quickly and more efficiently to Huawei’s vast users. Ads kit helps your app to be monetize quickly and start generating revenue.
Huawei supports following Ads types
Banner
Interstitial
Native
Reward
Splash
Instream(Roll)
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.
Development Overview
You 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 process
Step 1: Create flutter project.
Step 2: Add the App level gradle dependencies. Choose inside project Android > app > build.gradle.
Code:
apply plugin: 'com.android.application'
apply plugin: 'com.huawei.agconnect'
Root level gradle dependencies
Code:
maven {url 'https://developer.huawei.com/repo/'}
classpath 'com.huawei.agconnect:agcp:1.4.1.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: Download flutter plugins
Step 5: Add downloaded file into parent directory of the project. Declare plugin path in pubspec.yaml file under dependencies.
Add path location for asset image.
Let's start coding
loginScreen.dart
Code:
class LoginScreen extends StatelessWidget {
const LoginScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: LoginDemo(),
);
}
}
class LoginDemo extends StatefulWidget {
@override
_LoginDemoState createState() => _LoginDemoState();
}
class _LoginDemoState extends State<LoginDemo> {
final HMSAnalytics _hmsAnalytics = new HMSAnalytics();
@override
void initState() {
// TODO: implement initState
HwAds.init();
_enableLog();
_predefinedEvent();
showBannerAd();
super.initState();
}
Future<void> _enableLog() async {
_hmsAnalytics.setUserId("TestUserStoryApp");
await _hmsAnalytics.enableLog();
}
void _predefinedEvent() async {
String name = HAEventType.SIGNIN;
dynamic value = {HAParamType.ENTRY: 06534797};
await _hmsAnalytics.onEvent(name, value);
print("Event posted");
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text("Login Page"),
backgroundColor: Colors.grey[850],
),
body: RefreshIndicator(
onRefresh: showToast,
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 60.0),
child: Center(
child: Container(
width: 200,
height: 150,
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(60.0)),
child: Image.asset('images/logo_huawei.png')),
),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 15),
child: TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Email',
hintText: 'Enter valid email id '),
),
),
Padding(
padding: const EdgeInsets.only(
left: 15.0, right: 15.0, top: 15, bottom: 0),
child: TextField(
obscureText: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Password',
hintText: 'Enter password'),
),
),
FlatButton(
onPressed: () {
//TODO FORGOT PASSWORD SCREEN GOES HERE
},
child: Text(
'Forgot Password',
style: TextStyle(color: Colors.blue, fontSize: 15),
),
),
Container(
height: 50,
width: 270,
decoration: BoxDecoration(
color: Colors.red, borderRadius: BorderRadius.circular(20)),
child: FlatButton(
onPressed: () async {
try {
try {
final bool result = await AccountAuthService.signOut();
if (result) {
final bool response =
await AccountAuthService.cancelAuthorization();
}
} on Exception catch (e) {
print(e.toString());
}
} on Exception catch (e) {
print(e.toString());
}
},
child: GestureDetector(
onTap: () async {
try {
final bool response =
await AccountAuthService.cancelAuthorization();
} on Exception catch (e) {
print(e.toString());
}
},
child: Text(
'Login',
style: TextStyle(color: Colors.white, fontSize: 25),
),
),
),
),
SizedBox(
height: 5,
),
Container(
height: 50,
width: 270,
decoration: BoxDecoration(
color: Colors.red, borderRadius: BorderRadius.circular(20)),
child: HuaweiIdAuthButton(
theme: AuthButtonTheme.FULL_TITLE,
buttonColor: AuthButtonBackground.RED,
borderRadius: AuthButtonRadius.MEDIUM,
onPressed: () {
signInWithHuaweiAccount();
}),
),
SizedBox(
height: 30,
),
GestureDetector(
onTap: () {
showBannerAd();
},
child: Text('New User? Create Account'),
),
],
),
),
),
);
}
void showBannerAd() {
BannerAd _bannerAd;
_bannerAd = createBannerAd();
_bannerAd
..loadAd()
..show(gravity: Gravity.bottom, offset: 5);
}
//Create BannerAd
static BannerAd createBannerAd() {
BannerAd banner = BannerAd(
adSlotId: "testw6vs28auh3",
size: BannerAdSize.sSmart,
adParam: AdParam());
banner.setAdListener = (AdEvent event, {int? errorCode}) {
print("Banner Ad event : $event");
};
return banner;
}
void signInWithHuaweiAccount() async {
AccountAuthParamsHelper helper = new AccountAuthParamsHelper();
helper.setAuthorizationCode();
try {
// The sign-in is successful, and the user's ID information and authorization code are obtained.
Future<AuthAccount> account = AccountAuthService.signIn(helper);
account.then((value) => Fluttertoast.showToast(
msg: "Welcome " + value.displayName.toString(),
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 1,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 16.0));
Navigator.push(context, MaterialPageRoute(builder: (_) => Main1()));
} on Exception catch (e) {
print(e.toString());
}
}
Future<void> showToast() async {
Fluttertoast.showToast(
msg: "Refreshing.. ",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 1,
backgroundColor: Colors.lightBlue,
textColor: Colors.white,
fontSize: 16.0);
}
BannerAd createAd() {
return BannerAd(
adSlotId: "testw6vs28auh3",
size: BannerAdSize.s468x60,
adParam: AdParam(),
);
}
}
Main.dart
Code:
class Main extends StatefulWidget {
@override
_Main1State createState() => _Main1State();
}
var cardAspectRation = 12.0 / 20.0;
var widgetAspectRatio = cardAspectRation * 1.2;
var verticalInset = 20.0;
class _Main1State extends State<Main1> {
var currentPage = images.length - 1.0;
bool isMenuClosed = true;
late double screenWidth;
final HMSAnalytics _hmsAnalytics = new HMSAnalytics();
@override
void dispose() {
// SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values);
super.dispose();
}
@override
initState() {
// SystemChrome.setEnabledSystemUIOverlays([]);
super.initState();
_enableLog();
}
Future<void> _enableLog() async {
_hmsAnalytics.setUserId("TestUserStoryApp");
await _hmsAnalytics.enableLog();
}
@override
Widget build(BuildContext context) {
// The initial todos
PageController controller = PageController(initialPage: images.length - 1);
controller.addListener(() {
setState(() {
currentPage = controller.page!;
});
});
Size size = MediaQuery.of(context).size;
screenWidth = size.width;
return Scaffold(
backgroundColor: Colors.grey[850],
body: Stack(
children: <Widget>[
menu(context),
dashboard(context),
],
),
);
}
Widget menu(context) {
return Padding(
padding: EdgeInsets.fromLTRB(0, 0, screenWidth * 0.15, 0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Flexible(
flex: 4,
child: Container(
color: Colors.grey[850],
child: Align(
alignment: Alignment.center,
child: circularImage(),
),
),
),
Flexible(
flex: 6,
child: Container(
child: Row(
children: <Widget>[
Flexible(
child: Column(
children: <Widget>[
Flexible(
child: GestureDetector(
onTap: () {
print("Clicked on Ads");
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => SecondScreen()));
},
child: Container(
decoration: new BoxDecoration(
color: Color.fromRGBO(218, 107, 107, 1),
borderRadius: new BorderRadius.only(
topLeft: const Radius.circular(80.0))),
child: Align(
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.headset,
color: Colors.white,
size: 50,
),
Text('Ads',
style: TextStyle(
color: Colors.white, fontSize: 22))
],
),
),
),
),
),
Flexible(
child: GestureDetector(
onTap: () {
print("Clicked on browse");
},
child: Container(
color: Color.fromRGBO(211, 96, 96, 1),
child: Align(
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.satellite,
color: Colors.white,
size: 50,
),
Text('Browse',
style: TextStyle(
color: Colors.white, fontSize: 22))
],
),
),
),
),
)
],
),
),
Flexible(
child: Column(
children: <Widget>[
Flexible(
child: GestureDetector(
onTap: () {
print("Clicked on Analytics");
sendCustomEvent();
},
child: Container(
decoration: new BoxDecoration(
color: Color.fromRGBO(211, 96, 96, 1),
borderRadius: new BorderRadius.only(
topRight: const Radius.circular(80.0))),
child: Align(
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.assignment,
color: Colors.white,
size: 50,
),
Text('Analytics',
style: TextStyle(
color: Colors.white, fontSize: 22))
],
),
),
),
),
),
Flexible(
child: GestureDetector(
onTap: () {
print("Clicked on settings");
},
child: Container(
color: Color.fromRGBO(218, 107, 107, 1),
child: Align(
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.settings,
color: Colors.white,
size: 50,
),
Text('Settings',
style: TextStyle(
color: Colors.white, fontSize: 22))
],
),
),
),
),
)
],
),
)
],
),
),
)
],
),
);
}
Widget dashboard(context) {
return AnimatedPositioned(
//top: isMenuClosed? 0 : 100,
//bottom: isMenuClosed? 0 : 100,
top: 0,
bottom: 0,
left: isMenuClosed ? 0 : screenWidth - screenWidth * 0.15,
right: isMenuClosed ? 0 : -90,
duration: Duration(milliseconds: 300),
child: Material(
elevation: 8,
color: Colors.grey[850],
child: Padding(
padding: EdgeInsets.fromLTRB(10, 30, 10, 10),
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
InkWell(
child: Icon(
Icons.segment,
color: Colors.white,
size: 40,
),
onTap: () {
setState(() {
isMenuClosed = !isMenuClosed;
});
},
),
Icon(
Icons.search_outlined,
color: Colors.white,
size: 40,
),
],
),
Text("Trending",
style: TextStyle(fontSize: 28, color: Colors.white)),
CardScrollWidget(currentPage),
],
),
),
),
);
}
Widget circularImage() {
return Container(
width: 200.0,
height: 200.0,
decoration: new BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: Colors.teal, width: 10.0, style: BorderStyle.solid),
image: new DecorationImage(
fit: BoxFit.cover, image: AssetImage("images/logo_huawei.png"))),
);
}
void sendCustomEvent() async {
String value = "This is custom event from app";
dynamic eventData = {'Message': value};
await _hmsAnalytics.onEvent("CustomEvent", eventData);
print("Event posted");
}
}
class CardScrollWidget extends StatelessWidget {
var currentPage;
var padding = 20.0;
CardScrollWidget(this.currentPage);
@override
Widget build(BuildContext context) {
return AspectRatio(
aspectRatio: widgetAspectRatio,
child: LayoutBuilder(
builder: (context, constraints) {
var width = constraints.maxWidth;
var height = constraints.maxHeight;
var safeWidth = width - 2 * padding;
var safeHeight = height - 2 * padding;
var heightOfPrimaryCard = safeHeight;
var widthOfPrimaryCard = heightOfPrimaryCard * cardAspectRation;
var primaryCardLeft = safeWidth - widthOfPrimaryCard;
var horizontalInset = primaryCardLeft / 2;
List<Widget> cardList = [];
for (var i = 0; i < images.length; i++) {
var delta = i - currentPage;
bool isOnRight = delta > 0;
var start = padding +
max(
primaryCardLeft -
horizontalInset * -delta * (isOnRight ? 15 : 1),
0.0);
var cardItem = Positioned.directional(
top: padding + verticalInset * max(-delta, 0.0),
bottom: padding + verticalInset * max(-delta, 0.0),
start: start,
textDirection: TextDirection.rtl,
child: ClipRRect(
borderRadius: BorderRadius.circular(16.0),
child: Container(
decoration: BoxDecoration(color: Colors.white, boxShadow: [
BoxShadow(
color: Colors.black12,
offset: Offset(3.0, 6.0),
blurRadius: 10.0)
]),
child: AspectRatio(
aspectRatio: cardAspectRation,
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Image.asset(images[i], fit: BoxFit.cover),
Align(
alignment: Alignment.bottomLeft,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
decoration: BoxDecoration(
color: Colors.black45,
borderRadius: BorderRadius.circular(20.0)),
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: 16.0, vertical: 8.0),
child: Text(title[i],
style: TextStyle(
color: Colors.white,
fontSize: 25.0,
fontFamily: "SF-Pro-Text-Regular")),
),
),
SizedBox(
height: 10.0,
),
Padding(
padding: const EdgeInsets.only(
left: 12.0, bottom: 12.0),
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 22.0, vertical: 6.0),
decoration: BoxDecoration(
color: Colors.blueAccent,
borderRadius:
BorderRadius.circular(20.0)),
child: GestureDetector(
onTap: () {
print("FFFFFF");
},
child: Text(
"Read more..",
style: TextStyle(color: Colors.white),
),
),
),
)
],
),
)
],
),
),
),
),
);
cardList.add(cardItem);
}
return Stack(
children: cardList,
);
},
),
);
}
}
Result
Tricks and Tips
Make sure that downloaded plugin is unzipped in parent directory of project.
Makes sure that agconnect-services.json file added.
Make sure dependencies are added yaml file.
Run flutter pug get after adding dependencies.
Make sure that service is enabled in agc.
Makes sure images are defined in yaml file.
Conclusion
In this article, we have learnt how to integrate Account kit, Analytics and Ads kit into Huawei StoryApp for flutter. Once Account kit integrated, users can login quickly and conveniently sign in to apps with their Huawei IDs after granting initial access permission. Analytics helps you to analyse the user behaviour and Ads kit helps you to monetize the app.
Thank you so much for reading, I hope thiis article helps you to understand the integration of Huawei Account kit, Ads kit and Analytics kit in flutter.
Reference
Ads Kit
Analytics Kit
Ads Kit – Training Video
Analytics Kit – Training Video
Checkout in forum

Integration of Huawei Account kit, Push kit and Crash kit in Flutter DietApp – Part 4

{
"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
In this article, we will be integrating Huawei Account, Push kit and Analytics kit in Flutter DietApp. Here we can see on filling form diet plan i.e. what kind of food you can have to gain/loose the weight with images, we can see in the diet screen with paid diet plan. Flutter plugin provides simple and convenient way to experience authorization of users. Flutter Account Plugin allows users to connect to the Huawei ecosystem using their Huawei IDs from the different devices such as mobiles phones and tablets, added users can login quickly and conveniently sign in to apps with their Huawei IDs after granting initial access permission.
Huawei Push kit is a messaging service provided for you. It establishes a messaging channel from the cloud to devices. By integrating Push Kit, you can send messages to your apps on user’s devices in real time. This helps you to maintain closer ties with users and increases user awareness and engagement with your apps.
Huawei Analytics is a one-stop solution to get user behavior analysis in different platforms for all yours products such as mobile apps, web apps and quick apps. It helps developers to get detailed analysis report and also provides crash reports by default. It offers scenario-specific data collection, management, analysis, and usage, helping enterprises to achieve effective user acquisition, product optimization, precise operations, and business growth.
We will also learn to integrate Huawei Crash Service in this application. It is a responsibility of the developer to build crash free i.e. unexpected exit of application, it is very difficult to find the cause of the crash in huge application code base. This unexpected crash makes app users annoying and may lead to business loss and reduce market value of the product and the company. To avoid such crashes Huawei provides Crash service which makes developer to find the crash and cause of unexpected exit of the application in AG-console. Crash SDK provides very simple and developer need not code much to implementation crash service. You can also download the detailed crash report whenever required.
Previous article
Please check my previous articles, if you have not gone through, click on part-3.
Development Overview
You 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.
Android 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 process
Step 1: Create Flutter project.
Step 2: Add the App level gradle dependencies. Choose inside project Android > app > build.gradle.
apply plugin: 'com.android.application'
apply plugin: 'com.huawei.agconnect'
Root level gradle dependencies
maven {url 'https://developer.huawei.com/repo/'}
classpath 'com.huawei.agconnect:agcp:1.5.2.300'
Step 3: Add the below permissions in Android Manifest file.
<uses-permission android:name="android.permission.INTERNET" />
Step 4: Download flutter push kit plugins
Step 5: Add downloaded file into parent directory of the project. Declare plugin path in pubspec.yaml file under dependencies.
Add path location for asset image.
Lets start coding
SplashScreen.dart
[/B]
class SplashScreen extends StatefulWidget {
@override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
@override
void initState() {
super.initState();
Timer(
Duration(seconds: 3),
() => Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) => const LoginScreen())));
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white, child: Image.asset('images/logo_huawei.png'));
}
}
[B]
loginScreen.dart
[/B][/B]
class LoginScreen extends StatelessWidget {
const LoginScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: LoginDemo(),
);
}
}
class LoginDemo extends StatefulWidget {
@override
_LoginDemoState createState() => _LoginDemoState();
}
class _LoginDemoState extends State<LoginDemo> {
final HMSAnalytics _hmsAnalytics = new HMSAnalytics();
@override
void initState() {
_enableLog();
super.initState();
}
Future<void> _enableLog() async {
_hmsAnalytics.setUserId("TestUserDietApp");
await _hmsAnalytics.enableLog();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text("Login"),
backgroundColor: Colors.blue,
),
body: RefreshIndicator(
onRefresh: showToast,
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 90.0),
child: Center(
child: Container(
width: 320,
height: 220,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(50.0)),
child: Image.asset('images/logo_huawei.png')),
),
),
Padding(
padding: EdgeInsets.only(
left: 40.0, right: 40.0, top: 15, bottom: 0),
child: TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Email',
hintText: 'Enter valid email id '),
),
),
Padding(
padding: const EdgeInsets.only(
left: 40.0, right: 40.0, top: 10, bottom: 0),
child: TextField(
obscureText: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Password',
hintText: 'Enter password'),
),
),
FlatButton(
onPressed: () {
//TODO FORGOT PASSWORD SCREEN GOES HERE
},
child: Text(
'Forgot Password',
style: TextStyle(color: Colors.blue, fontSize: 15),
),
),
Container(
height: 50,
width: 270,
decoration: BoxDecoration(
color: Colors.red, borderRadius: BorderRadius.circular(20)),
child: FlatButton(
onPressed: () async {
try {
try {
final bool result = await AccountAuthService.signOut();
if (result) {
final bool response =
await AccountAuthService.cancelAuthorization();
}
} on Exception catch (e) {
print(e.toString());
}
} on Exception catch (e) {
print(e.toString());
}
},
child: GestureDetector(
onTap: () async {
try {
final bool response =
await AccountAuthService.cancelAuthorization();
} on Exception catch (e) {
print(e.toString());
}
},
child: Text(
'Login',
style: TextStyle(color: Colors.white, fontSize: 22),
),
),
),
),
SizedBox(
height: 20,
),
Container(
height: 50,
width: 270,
decoration:
BoxDecoration(borderRadius: BorderRadius.circular(5)),
child: HuaweiIdAuthButton(
theme: AuthButtonTheme.FULL_TITLE,
buttonColor: AuthButtonBackground.RED,
borderRadius: AuthButtonRadius.MEDIUM,
onPressed: () {
signInWithHuaweiID();
}),
),
SizedBox(
height: 30,
),
GestureDetector(
onTap: () {
//showBannerAd();
},
child: Text('New User? Create Account'),
),
],
),
),
),
);
}
void signInWithHuaweiID() async {
AccountAuthParamsHelper helper = new AccountAuthParamsHelper();
String name = '';
helper.setAuthorizationCode();
try {
// The sign-in is successful, and the user's ID information and authorization code are obtained.
Future<AuthAccount> account = AccountAuthService.signIn();
account.then(
(value) => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => MyHomePage(
title: value.displayName.toString(),
),
),
),
);
} on Exception catch (e) {
print(e.toString());
}
}
Future<void> showToast() async {
Fluttertoast.showToast(
msg: "Refreshing.. ",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 1,
backgroundColor: Colors.lightBlue,
textColor: Colors.white,
fontSize: 16.0);
}
}
[B][B]
main.dart
[/B][/B][/B]
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Exercise&DietApp',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SplashScreen(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late double? age, height, weight, tagetWeight, days;
TextEditingController ageController = TextEditingController();
TextEditingController genderController = TextEditingController();
TextEditingController heightController = TextEditingController();
TextEditingController weightController = TextEditingController();
TextEditingController targetWeightController = TextEditingController();
TextEditingController inDaysWeightController = TextEditingController();
TextEditingController dietPlanController = TextEditingController();
TextEditingController activeLevelPlanController = TextEditingController();
late SharedPreferences prefs;
String _genderLabel = "Male";
String _dietLabel = "Veg";
String _activeLable = "Low-Active";
final _genderList = ["Male", "Women"];
final _dietPlanList = ["Veg", "Non Veg", "Egg"];
final _activeLevelList = ["Low-Active", "Mid-Active", "Very Active"];
String _token = '';
void initState() {
initPreferences();
initTokenStream();
super.initState();
}
Future<void> initTokenStream() async {
if (!mounted) return;
Push.getTokenStream.listen(_onTokenEvent, onError: _onTokenError);
getToken();
}
void getToken() async {
// Call this method to request for a token
Push.getToken("");
}
void _onTokenEvent(String event) {
// Requested tokens can be obtained here
setState(() {
_token = event;
print("TokenEvent: " + _token);
});
}
void _onTokenError(Object error) {
print("TokenErrorEvent: " + error.toString());
PlatformException? e = error as PlatformException?;
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
resizeToAvoidBottomInset: true,
appBar: AppBar(
title: Text(widget.title.toString()),
automaticallyImplyLeading: false,
),
body: Center(
child: SingleChildScrollView(
reverse: true,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 0.0),
child: Center(
child: Container(
width: 120,
height: 120,
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(60.0)),
child: Image.asset('images/nu_icon.png')),
),
),
Padding(
padding: const EdgeInsets.only(
left: 22.0, right: 22.0, top: 15, bottom: 0),
child: TextField(
controller: ageController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Age',
hintText: 'Enter valid age '),
),
),
Padding(
padding: const EdgeInsets.only(
left: 22.0, right: 22.0, top: 15, bottom: 0),
child: TextField(
controller: heightController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Height',
hintText: 'Enter height '),
),
),
Padding(
padding: const EdgeInsets.only(
left: 22.0, right: 22.0, top: 15, bottom: 0),
child: TextField(
controller: weightController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Weight',
hintText: 'Enter Weight '),
),
),
Padding(
padding: const EdgeInsets.only(
left: 22.0, right: 22.0, top: 15, bottom: 0),
child: TextField(
controller: targetWeightController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Target Weight',
hintText: 'Enter target Weight '),
),
),
Padding(
padding: const EdgeInsets.only(
left: 22.0, right: 22.0, top: 15, bottom: 0),
child: TextField(
controller: inDaysWeightController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'In days',
hintText: 'How quickly you want loose/gain weight '),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
const Text('Gender :'),
DropdownButton(
items: _genderList
.map((String item) => DropdownMenuItem<String>(
child: SizedBox(
height: 22,
width: 100,
child: Text(item,
style: TextStyle(fontSize: 20))),
value: item))
.toList(),
onChanged: (value) {
setState(() {
_genderLabel = value.toString();
});
},
value: _genderLabel,
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('Diet Plan :'),
DropdownButton(
items: _dietPlanList
.map((String item) => DropdownMenuItem<String>(
child: SizedBox(
height: 22,
width: 100,
child: Text(item,
style: TextStyle(fontSize: 20))),
value: item))
.toList(),
onChanged: (value) {
setState(() {
_dietLabel = value.toString();
print('Diet plan changed to $value');
});
},
value: _dietLabel,
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('Active Level :'),
DropdownButton(
items: _activeLevelList
.map((String item) => DropdownMenuItem<String>(
child: SizedBox(
height: 22,
width: 100,
child: Text(item,
style: TextStyle(fontSize: 20))),
value: item))
.toList(),
onChanged: (value) {
setState(() {
_activeLable = value.toString();
print('Active level changed to $value');
});
},
value: _activeLable,
)
],
)
,
Padding(
padding: const EdgeInsets.only(
left: 22.0, right: 22.0, top: 20, bottom: 0),
child: MaterialButton(
child: const Text(
'Next',
style: TextStyle(
fontSize: 22,
color: Colors.white,
fontWeight: FontWeight.w300),
),
height: 45,
minWidth: 140,
color: Colors.lightBlue,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
onPressed: () {
print('Button clicked.....');
age = double.parse(ageController.text);
height = double.parse(heightController.text);
weight = double.parse(weightController.text);
tagetWeight = double.parse(targetWeightController.text);
days = double.parse(inDaysWeightController.text);
storeDataLocally();
},
),
),
],
),
),
),
),
);
}
Future<void> storeDataLocally() async {
prefs.setString("name", widget.title.toString());
prefs.setDouble("age", age!);
prefs.setDouble("height", height!);
prefs.setString("gender", _genderLabel);
prefs.setDouble("weight", weight!);
prefs.setString("dietPlan", _dietLabel);
prefs.setDouble("targetWeight", tagetWeight!);
prefs.setString("activeLevel", _activeLable);
prefs.setDouble("inDays", days!);
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const DietPlanScreen()),
);
}
Future<void> initPreferences() async {
prefs = await SharedPreferences.getInstance();
showData();
}
void showData() {
print("Gender ===>" + prefs.getString('gender').toString());
print("Diet Plan ===>" + prefs.getString('dietPlan').toString());
print("Active Level ===>" + prefs.getString('activeLevel').toString());
if (prefs.getDouble('age')! > 10) {
ageController.text = prefs.getDouble('age').toString();
heightController.text = prefs.getDouble('height').toString();
targetWeightController.text = prefs.getDouble('targetWeight').toString();
genderController.text = prefs.getString('gender').toString();
dietPlanController.text = prefs.getString('dietPlan').toString();
weightController.text = prefs.getDouble('weight').toString();
inDaysWeightController.text = prefs.getDouble('inDays').toString();
activeLevelPlanController.text =
prefs.getString('activeLevel').toString();
if (prefs.getString('gender').toString() != null &&
prefs.getString('gender').toString() != '') {
_genderLabel = prefs.getString('gender').toString();
}
if (prefs.getString('dietPlan').toString() != null) {
_dietLabel = prefs.getString('dietPlan').toString();
}
if (prefs.getString('activeLevel').toString() != null) {
_activeLable = prefs.getString('activeLevel').toString();
}
}
}
}
[B][B][B]
paidPlanScreen.dart
[/B][/B][/B][/B]
class PaidPlanScreen extends StatelessWidget {
String plan = '';
PaidPlanScreen(this.plan, {Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '$plan',
home: MyHomePage(
title: '$plan',
),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late SharedPreferences prefs;
String dietPlan = '', wt_lg = '';
double wt = 0;
bool isGain = false;
void initState() {
initPreference();
super.initState();
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text(widget.title.toString()),
backgroundColor: Colors.blue,
automaticallyImplyLeading: false,
centerTitle: true,
),
body: SingleChildScrollView(
child: Card(
color: Colors.white,
child: Center(
child: Padding(
padding: EdgeInsets.all(15),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Card(
elevation: 3,
shape: Border(
right: BorderSide(color: Colors.red, width: 5)),
child: Padding(
padding: EdgeInsets.all(20),
child: Text(
dietPlan,
style: const TextStyle(
fontSize: 19, color: Colors.deepOrange),
),
),
),
Card(
elevation: 3,
shape: Border(
right:
BorderSide(color: Colors.greenAccent, width: 5)),
child: Padding(
padding: EdgeInsets.all(5),
child: ExpansionTile(
title: Text('Week 1',
style:
TextStyle(fontSize: 22, color: Colors.teal)),
children: <Widget>[
ListTile(
title: getTile('Day 1'),
),
Padding(
padding: EdgeInsets.all(15),
child: Image.asset('images/day1.png'),
),
Text(
'Breakfast: 1 serving Avocado-Egg Toast (271 calories)'
'A.M. Snack: 1 medium orange (62 calories)'
'Lunch: 1 serving Butternut Squash Soup with Avocado & Chickpeas (402 calories)'
'P.M. Snack: 1 medium kiwi (42 calories)'
'Dinner: 1 serving Citrus Poached Salmon with Asparagus with 3/4 cup Cauliflower Rice (451 calories)'
'Daily Totals: 1,228 calories, 58 g protein, 126 g carbohydrates, 30 g fiber, 59 g fat, 1,709 mg sodium.'
'',
style:
TextStyle(color: Colors.green, fontSize: 20),
),
ListTile(
title: Column(
children: <Widget>[
ListTile(
title: getTile('Day 2'),
),
Padding(
padding: EdgeInsets.all(15),
child: Image.asset('images/day2.png'),
),
Text(
'Breakfast: 1 serving Blueberry-Banana Overnight Oats (285 calories)'
'A.M. Snack: 1 serving Apple Cider Vinegar Tonic (22 calories)'
'Lunch: 1 1/2 cups Slow-Cooker Vegetable Soup with 1 slice Everything Bagel Avocado Toast (347 calories)'
'P.M. Snack: 1/2 cup edamame (in pods) sprinkled with a pinch of coarse sea salt (100 calories)'
'Dinner: 1 serving Vegan Coconut Chickpea Curry (471 calories)'
'Meal-Prep Tip: Prepare the Blueberry-Banana Overnight Oats so its ready to grab-and-go in the morning of Day 3.'
'Daily Totals: 1,225 calories, 41 g protein, 181 g carbohydrates, 36 g fiber, 41 g fat, 1,842 mg sodium.',
style: TextStyle(
color: Colors.green, fontSize: 20),
),
/* Image.asset('images/day3.jpg'),
ListTile(
title: getTile('Day 3 '),
),
Image.asset('images/eggfat.jpeg'),
ListTile(
title: getTile('Dried fruits'),
),
Image.asset('images/dryfruit.jpg'),*/
],
),
),
],
),
),
),
Card(
elevation: 3,
shape: Border(
right:
BorderSide(color: Colors.greenAccent, width: 5)),
child: Padding(
padding: EdgeInsets.all(5),
child: ExpansionTile(
title: Text('Week 2',
style:
TextStyle(fontSize: 22, color: Colors.teal)),
children: <Widget>[
ListTile(
title: getTile('Day 1'),
),
Padding(
padding: EdgeInsets.all(15),
child: Image.asset('images/day1.png'),
),
Text(
'Breakfast: 1 serving Avocado-Egg Toast (271 calories)'
'A.M. Snack: 1 medium orange (62 calories)'
'Lunch: 1 serving Butternut Squash Soup with Avocado & Chickpeas (402 calories)'
'P.M. Snack: 1 medium kiwi (42 calories)'
'Dinner: 1 serving Citrus Poached Salmon with Asparagus with 3/4 cup Cauliflower Rice (451 calories)'
'Daily Totals: 1,228 calories, 58 g protein, 126 g carbohydrates, 30 g fiber, 59 g fat, 1,709 mg sodium.'
'',
style:
TextStyle(color: Colors.green, fontSize: 20),
),
ListTile(
title: Column(
children: <Widget>[
ListTile(
title: getTile('Day 2'),
),
Padding(
padding: EdgeInsets.all(15),
child: Image.asset('images/day2.png'),
),
Text(
'Breakfast: 1 serving Blueberry-Banana Overnight Oats (285 calories)'
'A.M. Snack: 1 serving Apple Cider Vinegar Tonic (22 calories)'
'Lunch: 1 1/2 cups Slow-Cooker Vegetable Soup with 1 slice Everything Bagel Avocado Toast (347 calories)'
'P.M. Snack: 1/2 cup edamame (in pods) sprinkled with a pinch of coarse sea salt (100 calories)'
'Dinner: 1 serving Vegan Coconut Chickpea Curry (471 calories)'
'Meal-Prep Tip: Prepare the Blueberry-Banana Overnight Oats so its ready to grab-and-go in the morning of Day 3.'
'Daily Totals: 1,225 calories, 41 g protein, 181 g carbohydrates, 36 g fiber, 41 g fat, 1,842 mg sodium.',
style: TextStyle(
color: Colors.green, fontSize: 20),
),
/* Image.asset('images/day3.jpg'),
ListTile(
title: getTile('Day 3 '),
),
Image.asset('images/eggfat.jpeg'),
ListTile(
title: getTile('Dried fruits'),
),
Image.asset('images/dryfruit.jpg'),*/
],
),
),
],
),
),
),
],
),
),
),
),
),
),
);
}
void getPlan() {
double? days = prefs.getDouble('inDays');
double? tagetWeight = prefs.getDouble('targetWeight');
double? weight = prefs.getDouble('weight');
print('Target $tagetWeight');
print('weight $weight');
setState(() {
if (tagetWeight! > weight!) {
setState(() {
isGain = true;
});
wt_lg = 'gain';
wt = tagetWeight - weight;
dietPlan = 'Aim is to $wt_lg $wt kg weight in $days days.';
} else {
setState(() {
isGain = false;
});
wt_lg = 'loose';
wt = weight - tagetWeight;
dietPlan = 'Aim is to $wt_lg $wt kg weight in $days days.';
}
});
}
Future<void> initPreference() async {
prefs = await SharedPreferences.getInstance();
getPlan();
}
RichText getTile(String item) {
return RichText(
text: TextSpan(
text: '• ',
style: TextStyle(
color: Colors.lightBlue,
),
children: <TextSpan>[
TextSpan(text: '$item', style: TextStyle(fontSize: 18)),
],
),
);
}
}
[B][B][B][B]
How do I report a crash?
[/B][/B][/B][/B]
void crashTest() {
CrashService.enableCollection();
AGCCrash.instance.setUserId("TestDietApp");
AGCCrash.instance.setCustomKey("Test",
"This is test crash for crash service while login button click.");
AGCCrash.instance.setCustomKey("LoginCLickTestCrash", "Crash Test");
AGCCrash.instance.testIt();
}
[B][B][B][B]
Result
Tricks and Tips
Make sure that downloaded plugin is unzipped in parent directory of project.
Makes sure that agconnect-services.json file added.
Make sure dependencies are added yaml file.
Run flutter pug get after adding dependencies.
Make sure that service is enabled in agc.
Makes sure images are defined in yaml file.
Conclusion
In this article, we have learnt how to integrate Huawei Account kit, Push kit and Crash kit in Flutter DietApp. Once Account kit integrated, users can login quickly and conveniently sign in to apps with their Huawei IDs after granting initial access permission. Push kit enables you to send push notification to user device in real time, you can see the push notification in the result part. Huawei Crash service which provides developers to quickly detect, locate and fix the crash or unexpected exit of application. Hence improves stability and reliability of application.
Thank you so much for reading. I hope this article helps you to understand the integration of Huawei Account kit, Push kit and Crash kit in flutter DietApp.
Reference
Crash Kit
Account Kit – Training Video
Push Kit – Training Video
Crash Kit – Training Video
Checkout in forum

Integration of Huawei Account kit, Remote Configuration, Crash kit and IAP Service in Flutter DietApp – Part 6

{
"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
In this article, we will be integrating Huawei Remote Configuration service, Account Kit, Crash Kit and Huawei IAP Service in Flutter DietApp. Flutter plugin provides simple and convenient way to experience authorization of users. Flutter Account Plugin allows users to connect to the Huawei ecosystem using their Huawei IDs from the different devices such as mobiles phones and tablets, added users can login quickly and conveniently sign in to apps with their Huawei IDs after granting initial access permission.
We will also learn to integrate Huawei Crash Service in this application. It is a responsibility of the developer to build crash free i.e. unexpected exit of application, it is very difficult to find the cause of the crash in huge application code base. This unexpected crash makes app users annoying and may lead to business loss and reduce market value of the product and the company. To avoid such crashes Huawei provides Crash service which makes developer to find the crash and cause of unexpected exit of the application in AG-console.
Crash SDK provides very simple and developer need not code much to implementation crash service. You can also download the detailed crash report whenever required.
Remote Configuration is a service provided by Huawei. It provides cloud-based services. Once you integrate the client SDK, your app can periodically fetch parameter values from the cloud. The service checks whether the parameter that your app tries fetching has an on-cloud value update and returns the new value if so. Based on the parameter values fetched, you can implement service processing logic to change behaviour or appearance of your app. In this sample i.e. DietApp, we are fetching the Personal Exercise data using Remote Configuration service by AppGallery.
Flutter In-App Purchases (IAP) plugin provides communication between the HMS Core IAP SDK and Flutter platform. Huawei IAP allows you to offer in-app purchases and facilitates in-app payment. Users can purchase a variety of virtual products, including one-time virtual products and subscriptions, directly within your app.
Flutter IAP plugin provides the following APIs, which are also core capabilities you need to quickly build apps with which your users can buy, consume, and subscribe to services you provide:
isEnvReady: Returns a response which indicates user's sign-in and location status for HUAWEI IAP.
isSandboxActivated: Returns a response which indicates user's account capabilities for sandbox testing.
obtainProductInfo: Returns a list of products.
startIapActivity: Starts an activity to manage and edit subscriptions.
createPurchaseIntent: Starts an activity to purchase a desired product or subscribe to a product.
consumeOwnedPurchase: Consumes a desired product.
obtainOwnedPurchases: Returns the list of products purchased by a user.
obtainOwnedPurchaseRecord: Returns the list of products purchased and consumed by a user.
Previous article
Please check my previous articles, if you have not gone through, click on part-5.
Development Overview
You 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.
Android 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 process
Step 1: Create Flutter project.
Step 2: Add the App level gradle dependencies. Choose inside project Android > app > build.gradle.
apply plugin: 'com.android.application'
apply plugin: 'com.huawei.agconnect'
Root level gradle dependencies
maven {url 'https://developer.huawei.com/repo/'}
classpath 'com.huawei.agconnect:agcp:1.5.2.300'
Step 3: Add the below permissions in Android Manifest file.
<uses-permission android:name="android.permission.INTERNET" />
Step 4: Download flutter IAP kit plugins
Step 5: Add downloaded file into parent directory of the project. Declare plugin path in pubspec.yaml file under dependencies.
Add path location for asset image.
Let's start coding
SplashScreen.dart
[/B]
class SplashScreen extends StatefulWidget {
@override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
@override
void initState() {
super.initState();
Timer(
Duration(seconds: 3),
() => Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) => const LoginScreen())));
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white, child: Image.asset('images/logo_huawei.png'));
}
}
[B]
loginScreen.dart
[/B][/B]
class LoginScreen extends StatelessWidget {
const LoginScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: LoginDemo(),
);
}
}
class LoginDemo extends StatefulWidget {
@override
_LoginDemoState createState() => _LoginDemoState();
}
class _LoginDemoState extends State<LoginDemo> {
final HMSAnalytics _hmsAnalytics = new HMSAnalytics();
@override
void initState() {
_enableLog();
super.initState();
}
Future<void> _enableLog() async {
_hmsAnalytics.setUserId("TestUserDietApp");
await _hmsAnalytics.enableLog();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text("Login"),
backgroundColor: Colors.blue,
),
body: RefreshIndicator(
onRefresh: showToast,
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 90.0),
child: Center(
child: Container(
width: 320,
height: 220,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(50.0)),
child: Image.asset('images/logo_huawei.png')),
),
),
Padding(
padding: EdgeInsets.only(
left: 40.0, right: 40.0, top: 15, bottom: 0),
child: TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Email',
hintText: 'Enter valid email id '),
),
),
Padding(
padding: const EdgeInsets.only(
left: 40.0, right: 40.0, top: 10, bottom: 0),
child: TextField(
obscureText: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Password',
hintText: 'Enter password'),
),
),
FlatButton(
onPressed: () {
//TODO FORGOT PASSWORD SCREEN GOES HERE
},
child: Text(
'Forgot Password',
style: TextStyle(color: Colors.blue, fontSize: 15),
),
),
Container(
height: 50,
width: 270,
decoration: BoxDecoration(
color: Colors.red, borderRadius: BorderRadius.circular(20)),
child: FlatButton(
onPressed: () async {
try {
try {
final bool result = await AccountAuthService.signOut();
if (result) {
final bool response =
await AccountAuthService.cancelAuthorization();
}
} on Exception catch (e) {
print(e.toString());
}
} on Exception catch (e) {
print(e.toString());
}
},
child: GestureDetector(
onTap: () async {
try {
final bool response =
await AccountAuthService.cancelAuthorization();
} on Exception catch (e) {
print(e.toString());
}
},
child: Text(
'Login',
style: TextStyle(color: Colors.white, fontSize: 22),
),
),
),
),
SizedBox(
height: 20,
),
Container(
height: 50,
width: 270,
decoration:
BoxDecoration(borderRadius: BorderRadius.circular(5)),
child: HuaweiIdAuthButton(
theme: AuthButtonTheme.FULL_TITLE,
buttonColor: AuthButtonBackground.RED,
borderRadius: AuthButtonRadius.MEDIUM,
onPressed: () {
signInWithHuaweiID();
}),
),
SizedBox(
height: 30,
),
GestureDetector(
onTap: () {
//showBannerAd();
},
child: Text('New User? Create Account'),
),
],
),
),
),
);
}
void signInWithHuaweiID() async {
AccountAuthParamsHelper helper = new AccountAuthParamsHelper();
String name = '';
helper.setAuthorizationCode();
try {
// The sign-in is successful, and the user's ID information and authorization code are obtained.
Future<AuthAccount> account = AccountAuthService.signIn();
account.then(
(value) => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => MyHomePage(
title: value.displayName.toString(),
),
),
),
);
} on Exception catch (e) {
print(e.toString());
}
}
Future<void> showToast() async {
Fluttertoast.showToast(
msg: "Refreshing.. ",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 1,
backgroundColor: Colors.lightBlue,
textColor: Colors.white,
fontSize: 16.0);
}
}
[B][B]
main.dart
[/B][/B][/B]
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Exercise&DietApp',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SplashScreen(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late double? age, height, weight, tagetWeight, days;
TextEditingController ageController = TextEditingController();
TextEditingController genderController = TextEditingController();
TextEditingController heightController = TextEditingController();
TextEditingController weightController = TextEditingController();
TextEditingController targetWeightController = TextEditingController();
TextEditingController inDaysWeightController = TextEditingController();
TextEditingController dietPlanController = TextEditingController();
TextEditingController activeLevelPlanController = TextEditingController();
late SharedPreferences prefs;
String _genderLabel = "Male";
String _dietLabel = "Veg";
String _activeLable = "Low-Active";
final _genderList = ["Male", "Women"];
final _dietPlanList = ["Veg", "Non Veg", "Egg"];
final _activeLevelList = ["Low-Active", "Mid-Active", "Very Active"];
String _token = '';
void initState() {
initPreferences();
initTokenStream();
super.initState();
}
Future<void> initTokenStream() async {
if (!mounted) return;
Push.getTokenStream.listen(_onTokenEvent, onError: _onTokenError);
getToken();
}
void getToken() async {
// Call this method to request for a token
Push.getToken("");
}
void _onTokenEvent(String event) {
// Requested tokens can be obtained here
setState(() {
_token = event;
print("TokenEvent: " + _token);
});
}
void _onTokenError(Object error) {
print("TokenErrorEvent: " + error.toString());
PlatformException? e = error as PlatformException?;
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
resizeToAvoidBottomInset: true,
appBar: AppBar(
title: Text(widget.title.toString()),
automaticallyImplyLeading: false,
),
body: Center(
child: SingleChildScrollView(
reverse: true,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 0.0),
child: Center(
child: Container(
width: 120,
height: 120,
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(60.0)),
child: Image.asset('images/nu_icon.png')),
),
),
Padding(
padding: const EdgeInsets.only(
left: 22.0, right: 22.0, top: 15, bottom: 0),
child: TextField(
controller: ageController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Age',
hintText: 'Enter valid age '),
),
),
Padding(
padding: const EdgeInsets.only(
left: 22.0, right: 22.0, top: 15, bottom: 0),
child: TextField(
controller: heightController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Height',
hintText: 'Enter height '),
),
),
Padding(
padding: const EdgeInsets.only(
left: 22.0, right: 22.0, top: 15, bottom: 0),
child: TextField(
controller: weightController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Weight',
hintText: 'Enter Weight '),
),
),
Padding(
padding: const EdgeInsets.only(
left: 22.0, right: 22.0, top: 15, bottom: 0),
child: TextField(
controller: targetWeightController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Target Weight',
hintText: 'Enter target Weight '),
),
),
Padding(
padding: const EdgeInsets.only(
left: 22.0, right: 22.0, top: 15, bottom: 0),
child: TextField(
controller: inDaysWeightController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'In days',
hintText: 'How quickly you want loose/gain weight '),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
const Text('Gender :'),
DropdownButton(
items: _genderList
.map((String item) => DropdownMenuItem<String>(
child: SizedBox(
height: 22,
width: 100,
child: Text(item,
style: TextStyle(fontSize: 20))),
value: item))
.toList(),
onChanged: (value) {
setState(() {
_genderLabel = value.toString();
});
},
value: _genderLabel,
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('Diet Plan :'),
DropdownButton(
items: _dietPlanList
.map((String item) => DropdownMenuItem<String>(
child: SizedBox(
height: 22,
width: 100,
child: Text(item,
style: TextStyle(fontSize: 20))),
value: item))
.toList(),
onChanged: (value) {
setState(() {
_dietLabel = value.toString();
print('Diet plan changed to $value');
});
},
value: _dietLabel,
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('Active Level :'),
DropdownButton(
items: _activeLevelList
.map((String item) => DropdownMenuItem<String>(
child: SizedBox(
height: 22,
width: 100,
child: Text(item,
style: TextStyle(fontSize: 20))),
value: item))
.toList(),
onChanged: (value) {
setState(() {
_activeLable = value.toString();
print('Active level changed to $value');
});
},
value: _activeLable,
)
],
)
,
Padding(
padding: const EdgeInsets.only(
left: 22.0, right: 22.0, top: 20, bottom: 0),
child: MaterialButton(
child: const Text(
'Next',
style: TextStyle(
fontSize: 22,
color: Colors.white,
fontWeight: FontWeight.w300),
),
height: 45,
minWidth: 140,
color: Colors.lightBlue,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
onPressed: () {
print('Button clicked.....');
age = double.parse(ageController.text);
height = double.parse(heightController.text);
weight = double.parse(weightController.text);
tagetWeight = double.parse(targetWeightController.text);
days = double.parse(inDaysWeightController.text);
storeDataLocally();
},
),
),
],
),
),
),
),
);
}
Future<void> storeDataLocally() async {
prefs.setString("name", widget.title.toString());
prefs.setDouble("age", age!);
prefs.setDouble("height", height!);
prefs.setString("gender", _genderLabel);
prefs.setDouble("weight", weight!);
prefs.setString("dietPlan", _dietLabel);
prefs.setDouble("targetWeight", tagetWeight!);
prefs.setString("activeLevel", _activeLable);
prefs.setDouble("inDays", days!);
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const DietPlanScreen()),
);
}
Future<void> initPreferences() async {
prefs = await SharedPreferences.getInstance();
showData();
}
void showData() {
print("Gender ===>" + prefs.getString('gender').toString());
print("Diet Plan ===>" + prefs.getString('dietPlan').toString());
print("Active Level ===>" + prefs.getString('activeLevel').toString());
if (prefs.getDouble('age')! > 10) {
ageController.text = prefs.getDouble('age').toString();
heightController.text = prefs.getDouble('height').toString();
targetWeightController.text = prefs.getDouble('targetWeight').toString();
genderController.text = prefs.getString('gender').toString();
dietPlanController.text = prefs.getString('dietPlan').toString();
weightController.text = prefs.getDouble('weight').toString();
inDaysWeightController.text = prefs.getDouble('inDays').toString();
activeLevelPlanController.text =
prefs.getString('activeLevel').toString();
if (prefs.getString('gender').toString() != null &&
prefs.getString('gender').toString() != '') {
_genderLabel = prefs.getString('gender').toString();
}
if (prefs.getString('dietPlan').toString() != null) {
_dietLabel = prefs.getString('dietPlan').toString();
}
if (prefs.getString('activeLevel').toString() != null) {
_activeLable = prefs.getString('activeLevel').toString();
}
}
}
}
[B][B][B]
tabScreen.dart
[/B][/B][/B][/B]
class TabScreen extends StatefulWidget {
@override
TabScreenPage createState() => TabScreenPage();
}
class TabScreenPage extends State<TabScreen> {
final rnd = math.Random();
List<String> gridItems = [];
@override
void initState() {
// TODO: implement initState
fetchRemoteConfiguration();
super.initState();
}
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
title: const Text(
'Diet Plan',
),
centerTitle: true,
backgroundColor: Colors.blue,
elevation: 0,
),
body: Column(
children: <Widget>[
// the tab bar with two items
SizedBox(
height: 50,
child: AppBar(
bottom: const TabBar(
tabs: [
Tab(
text: 'Paid Diet Plans',
),
Tab(
text: 'Personal Exercise',
),
],
),
),
),
// create widgets for each tab bar here
Expanded(
child: TabBarView(
children: [
// first tab bar view widget
Container(
color: Colors.white,
child: ListView(
padding: EdgeInsets.all(8),
children: <Widget>[
Card(
child: ListTile(
title: Text("30 days plan"),
iconColor: Colors.blue,
subtitle: Text('Meal Plan: Burn 1,200 Calories'),
leading: Icon(Icons.sports_gymnastics),
onTap: () {
routeScreen('30 days plan');
},
),
),
Card(
child: ListTile(
title: Text("28 days plan"),
iconColor: Colors.blue,
subtitle: Text('Meal Plan: Burn 950 Calories'),
leading: Icon(Icons.sports_gymnastics),
onTap: () {
routeScreen('28 days plan');
},
),
),
Card(
child: ListTile(
title: Text("22 days plan"),
iconColor: Colors.blue,
subtitle: Text('Meal Plan: Burn 850 Calories'),
leading: Icon(Icons.sports_gymnastics),
onTap: () {
routeScreen('22 days plan');
},
),
),
Card(
child: ListTile(
iconColor: Colors.blue,
title: Text("18 days plan"),
subtitle: Text('Meal Plan: Burn 650 Calories'),
leading: Icon(Icons.sports_gymnastics),
onTap: () {
routeScreen('18 days plan');
},
),
),
],
),
),
// second tab bar viiew widget
Container(
color: Colors.white,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: GridView.builder(
gridDelegate:
const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200,
childAspectRatio: 3 / 3,
crossAxisSpacing: 20,
mainAxisSpacing: 20),
itemCount: gridItems.length,
itemBuilder: (BuildContext ctx, index) {
return Center(
child: GestureDetector(
onTap: () {
print(' ' + gridItems[index]);
checkDeviceEnvirnoment();
//makePayment();
//checkSDK();
},
child: Container(
alignment: Alignment.center,
child: Image.asset('images/image$index.png'),
decoration: BoxDecoration(
color: Colors.primaries[Random()
.nextInt(Colors.primaries.length)],
borderRadius: BorderRadius.circular(15)),
),
),
);
}),
),
),
],
),
),
],
),
),
);
}
void routeScreen(String title) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => PaidPlanScreen(title)),
);
}
Future<void> fetchRemoteConfiguration() async {
await AGCRemoteConfig.instance
.fetch()
.catchError((error) => print(error.toString()));
await AGCRemoteConfig.instance.applyLastFetched();
Map value = await AGCRemoteConfig.instance.getMergedAll();
final data = jsonDecode(value["categories"].toString());
List<String> gridItem = List<String>.from(data['categories']);
setState(() {
gridItems.addAll(gridItem);
});
}
void checkDeviceEnvirnoment() async {
try {
//Call the isEnvReady API.
IsEnvReadyResult result = await IapClient.isEnvReady();
print('*****');
print(result.returnCode.toString());
//Call isSandboxActivated API.
IsSandboxActivatedResult result1 = await IapClient.isSandboxActivated();
print('####');
//Print the isSandboxUser property.
print(result1.isSandboxUser.toString());
bool? isUser = result1.isSandboxUser;
if (isUser!) {
makePayment();
}
} on Exception catch (_, e) {
print(e.toString());
}
}
void makePayment() async {
try {
//Constructing the request.
PurchaseIntentReq request = PurchaseIntentReq(
productId: '11223344', priceType: IapClient.IN_APP_CONSUMABLE);
request.priceType =
0; //You may also use IN_APP_CONSUMABLE from IapClient's constant values.
request.productId = "11223344";
request.developerPayload = "Test";
//Call the createPurchaseIntent API.
PurchaseResultInfo result = await IapClient.createPurchaseIntent(request);
print(result.inAppPurchaseData.toString());
} on Exception catch (e) {
print(e.toString());
}
}
}
[B][B][B][B]
Result
Using Remote Configuration fetching cloud data and displayed in UI
Before you use any service in AppGallery Console, enable the service first like shown below :
IAP allows three types products to add in AppGallery Console once you click navigate below shown path click add products and choose the desired product type and add details and save, after adding Activate the product then only it will be accessible.
Tricks and Tips
Make sure that downloaded plugin is unzipped in parent directory of project.
Makes sure that agconnect-services.json file added.
Make sure dependencies are added yaml file.
Run flutter pug get after adding dependencies.
Make sure that service is enabled in agc.
Makes sure images are defined in yaml file.
Add products in AppGallery My apps/Operate/Product Management
Conclusion
In this article, we have learnt how to integrate Huawei Remote Configuration service, Account kit, Crash kit flutter DietApp. Once Account kit integrated, users can login quickly and conveniently sign in to apps with their Huawei IDs after granting initial access permission. Push kit enables you to send push notification to user device in real time, you can see the push notification in the result part. Huawei Crash service which provides developers to quickly detect, locate and fix the crash or unexpected exit of application. Hence improves stability and reliability of application. With the help of Remote Configuration service we are fetching the Personal Exercise data from the cloud. Using Huawei IAP we are making payment for access personal exercise.
Thank you so much for reading. I hope this article helps you to understand the integration of Huawei Remote Configuration, Account kit, Huawei IAP and Crash kit in flutter DietApp.
Reference
Crash Kit
Account Kit – Training Video
Crash Kit – Training Video
IAP
Checkout in forum

Categories

Resources