[Huawei Developers] Berk Özyurt:
​

Hello everyone,

In this article, I will give you some information about the Auth Service offered by Huawei AppGallery Connect to developers and how to use it in cross-platform applications that you will develop with Flutter.

What is Auth Service ?

Many mobile applications require membership systems and authentication methods. Setting up this system from scratch in mobile applications can be difficult and time consuming. Huawei AGC Auth Service enables you to quickly and securely integrate this authentication process into your mobile application. Moreover, Auth Service offers many authentication methods. It can be used in Android Native, IOS Native and cross-platform (Flutter, React-Native, Cordova) projects.

Highly secure, fast and easy to use, Auth Service supports all the following account methods and authentication methods.

Mobile Number (Android, IOS, Web)

Email Address (Android, IOS, Web)

HUAWEI ID (Android)

HUAWEI Game Center account (Android)

WeChat account (Android, IOS, Web)

QQ account (Android, IOS, Web)

Weibo account (Android, IOS)

Apple ID (IOS)

Google account* (Android, IOS)

Google Play Games account* (Android)

Facebook account* (Android, IOS)

Twitter account* (Android, IOS)

Anonymous account (Android, IOS, Web)

Self-owned account (Android, IOS)

Development Steps

Integration

After creating your application on the AGC Console and completing all of the necessary steps, 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.

For IOS, the agconnect-services.plist configuration file should be added under ios/Runner 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.

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.

apply plugin: 'com.huawei.agconnect'

Finally, the Auth Service SDK should be added to the pubspec.yaml file. To do this, open the pubspec.yaml file and add the required dependency as follows.

dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
agconnect_auth: ^1.1.0
agconnect_core: ^1.1.0

And, by clicking “pub get”, the dependencies are added to Android Studio. After all these steps are completed, your app is ready to code.

2. Register with Email

Create a new Dart file named AuthManager that contains all the operations we will do with Auth Service. In this class, the necessary methods for all operations such as sending verification code, registration, login will be written and all operations will be done in this class without any code confusion in the interface class.

When registering with the user’s email address, a verification code must be sent to the entered email address. In this way, it will be determined whether the users are a real person and security measures will be taken. For this, create a method called sendRegisterVerificationCode that takes the email address entered by the user as a parameter and sends a verification code to this email address. By creating a VerifyCodeSettings object within the method, it is specified for what purpose the verification code will be used by making the VerifyCodeAction value “registerLogin”. Finally, with EmailAuthProvider.requestVerifyCode, the verification code is sent to the mail address. Yo can find all of the method on the below.

void sendRegisterVerificationCode(String email) async{
VerifyCodeSettings settings = VerifyCodeSettings(VerifyCodeAction.registerLogin, sendInterval: 30);
EmailAuthProvider.requestVerifyCode(email, settings)
.then((result){
print("sendRegisterVerificationCode : " + result.validityPeriod);
});
}

After the user receives the verification code, the user is registered with the e-mail address, password, and verification code. Each user must set a special password, and this password must be at least 8 characters long and different from the e-mail address. In addition, lowercase letters, uppercase letters, numbers, spaces or special characters must meet at least two of the requirements. In order to the registration process, a method named registerWithEmail should be created and mail address, verification code and password should be given as parameters. Then create an EmailUser object and set these values. Finally, a new user is created with the AGCAuth.instance.createEmailUser (user) line. You can find registerWithEmail method on the below.

void registerWithEmail(String email, String verifyCode, String password, BuildContext context) async{
EmailUser user = EmailUser(email, verifyCode, password: password);
AGCAuth.instance.createEmailUser(user)
.then((signInResult) {
print("registerWithEmail : " + signInResult.user.email);
.catchError((error) {
print("Register Error " + error.toString());
_showMyDialog(context, error.toString());
});
}

3. Signin with Email

In order for users to log in to your mobile app after they have registered, a verification code should be sent. To send the verification code while logging in, a method should be created as in the registration, and a verification code should be sent to the e-mail address with this method.

After the verification code is sent, the user can login to the app with their e-mail address, password and verification code. You can test whether the operation is successful by adding .then and .catchError to the login method. You can find all the codes for the sign-in method below.

void sendSigninVerificationCode(String email) async{
VerifyCodeSettings settings = VerifyCodeSettings(VerifyCodeAction.registerLogin, sendInterval: 30);
EmailAuthProvider.requestVerifyCode(email, settings)
.then((result){
print("sendSigninVerificationCode : " + result.validityPeriod);
});
}

void loginWithEmail(String email, String verifyCode, String password) async{
AGCAuthCredential credential = EmailAuthProvider.credentialWithVerifyCode(email, verifyCode, password: password);
AGCAuth.instance.signIn(credential)
.then((signInResult){
AGCUser user = signInResult.user;
print("loginWithEmail : " + user.displayName);

})
.catchError((error){
print("Login Error " + error.toString());
});
}

4. Reset Password

If the user forgets or wants to change his password, the password reset method provided by Auth Service should be used. Otherwise, the user cannot change his password, and cannot log into his account.

As in every auth method, a verification code is still required when resetting the password. This verification code should be sent to the user’s mail address, similar to the register and sign. Unlike the register and signin operations, the VerifyCodeSettings parameter must be VerifyCodeAction.resetPassword. After sending the verification code to the user’s e-mail address, password reset can be done as follows.

void sendResetPasswordVerificationCode(String email) async{
VerifyCodeSettings settings = VerifyCodeSettings(VerifyCodeAction.resetPassword, sendInterval: 30);
EmailAuthProvider.requestVerifyCode(email, settings)
.then((result){
print(result.validityPeriod);
});
}

void resetPassword(String email, String newPassword, String verifyCode) async{
AGCAuth.instance.resetPasswordWithEmail(email, newPassword, verifyCode)
.then((value) {
print("Password Reseted");
})
.catchError((error) {
print("Password Reset Error " + error);
});
}

5. Logout

To end the user’s current session, an instance must be created from the AGCAuth object and the signOut() method must be called. You can find this code block on the below.

void signOut() async{
AGCAuth.instance.signOut().then((value) {
print("SignInSuccess");
}).catchError((error) => print("SignOut Error : " + error));
}

6. User Information

Auth Service provides a lot of data to show the user information of a logged in user. In order to obtain this data, an instance can be created from the AGCAuth object and all the information belonging to the user can be listed with the currentUser method.

void getCurrentUser() async {
AGCAuth.instance.currentUser.then((value) {
print('current user = ${value?.uid} , ${value?.email} , ${value?.displayName} , ${value?.phone} , ${value?.photoUrl} ');
});
}

The AuthManager class must contain these operations. Thanks to the above methods, you can log in and register with your Email address in your app. You can create an object from the AuthManager class and call the method you need wherever you need it. Now that the AuthManager class is complete, a registration page can be designed and the necessary methods can be called.

7. Create Register Page

I will share an example to give you an idea about design. I designed an animation so that the elements in the page design come with animation at 5 second intervals. In addition, I prepared a design that makes circles around the icon you add periodically to highlight your application’s logo.

I used the avatar_glow library for this. Avatar Glow library allows us to make a simple and stylish design. To add this library, you can add “avatar_glow: ^ 1.1.0” line to pubspec.yaml file and integrate it into your project with “pub get”.

After the library is added, we create a Dart file named DelayedAnimation to run the animations. In this class, we define all the features of animation. You can find all the codes of the class below.

import 'dart:async';

import 'package:flutter/material.dart';

class DelayedAnimation extends StatefulWidget {
final Widget child;
final int delay;

DelayedAnimation({@required this.child, this.delay});

@override
_DelayedAnimationState createState() => _DelayedAnimationState();
}

class _DelayedAnimationState extends State<DelayedAnimation>
with TickerProviderStateMixin {
AnimationController _controller;
Animation<Offset> _animOffset;

@override
void initState() {
super.initState();

_controller =
AnimationController(vsync: this, duration: Duration(milliseconds: 800));
final curve =
CurvedAnimation(curve: Curves.decelerate, parent: _controller);
_animOffset =
Tween<Offset>(begin: const Offset(0.0, 0.35), end: Offset.zero)
.animate(curve);

if (widget.delay == null) {
_controller.forward();
} else {
Timer(Duration(milliseconds: widget.delay), () {
_controller.forward();
});
}
}

@override
void dispose() {
super.dispose();
_controller.dispose();
}

@override
Widget build(BuildContext context) {
return FadeTransition(
child: SlideTransition(
position: _animOffset,
child: widget.child,
),
opacity: _controller,
);
}
}

Then we can create a Dart file called RegisterPage and continue coding.

In this class, we first set a fixed delay time. I set it to 500 ms. Then I increased it by 500ms for each element and made it load one after the other.

Then TextEditingController objects should be created to get values such as email, password, verify code written into TextFormField.

Finally, when clicked the send verification code button, I set a visibility value as bool to change the name of the button and the visibility of the field where a new verification code will be entered.

final int delayedAmount = 500;
AnimationController _controller;
bool _visible = false;
String buttonText = "Send Verify Code";

TextEditingController emailController = new TextEditingController();
TextEditingController passwordController = new TextEditingController();
TextEditingController verifyCodeController = new TextEditingController();

Now, AnimationController values must be set in initState method.

@override
void initState() {
_controller = AnimationController(
vsync: this,
duration: Duration(
milliseconds: 200,
),
lowerBound: 0.0,
upperBound: 0.1,
)..addListener(() {
setState(() {});
});
super.initState();
}

Then a method should be created for the verification code send button and the save button, and these methods should be called in the Widget build method where necessary. In both methods, first of all, the visibility values and texts should be changed and the related methods should be called by creating an object from the AuthManager class.

void _toggleVerifyCode() {
setState(() {
_visible = true;
buttonText = "Send Again";
final AuthManager authManager = new AuthManager();
authManager.sendRegisterVerificationCode(emailController.text);
});
}

void _toggleRegister() {
setState(() {
_visible = true;
buttonText = "Send Again";
final AuthManager authManager = new AuthManager();
authManager.registerWithEmail(emailController.text, verifyCodeController.text, passwordController.text, this.context);
});
}

Finally, in the Widget build method, the design of each element should be prepared separately and returned at the end. If all the codes are written under return, the code will look too complex and debugging or modification will be difficult. As seen on the below, I prepared an Avatar Glow object at the top. Then, create two TextFormFields for the user to enter their mail address and password. Under these two TextFormFields, there is a button for sending the verification code. When this button is clicked, a verification code is sent to the mail address, and a new button design is created for entering this verification code and a new TextFormField and register operations. Yo can find screenshots and all of the codes on the below.

​

@override
Widget build(BuildContext context) {

final color = Color(0xFFF4EADE);
_scale = 1 - _controller.value;

final logo = AvatarGlow(
endRadius: 90,
duration: Duration(seconds: 2),
glowColor: Color(0xFF2F496E),
repeat: true,
repeatPauseDuration: Duration(seconds: 2),
startDelay: Duration(seconds: 1),
child: Material(
elevation: 8.0,
shape: CircleBorder(),
child: CircleAvatar(
backgroundColor: Color(0xFFF4EADE),
backgroundImage: AssetImage('assets/huawei_logo.png'),
radius: 50.0,
)
),
);

final title = DelayedAnimation(
child: Text(
"Register",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 35.0,
color: Color(0xFF2F496E)),
),
delay: delayedAmount + 500,
);

final email = DelayedAnimation(
delay: delayedAmount + 500,
child: TextFormField(
controller: emailController,
keyboardType: TextInputType.emailAddress,
autofocus: false,
decoration: InputDecoration(
hintText: '* Email',
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(100.0)),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(100.0),
borderSide: BorderSide(
color: Color(0xFF2F496E),
),
),
),
),
);

final password = DelayedAnimation(
delay: delayedAmount + 1000,
child: TextFormField(
controller: passwordController,
autofocus: false,
obscureText: true,
decoration: InputDecoration(
hintText: '* Password',
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(100.0)),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(100.0),
borderSide: BorderSide(
color: Color(0xFF2F496E),
),
),
),
),
);

final sendVerifyCodeButton = RaisedButton(
color: Color(0xFF2F496E),
highlightColor: Color(0xFF2F496E),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100.0),
),
onPressed: _toggleVerifyCode,
child: Text(
buttonText,
style: TextStyle(
fontSize: 15.0,
fontWeight: FontWeight.normal,
color: color,
),
),
);

final verifyCode = DelayedAnimation(
delay: 500,
child: TextFormField(
controller: verifyCodeController,
keyboardType: TextInputType.emailAddress,
autofocus: false,
decoration: InputDecoration(
hintText: '* Verify Code',
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(100.0)),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(100.0),
borderSide: BorderSide(
color: Color(0xFF2F496E),
),
),
),
),
);

final registerButton = RaisedButton(
color: Color(0xFF2F496E),
highlightColor: Color(0xFF2F496E),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100.0),
),
onPressed: _toggleRegister,
child: Text(
'Register',
style: TextStyle(
fontSize: 15.0,
fontWeight: FontWeight.normal,
color: color,
),
),
);

return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
backgroundColor: Color(0xFFF4EADE),
body: Center(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
new Container(
margin: const EdgeInsets.all(20.0),
child: new Container()
),
title,
logo,
SizedBox(
height: 50,
width: 300,
child: email,
),
SizedBox(height: 15.0),
SizedBox(
height: 50,
width: 300,
child: password,
),
SizedBox(height: 15.0),
SizedBox(
height: 40,
width: 300,
child: DelayedAnimation(
delay: delayedAmount + 1500,
child: sendVerifyCodeButton
),
),
SizedBox(height: 15.0),
SizedBox(
height: 50,
width: 300,
child: Visibility(
maintainSize: true,
maintainAnimation: true,
maintainState: true,
visible: _visible,
child: DelayedAnimation(
delay: delayedAmount + 1500,
child: verifyCode
),
)
),
SizedBox(height: 15.0),
SizedBox(
height: 50,
width: 300,
child: Visibility(
maintainSize: true,
maintainAnimation: true,
maintainState: true,
visible: _visible,
child: DelayedAnimation(
delay: delayedAmount + 1500,
child: registerButton
),
)
),
SizedBox(height: 50.0,),
],
),
),
),
),
);
}

8. Create Login Page

We coded the all of requirements for login in the AuthManager class as above. Using the same design on the Register page and changing the button’s onPressed method, the Login page can be created easily. Since all codes are the same, I will not share the codes for this class again. As I mentioned, this is just a design example, you can change your login and registration pages to your application needs.

​