Login/Signin page in Flutter

Welcome to this interactive guide on creating a login page in Flutter. In this tutorial, we will be creating a simple login page with email and password fields and a login button. By the end of this tutorial, you will have a basic understanding of how to create a login page in Flutter.

Before we begin, make sure you have Flutter installed on your computer and that you have set up an Android or iOS development environment.

Create the login page layout

Open any previous project or create a new project, after that create a StatefulWidget as “LoginPage” name. I will be using the Scaffold widget as the root of our login page. The Scaffold widget provides a basic layout structure.

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: LoginPage(),
    );
  }
}


class LoginPage extends StatefulWidget {
  const LoginPage({Key? key}) : super(key: key);

  @override
  State<LoginPage> createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

 

now to enable scrolling of the login page when the keyboard is open and avoiding bottom overflow use SingleChildScrollView widget give an interactive title using Text widget I use simple “Login Page” text and wrap it with Container.

Scaffold(
  body: SingleChildScrollView(
    child: Container(
      padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 40),
      child: Column(
        children: [
        const SizedBox(height: 50),
        const Align(
          alignment: Alignment.topLeft,
          child: Text(
            'Login Page',
            style: TextStyle(fontSize: 32, fontWeight: FontWeight.w500),
          ),
        ),
      ]),
    ),
  ),
);

Add any other things you want to show, I am using Image to look nice, after that create 2 TextFormField widget inside column and wrap that with Form. give controller to each TextFormField , and to look nice add some decoration to that, also add some variables which we will use later.

final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
final _formKey = GlobalKey<FormState>();
bool showPassword = false;
bool isLoading = false;

Here’s the code.

Form(
  key: _formKey,
  child: Column(children: [
    const SizedBox(height: 50),
    TextFormField(
      controller: _emailController,
      style: const TextStyle(color: Colors.white),
      decoration: InputDecoration(
          filled: true,
          hintText: 'Enter your email',
          hintStyle: const TextStyle(color: Colors.white),
          prefixIcon: const Icon(
            Icons.person,
            color: Colors.grey,
          ),
          border: OutlineInputBorder(
            borderRadius: BorderRadius.circular(36.0),
            borderSide: BorderSide.none,
          ),
          fillColor: Colors.grey.shade900,
          focusColor: Colors.white),
    ),
    const SizedBox(height: 25),
    TextFormField(
      controller: _passwordController,
      obscureText: showPassword ? false : true,
      style: const TextStyle(color: Colors.white),
      decoration: InputDecoration(
        filled: true,
        hintText: 'Enter your password',
        hintStyle: const TextStyle(color: Colors.white),
        prefixIcon: const Icon(
          Icons.security,
          color: Colors.grey,
        ),
        suffixIcon: InkWell(
            onTap: () {
              setState(() {
                showPassword = !showPassword;
              });
            },
            child: Icon(
                showPassword
                    ? Icons.visibility_off
                    : Icons.remove_red_eye,
                color: Colors.white)),
        border: OutlineInputBorder(
          borderRadius: BorderRadius.circular(36.0),
          borderSide: BorderSide.none,
        ),
        fillColor: Colors.grey.shade900,
        focusColor: Colors.white
      ),
    ),
  ]),
)

to validate the user input at front-end add validation before that add these lines lines inside LoginPage.

 // Email Validation
final emailPattern =
    r'^(([^<>()[\]\\.,;:\[email protected]\"]+(\.[^<>()[\]\\.,;:\[email protected]\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';

bool validateEmail(String email) {
  final regExp = RegExp(emailPattern);
  return regExp.hasMatch(email);
}
// Validation for email
validator: (value) {
  if (value!.isEmpty) {
    return 'Please Enter Your email';
  }
  if (!validateEmail(_emailController.text)) {
    return 'Please Enter Valid Email';
  }
  return null;
},
// Validation for password
validator: (value) {
  if (value!.isEmpty) {
    return 'Please Enter Your Password';
  }
  if (value.length < 6) {
    return 'Password must be at least 6 characters';
  }
  return null;
},

Finally, create a login button, for that, I will use Container, and wrap it with InkWell to use onTap functionality. create a login function, which will be called on pressed, login function is called when the form is submitted and it checks if the form is valid using _formKey.currentState!.validate(). If it is valid, it sets the isLoading variable to true, which triggers the loading animation to be shown. After a 2-second delay, the isLoading variable is set to false, and user will route to the next page. Here’s the code for the function and button.

void login() {
   if (_formKey.currentState!.validate()) {
     isLoading = true;
     setState(() {});
     Future.delayed(const Duration(seconds: 2), () {
       isLoading = false;
       setState(() {});
       Navigator.push(
         context,
         MaterialPageRoute(builder: (context) => const HomePage()),
       );
     });
   }
 }
InkWell(
  onTap: () {
    login();
  },
  child: Container(
    height: 50,
    decoration: BoxDecoration(
      color: Colors.grey.shade900,
      borderRadius: BorderRadius.circular(36),
    ),
    child: Center(
      child: isLoading
          ? const Padding(
              padding: EdgeInsets.all(8.0),
              child: CircularProgressIndicator(
                color: Colors.white,
              ),
            )
          : const Text(
              'Login',
              style: TextStyle(
                  color: Colors.white,
                  fontSize: 18,
                  fontWeight: FontWeight.w500),
            ),
    ),
  ),
),

Final Code

to run this just copy this and paste in main.dart or just use LoginPage Widget.

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: LoginPage(),
    );
  }
}

class LoginPage extends StatefulWidget {
  const LoginPage({Key? key}) : super(key: key);

  @override
  State<LoginPage> createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  final TextEditingController _emailController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();
  final _formKey = GlobalKey<FormState>();
  bool showPassword = false;
  bool isLoading = false;

  // Email Validation
  final emailPattern =
      r'^(([^<>()[\]\\.,;:\[email protected]\"]+(\.[^<>()[\]\\.,;:\[email protected]\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';

  bool validateEmail(String email) {
    final regExp = RegExp(emailPattern);
    return regExp.hasMatch(email);
  }

  // Sign In Function
  void login() {
    if (_formKey.currentState!.validate()) {
      isLoading = true;
      setState(() {});
      Future.delayed(const Duration(seconds: 2), () {
        isLoading = false;
        setState(() {});
        Navigator.push(
          context,
          MaterialPageRoute(builder: (context) => const HomePage()),
        );
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SingleChildScrollView(
        child: Container(
          padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 40),
          child: Column(
            children: [
              const SizedBox(height: 50),
              const Align(
                alignment: Alignment.topLeft,
                child: Text(
                  'Login Page',
                  style: TextStyle(fontSize: 32, fontWeight: FontWeight.w500),
                ),
              ),
              const SizedBox(height: 50),
              Image.asset(
                "assets/images/flutter.png",
                height: 200,
              ),
              Form(
                key: _formKey,
                child: Column(
                  children: [
                    const SizedBox(height: 50),
                    TextFormField(
                      controller: _emailController,
                      validator: (value) {
                        if (value!.isEmpty) {
                          return 'Please Enter Your email';
                        }
                        if (!validateEmail(_emailController.text)) {
                          return 'Please Enter Valid Email';
                        }
                        return null;
                      },
                      style: const TextStyle(color: Colors.white),
                      decoration: InputDecoration(
                          filled: true,
                          hintText: 'Enter your email',
                          hintStyle: const TextStyle(color: Colors.white),
                          prefixIcon: const Icon(
                            Icons.person,
                            color: Colors.grey,
                          ),
                          border: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(36.0),
                            borderSide: BorderSide.none,
                          ),
                          fillColor: Colors.grey.shade900,
                          focusColor: Colors.white),
                    ),
                    const SizedBox(height: 25),
                    TextFormField(
                      controller: _passwordController,
                      obscureText: showPassword ? false : true,
                      validator: (value) {
                        if (value!.isEmpty) {
                          return 'Please Enter Your Password';
                        }
                        if (value.length < 6) {
                          return 'Password must be at least 6 characters';
                        }
                        return null;
                      },
                      style: const TextStyle(color: Colors.white),
                      decoration: InputDecoration(
                        filled: true,
                        hintText: 'Enter your password',
                        hintStyle: const TextStyle(color: Colors.white),
                        prefixIcon: const Icon(
                          Icons.security,
                          color: Colors.grey,
                        ),
                        suffixIcon: InkWell(
                          onTap: () {
                            setState(() {
                              showPassword = !showPassword;
                            });
                          },
                          child: Icon(
                            showPassword
                                ? Icons.visibility_off
                                : Icons.remove_red_eye,
                            color: Colors.white,
                          ),
                        ),
                        border: OutlineInputBorder(
                          borderRadius: BorderRadius.circular(36.0),
                          borderSide: BorderSide.none,
                        ),
                        fillColor: Colors.grey.shade900,
                        focusColor: Colors.white,
                      ),
                    ),
                    const SizedBox(height: 50),
                    InkWell(
                      onTap: () {
                        login();
                      },
                      child: Container(
                        height: 50,
                        decoration: BoxDecoration(
                          color: Colors.grey.shade900,
                          borderRadius: BorderRadius.circular(36),
                        ),
                        child: Center(
                          child: isLoading
                              ? const Padding(
                                  padding: EdgeInsets.all(8.0),
                                  child: CircularProgressIndicator(
                                    color: Colors.white,
                                  ),
                                )
                              : const Text(
                                  'Login',
                                  style: TextStyle(
                                    color: Colors.white,
                                    fontSize: 18,
                                    fontWeight: FontWeight.w500,
                                  ),
                                ),
                        ),
                      ),
                    ),
                  ],
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(
        child: Text(
          'Home Page',
          style: TextStyle(fontSize: 32, fontWeight: FontWeight.w500),
        ),
      ),
    );
  }
}

The preview is given below.

I hope you will find it helpful, In case of any issue or question feel free to comment.

Leave a Reply

Your email address will not be published. Required fields are marked *