How to Build an OTP Verification Screen in Flutter with GetX: Complete Guide with Beautified UI
Creating an OTP (One-Time Password) verification screen is a crucial part of many apps for security purposes. In this post, we’ll guide you through setting up a modern OTP verification screen in Flutter using GetX for state management. We'll also provide a beautiful UI that resembles the design shared.
Step 1: Add Dependencies
Add the necessary dependencies in your pubspec.yaml
file:
dependencies: flutter: sdk: flutter get: ^4.6.5 pin_code_fields: ^7.4.0
Run flutter pub get
to fetch the packages.
Step 2: Create the OTP Controller with GetX
We’ll create a controller that manages the timer, OTP input, and validation.
otp_controller.dart
import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'dart:async'; class OTPController extends GetxController { var isCodeExpired = false.obs; var otp = ''.obs; var remainingTime = 60.obs; // 1 minute in seconds Timer? timer; TextEditingController otpController = TextEditingController(); // Create a TextEditingController @override void onInit() { startTimer(); super.onInit(); } void startTimer() { timer = Timer.periodic(const Duration(seconds: 1), (timer) { if (remainingTime.value > 0) { remainingTime.value--; } else { isCodeExpired.value = true; timer.cancel(); } }); } void resendCode() { remainingTime.value = 60; // Reset timer to 60 seconds isCodeExpired.value = false; otp.value = ''; // Clear the OTP field in the controller otpController.clear(); // Clear the text fields in PinCodeTextField startTimer(); // Restart the timer update(); // Notify UI to update } void onOTPComplete(String value) { otp.value = value; } @override void onClose() { timer?.cancel(); otpController.dispose(); // Dispose of the controller super.onClose(); } }
Step 3: Create the Beautified OTP Verification UI
We'll design the UI similar to the one you shared with a countdown timer, input fields for the OTP, and a "Resend Code" option.
otp_view.dart
import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:get/get.dart'; import 'package:getx_tutorials/widgets/custom_button/custom_button.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:pin_code_fields/pin_code_fields.dart'; import 'package:sizer/sizer.dart'; import '../widgets/app_color/app_color.dart'; import '../widgets/app_image/app_image.dart'; import 'otp_controller.dart'; class OtpVerificationScreen extends GetView { @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, body: Padding( padding: const EdgeInsets.all(16.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ SvgPicture.asset( AppImages.scanIcon, height: 65.0, // Adjust the size of the icon if necessary width: 65.0, ), // Add your logo here const SizedBox(height: 20), Text( 'OTP Verification', style: GoogleFonts.poppins( fontSize: 24, fontWeight: FontWeight.bold), ), const SizedBox(height: 10), Text( 'We have sent an OTP on given number +91 2224 555 333', textAlign: TextAlign.center, style: GoogleFonts.poppins(fontSize: 16, color: Colors.grey), ), const SizedBox(height: 20), Obx(() { return Text( controller.isCodeExpired.value ? "Your otp has expired please resend" : "00:${controller.remainingTime.value.toString().padLeft(2, '0')}", style: GoogleFonts.poppins( color: controller.isCodeExpired.value ? AppColors.appColor : AppColors.appColor, fontSize: 18, ), ); }), SizedBox(height: 4.h), PinCodeTextField( appContext: context, length: 4, onChanged: (value) { controller.update(); }, onCompleted: controller.onOTPComplete, controller: controller.otpController, autoDismissKeyboard: true, enablePinAutofill: true, pinTheme: PinTheme( shape: PinCodeFieldShape.box, borderRadius: BorderRadius.circular(15), fieldHeight: 75, fieldWidth: 75, activeColor: AppColors.appColor, activeFillColor: AppColors.appbarColor, selectedColor: AppColors.appColor, selectedFillColor: AppColors.btnColor, inactiveColor: Colors.grey, ), ), const SizedBox(height: 20), const SizedBox(height: 20), Obx( () => CustomButton( onPressed: controller.resendCode, backgroundColor: controller.otp.value.length == 4 ? AppColors.appColor : controller.isCodeExpired.value ? AppColors.appColor : AppColors.btnColor, text: controller.isCodeExpired.value ? "Send Code Again" : 'Next', image: AppImages.rightArrow, imageColor: controller.isCodeExpired.value ? AppColors.white : controller.otp.value.length == 4 ? AppColors.whiteColor : AppColors.optionIconColor, color: controller.isCodeExpired.value ? AppColors.white : controller.otp.value.length == 4 ? AppColors.whiteColor : AppColors.optionIconColor, ), ) ], ), ), ); } }
Step 4: Binding the Controller
Bind the OTPController
to the view using GetX bindings.
otp_binding.dart
import 'package:get/get.dart'; import 'otp_controller.dart'; class OtpVerificationBindings extends Bindings { @override void dependencies() { Get.lazyPut(() => OTPController()); } }
Step 5: Main Application Setup
In your main.dart
, configure the routes and bindings using GetX.
main.dart
import 'package:flutter/material.dart'; import 'package:get/get_navigation/src/root/get_material_app.dart'; import 'package:getx_tutorials/widgets/app_color/app_color.dart'; import 'package:getx_tutorials/widgets/app_routes/app_routes.dart'; import 'package:responsive_framework/responsive_wrapper.dart'; import 'package:responsive_framework/utils/scroll_behavior.dart'; import 'package:sizer/sizer.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); // This widget is the root of your application. @override Widget build(BuildContext context) { Map color = { 50: const Color(0xFF7DEF83), 100: const Color(0xFF7DEF83), 200: const Color(0xFF7DEF83), 300: const Color(0xFF7DEF83), 400: const Color(0xFF7DEF83), 500: const Color(0xFF7DEF83), 600: const Color(0xFF7DEF83), 700: const Color(0xFF7DEF83), 800: const Color(0xFF7DEF83), 900: const Color(0xFF7DEF83), }; return Sizer( builder: (context, orientation, deviceType) { return GetMaterialApp( debugShowCheckedModeBanner: false, title: 'EV Assert', theme: ThemeData( primarySwatch: MaterialColor(0xFFA4573B, color), splashColor: AppColors.appColor, highlightColor: AppColors.appColor, ), // home: OnboardingScreen(), builder: (context, child) => ResponsiveWrapper.builder( BouncingScrollWrapper.builder(context, child!), maxWidth: 1400, minWidth: 450, defaultScale: true, breakpoints: [ const ResponsiveBreakpoint.resize(450, name: MOBILE), const ResponsiveBreakpoint.autoScale(800, name: TABLET), const ResponsiveBreakpoint.autoScale(1000, name: TABLET), const ResponsiveBreakpoint.resize(1200, name: DESKTOP), const ResponsiveBreakpoint.autoScale(2460, name: "4K"), ], background: Container(color: const Color(0xFFF5F5F5))), // initialBinding: BindingsBuilder(() { // printAction("Data coming here"); // }), initialRoute: AppRoutes.otpScreen, getPages: AppRoutes.pages, ); }, ); } }
This complete example demonstrates how to implement a functional and beautiful OTP verification screen in Flutter using GetX. The interface includes features like a countdown timer, OTP input field, and the ability to resend the code if it expires.
By following this guide, you can easily integrate a professional OTP verification system into your Flutter app with a modern and clean UI.
0 Comments
Please Do Not Comments Any Span Or Span link..