
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
- Basic knowledge of Getx State Management & Navigation Management.
- Basic knowledge of Flutter UI Design.
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',
)
],
);
}
}
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),
),
);
}
}
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,
))),
);
}
}
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())],
);
});
}
}
}
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())],
);
});
}
}
}
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';
}
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),
));
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:
Video Tutorial:
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.