Learn More

Try out the world’s first micro-repo!

Learn More

Flutter Getx Rest API Tutorial - Registration & Login

Flutter Getx Rest API Tutorial - Registration & Login

A step-by-step guide that will walk you through the process of easily implementing registration and login with Flutter and Getx - accompanied with a follow along youtube tutorial.

You can find the tutorial for this article here.

Prerequisites

Packages Used

Get Started

To begin, we must create a simple registration and login screen, as well as a home screen to redirect users after they successfully login or register. Then, using Getx Controllers and HTTP Request, we'll create two controllers to write our registration and login logic with.

The directory structure that you should have is as follows:

Let's now write the user interface code for our registration and login screens.

Auth Screen

// ignore_for_file: avoid_unnecessary_containers, prefer_const_constructors
import 'package:api_app/controllers/login_controller.dart';
import 'package:api_app/controllers/registeration_controller.dart';
import 'package:api_app/screens/auth/widgets/input_fields.dart';
import 'package:api_app/screens/auth/widgets/submit_button.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';

class AuthScreen extends StatefulWidget {
@override
State<AuthScreen> createState() => _AuthScreenState();
}

class _AuthScreenState extends State<AuthScreen> {
RegisterationController registerationController =
Get.put(RegisterationController());

LoginController loginController = Get.put(LoginController());
var isLogin = false.obs;
@override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(36),
child: Center(
child: Obx(
() => Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
height: 30,
),
Container(
child: Text(
'WELCOME',
style: TextStyle(
fontSize: 30,
color: Colors.black,
fontWeight: FontWeight.w400),
),
),
SizedBox(
height: 20,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
MaterialButton(
color: !isLogin.value ? Colors.white : Colors.amber,
onPressed: () {
isLogin.value = false;
},
child: Text('Register'),
),
MaterialButton(
color: isLogin.value ? Colors.white : Colors.amber,
onPressed: () {
isLogin.value = true;
},
child: Text('Login'),
),
],
),
SizedBox(
height: 80,
),
isLogin.value ? loginWidget() : registerWidget()
]),
),
),
),
),
);
}

Widget registerWidget() {
return Column(
children: [
InputTextFieldWidget(registerationController.nameController, 'name'),
SizedBox(
height: 20,
),
InputTextFieldWidget(
registerationController.emailController, 'email address'),
SizedBox(
height: 20,
),
InputTextFieldWidget(
registerationController.passwordController, 'password'),
SizedBox(
height: 20,
),
SubmitButton(
onPressed: () => registerationController.registerWithEmail(),
title: 'Register',
)
],
);
}

Widget loginWidget() {
return Column(
children: [
SizedBox(
height: 20,
),
InputTextFieldWidget(loginController.emailController, 'email address'),
SizedBox(
height: 20,
),
InputTextFieldWidget(loginController.passwordController, 'password'),
SizedBox(
height: 20,
),
SubmitButton(
onPressed: () => loginController.loginWithEmail(),
title: 'Login',
)
],
);
}
}

Save this code

Text Field Design

// ignore_for_file: prefer_const_constructors

import 'package:flutter/material.dart';

class InputTextFieldWidget extends StatelessWidget {
final TextEditingController textEditingController;
final String hintText;
InputTextFieldWidget(this.textEditingController, this.hintText);
@override
Widget build(BuildContext context) {
return Container(
height: 46,
child: TextField(
controller: textEditingController,
decoration: InputDecoration(
alignLabelWithHint: true,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.black)),
fillColor: Colors.white54,
hintText: hintText,
hintStyle: TextStyle(color: Colors.grey),
contentPadding: EdgeInsets.only(bottom: 15),
focusColor: Colors.white60),
),
);
}
}

Save this code

Submit Button Design

// ignore_for_file: prefer_const_constructors

import 'package:flutter/material.dart';

class SubmitButton extends StatelessWidget {
final VoidCallback onPressed;
final String title;
const SubmitButton({Key? key, required this.onPressed, required this.title})
: super(key: key);

@override
Widget build(BuildContext context) {
return Container(
width: 180,
height: 50,
decoration:
BoxDecoration(borderRadius: BorderRadius.circular(20), boxShadow: [
BoxShadow(
color: Colors.white.withOpacity(0.25),
offset: Offset(0, 0),
blurRadius: 2,
spreadRadius: 1)
]),
child: ElevatedButton(
style: ButtonStyle(
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: BorderSide.none)),
backgroundColor: MaterialStateProperty.all<Color>(
Colors.pinkAccent,
)),
onPressed: onPressed,
child: Text(title,
style: TextStyle(
fontSize: 24,
color: Colors.white,
fontWeight: FontWeight.w600,
))),
);
}
}

Save this code

We can now start writing the logic for our registration and login controllers.

Registration Controller

I've created three TextEditingControllers because my API requires these three fields to be passed into the body, but you can create controllers based on your needs.

If the status code is 200, it means that our request method was successful and that we can retrieve the response body. I've also checked for json['code']==0, which comes from the API and tells me whether or not the user is already registered. You can verify that condition based on your backend response. I've also saved the user token locally in shared preference to keep the user session alive.

import 'dart:convert';
import 'dart:io';

import 'package:api_app/screens/home.dart';
import 'package:api_app/utils/api_endpoints.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;

class RegisterationController extends GetxController {
TextEditingController nameController = TextEditingController();
TextEditingController emailController = TextEditingController();
TextEditingController passwordController = TextEditingController();

final Future<SharedPreferences> _prefs = SharedPreferences.getInstance();

Future<void> registerWithEmail() async {
try {
var headers = {'Content-Type': 'application/json'};
var url = Uri.parse(
ApiEndPoints.baseUrl + ApiEndPoints.authEndpoints.registerEmail);
Map body = {
'name': nameController.text,
'email': emailController.text.trim(),
'password': passwordController.text
};

http.Response response =
await http.post(url, body: jsonEncode(body), headers: headers);

if (response.statusCode == 200) {
final json = jsonDecode(response.body);
if (json['code'] == 0) {
var token = json['data']['Token'];
print(token);
final SharedPreferences? prefs = await _prefs;
await prefs?.setString('token', token);
nameController.clear();
emailController.clear();
passwordController.clear();
Get.offAll(HomeScreen());
} else {
throw jsonDecode(response.body)["Message"] ?? "Unknown Error Occured";
}
} else {
throw jsonDecode(response.body)["Message"] ?? "Unknown Error Occured";
}
} catch (e) {
Get.back();
showDialog(
context: Get.context!,
builder: (context) {
return SimpleDialog(
title: Text('Error'),
contentPadding: EdgeInsets.all(20),
children: [Text(e.toString())],
);
});
}
}
}

Save this code

Login Controller

import 'dart:convert';

import 'package:api_app/screens/home.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:api_app/utils/api_endpoints.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';

class LoginController extends GetxController {
TextEditingController emailController = TextEditingController();
TextEditingController passwordController = TextEditingController();
final Future<SharedPreferences> _prefs = SharedPreferences.getInstance();

Future<void> loginWithEmail() async {
var headers = {'Content-Type': 'application/json'};
try {
var url = Uri.parse(
ApiEndPoints.baseUrl + ApiEndPoints.authEndpoints.loginEmail);
Map body = {
'email': emailController.text.trim(),
'password': passwordController.text
};
http.Response response =
await http.post(url, body: jsonEncode(body), headers: headers);

if (response.statusCode == 200) {
final json = jsonDecode(response.body);
if (json['code'] == 0) {
var token = json['data']['Token'];
final SharedPreferences? prefs = await _prefs;
await prefs?.setString('token', token);

emailController.clear();
passwordController.clear();
Get.offAll(HomeScreen());
} else if (json['code'] == 1) {
throw jsonDecode(response.body)['message'];
}
} else {
throw jsonDecode(response.body)["Message"] ?? "Unknown Error Occured";
}
} catch (error) {
Get.back();
showDialog(
context: Get.context!,
builder: (context) {
return SimpleDialog(
title: Text('Error'),
contentPadding: EdgeInsets.all(20),
children: [Text(error.toString())],
);
});
}
}
}

Save this code

API Endpoints

class ApiEndPoints {
static final String baseUrl = 'http://base_url/api/';
static _AuthEndPoints authEndpoints = _AuthEndPoints();
}

class _AuthEndPoints {
final String registerEmail = 'authaccount/registration';
final String loginEmail = 'authaccount/login';
}

Save this code

Replace the API base URL and endpoints with your own. To log out, simply clear our shared preferences and redirect the user to AuthScreen.

 TextButton(
onPressed: () async {
final SharedPreferences? prefs = await _prefs;
prefs?.clear();
Get.offAll(AuthScreen());
},
child: Text(
'logout',
style: TextStyle(color: Colors.white),
));

Save this code

Conclusion

In this article, you learned how to use Flutter and Getx to implement Authentication (Register & Login). We started by creating a UI screen for our Registration & Login, and then we wrote the logic to register users via API using Getx Controllers and maintain their session by storing tokens in shared preferences.

Full Source Code:

https://bit.ly/3Dcq9FA

Video Tutorial:

https://bit.ly/3VM30Rw

Bonus Tip

If you want to be more productive and grab code easily from any website, Try Pieces. It allows you to save snippets in one click from any website or your own editor, automatically auto saves your frequently used code, recommends code snippets that align with framework standards, instantly save code snippets from screenshots, and much more.

Interested in becoming a Pieces Content Partner?

Learn More

Get our latest blog posts and product updates by signing up for our monthly newsletter! 

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Table of Contents

Flutter

GetX

Tutorial

More from Pieces