how to implement email verification after new account creation in flutter app using sql database?

56 views Asked by At

I have created a flutter app which uses sql database. I have created a signup page, I want that when the user creates a new account, an email should be sent to the entered email id for verification and only after the verification the user should be able to login.

Dart Code

import 'dart:convert';
import 'dart:io';
import 'package:flutter/scheduler.dart';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:law_diary/login/login.dart';
import 'package:mailer/mailer.dart';
import 'package:mailer/smtp_server/gmail.dart';
import '../widgets/widgets.dart';

class SignUpPage extends StatefulWidget {
  @override
  _SignUpPageState createState() => _SignUpPageState();
}

class _SignUpPageState extends State<SignUpPage> {
  TextEditingController _nameController = TextEditingController();
  TextEditingController _phoneController = TextEditingController();
  TextEditingController _emailController = TextEditingController();
  TextEditingController _usernameController = TextEditingController();
  TextEditingController _passwordController = TextEditingController();
  TextEditingController _confirmPasswordController = TextEditingController();

  bool _showPassword = false;

  Future _signup(BuildContext cont) async {
    final String name = _nameController.text.trim();
    final String phone = _phoneController.text.trim();
    final String email = _emailController.text.trim();
    final String username = _usernameController.text.trim();
    final String password = _passwordController.text.trim();
    final String confirmPassword = _confirmPasswordController.text.trim();

    if (name.isEmpty ||
        phone.isEmpty ||
        email.isEmpty ||
        username.isEmpty ||
        password.isEmpty ||
        confirmPassword.isEmpty) {
      ToastUtil.showToast("All the fields are required!");
    } else {
      var url =
          "https://legaldiary.000webhostapp.com/android/signup/checkUser.php";
      var response = await http.post(Uri.parse(url), body: {
        "name": name,
        "phone": phone,
        "email": email,
        "username": username,
        "password": password,
        "confirmPassword": confirmPassword,
      });

      print("Response from server: ${response.body}");

      if (response.statusCode == 200) {
        try {
          var trimmedResponse = response.body.trim();
          print(trimmedResponse);
          var data = json.decode(trimmedResponse);

          if (data["status"] == "success") {
            ToastUtil.showToast(
                "Account Creation Successful. Check your email for verification!");
            await sendEmail();
            // SchedulerBinding.instance.addPostFrameCallback((_) {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (_) => LoginPage(),
              ),
            );
            // });
          } else if (data["status"] == "error") {
            ToastUtil.showToast(data["message"]);
          }
        } catch (e) {
          print("Error parsing JSON: $e");
          ToastUtil.showToast(
              "An error occurred while processing the response.");
        }
      } else {
        print("HTTP Error: ${response.statusCode}");
        ToastUtil.showToast("An HTTP error occurred. Please try again later.");
      }
    }
  }

  Future<void> sendEmail() async {
    // Replace these values with your actual email and SMTP server details
    final String username = '[email protected]';
    final String password = 'Yashi@71102';
    final String recipient = _emailController.text.trim();

    final smtpServer = gmail(username, password);

    final message = Message()
      ..from = Address(username, 'Your Name')
      ..recipients.add(recipient)
      ..subject = 'Test Dart Mailer ${DateTime.now()}'
      ..text = 'This is a test email sent from Dart.';

    try {
      final sendReport = await send(message, smtpServer);
      print('Message sent: ' + sendReport.toString());
    } on MailerException catch (e) {
      print('Message not sent. \n' + e.toString());
      for (var p in e.problems) {
        print('Problem: ${p.code}: ${p.msg}');
      }
    } on SocketException catch (e) {
      print('Message not sent. \n' + e.toString());
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: CustomAppBar(),
      backgroundColor: Colors.grey[200], // Greyish background
      body: Stack(
        children: <Widget>[
          Container(
            decoration: BoxDecoration(
              gradient: LinearGradient(
                colors: [Color(0xFF333333), Color(0xFF666666)],
                begin: Alignment.topLeft,
                end: Alignment.bottomRight,
              ),
            ),
          ),
          Center(
            child: SingleChildScrollView(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Text(
                    'Join us now!',
                    style: TextStyle(
                      color: Colors.white,
                      fontSize: 28,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  SizedBox(height: 20),
                  Padding(
                    padding: EdgeInsets.symmetric(horizontal: 40),
                    child: TextFormField(
                      controller: _nameController,
                      style: TextStyle(color: Colors.white),
                      decoration: InputDecoration(
                        hintText: 'Name',
                        hintStyle: TextStyle(color: Colors.white70),
                        filled: true,
                        fillColor: Colors.white.withOpacity(0.1),
                        border: OutlineInputBorder(
                          borderRadius: BorderRadius.circular(10),
                          borderSide: BorderSide.none,
                        ),
                        prefixIcon: Icon(
                          Icons.person,
                          color: Colors.white,
                        ),
                      ),
                    ),
                  ),
                  SizedBox(height: 20),
                  Padding(
                    padding: EdgeInsets.symmetric(horizontal: 40),
                    child: TextFormField(
                      controller: _phoneController,
                      style: TextStyle(color: Colors.white),
                      decoration: InputDecoration(
                        hintText: 'Phone Number',
                        hintStyle: TextStyle(color: Colors.white70),
                        filled: true,
                        fillColor: Colors.white.withOpacity(0.1),
                        border: OutlineInputBorder(
                          borderRadius: BorderRadius.circular(10),
                          borderSide: BorderSide.none,
                        ),
                        prefixIcon: Icon(
                          Icons.phone,
                          color: Colors.white,
                        ),
                      ),
                    ),
                  ),
                  SizedBox(height: 20),
                  Padding(
                    padding: EdgeInsets.symmetric(horizontal: 40),
                    child: TextFormField(
                      controller: _emailController,
                      style: TextStyle(color: Colors.white),
                      decoration: InputDecoration(
                        hintText: 'Email',
                        hintStyle: TextStyle(color: Colors.white70),
                        filled: true,
                        fillColor: Colors.white.withOpacity(0.1),
                        border: OutlineInputBorder(
                          borderRadius: BorderRadius.circular(10),
                          borderSide: BorderSide.none,
                        ),
                        prefixIcon: Icon(
                          Icons.mail_outline,
                          color: Colors.white,
                        ),
                      ),
                    ),
                  ),
                  SizedBox(height: 20),
                  Padding(
                    padding: EdgeInsets.symmetric(horizontal: 40),
                    child: TextFormField(
                      controller: _usernameController,
                      style: TextStyle(color: Colors.white),
                      decoration: InputDecoration(
                        hintText: 'Username',
                        hintStyle: TextStyle(color: Colors.white70),
                        filled: true,
                        fillColor: Colors.white.withOpacity(0.1),
                        border: OutlineInputBorder(
                          borderRadius: BorderRadius.circular(10),
                          borderSide: BorderSide.none,
                        ),
                        prefixIcon: Icon(
                          Icons.account_box,
                          color: Colors.white,
                        ),
                      ),
                    ),
                  ),
                  SizedBox(height: 20),
                  Padding(
                    padding: EdgeInsets.symmetric(horizontal: 40),
                    child: TextFormField(
                      controller: _passwordController,
                      style: TextStyle(color: Colors.white),
                      obscureText: !_showPassword,
                      decoration: InputDecoration(
                        hintText: 'Password',
                        hintStyle: TextStyle(color: Colors.white70),
                        filled: true,
                        fillColor: Colors.white.withOpacity(0.1),
                        border: OutlineInputBorder(
                          borderRadius: BorderRadius.circular(10),
                          borderSide: BorderSide.none,
                        ),
                        prefixIcon: Icon(
                          Icons.lock_outline,
                          color: Colors.white,
                        ),
                        suffixIcon: IconButton(
                          icon: Icon(
                            _showPassword
                                ? Icons.visibility
                                : Icons.visibility_off,
                            color: Colors.white,
                          ),
                          onPressed: () {
                            setState(() {
                              _showPassword = !_showPassword;
                            });
                          },
                        ),
                      ),
                    ),
                  ),
                  SizedBox(height: 20),
                  Padding(
                    padding: EdgeInsets.symmetric(horizontal: 40),
                    child: TextFormField(
                      controller: _confirmPasswordController,
                      style: TextStyle(color: Colors.white),
                      obscureText: !_showPassword,
                      decoration: InputDecoration(
                        hintText: 'Confirm Password',
                        hintStyle: TextStyle(color: Colors.white70),
                        filled: true,
                        fillColor: Colors.white.withOpacity(0.1),
                        border: OutlineInputBorder(
                          borderRadius: BorderRadius.circular(10),
                          borderSide: BorderSide.none,
                        ),
                        prefixIcon: Icon(
                          Icons.lock_outline,
                          color: Colors.white,
                        ),
                        suffixIcon: IconButton(
                          icon: Icon(
                            _showPassword
                                ? Icons.visibility
                                : Icons.visibility_off,
                            color: Colors.white,
                          ),
                          onPressed: () {
                            setState(() {
                              _showPassword = !_showPassword;
                            });
                          },
                        ),
                      ),
                    ),
                  ),
                  SizedBox(height: 20),
                  ElevatedButton(
                    onPressed: () {
                      // TODO: Implement sign-up functionality
                      _signup(context);
                    },
                    style: ElevatedButton.styleFrom(
                      primary: Colors.white,
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(10),
                      ),
                    ),
                    child: Padding(
                      padding: const EdgeInsets.all(10.0),
                      child: Text(
                        'Sign Up',
                        style: TextStyle(
                          color: Colors.black87,
                          fontSize: 18,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                    ),
                  ),
                  SizedBox(height: 20),
                  GestureDetector(
                    onTap: () {
                      // Navigate back to the login page
                      SchedulerBinding.instance.addPostFrameCallback((_) {
                        Navigator.pop(context);
                      });
                    },
                    child: Text(
                      'Already have an account? Login',
                      style: TextStyle(
                        color: Colors.white,
                        fontSize: 16,
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

PHP file

<?php
header('Content-Type: application/json');

session_start();
include '../connection.php';

function checkPasswordStrength($password) {
    if (preg_match('/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/', $password)) {
        return 'very_strong';
    } elseif (preg_match('/^(?=.*[a-z])(?=.*[A-Z]).{8,}$/', $password)) {
        return 'strong';
    } elseif (strlen($password) >= 8) {
        return 'weak';
    } else {
        return 'very_weak';
    }
}

function generateVerificationToken() {
    return bin2hex(random_bytes(32));  // Use a secure method to generate a random token
}


if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (isset($_POST['name']) && isset($_POST['phone']) && isset($_POST['email']) && isset($_POST['username']) 
      && isset($_POST['password']) && isset($_POST['confirmPassword'])) {
        // Get data from the POST request
        $name = $_POST['name'];
        $phone = $_POST['phone'];
        $email = $_POST['email'];
        $username = $_POST['username'];
        $password = $_POST['password'];
        $confirmPassword = $_POST['confirmPassword'];


        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            echo json_encode(array("status" => "error", "message" => "Invalid email format."));
            die();
            // exit;
        }

        if (!preg_match('/^\d{10}$/', $phone)) {
            echo json_encode(array("status" => "error", "message" => "Invalid phone number format. It should have exactly 10 digits."));
            die(); // Terminate script execution
        }


        $conn = connectToDatabase();

        // Check if email or phone number already exists
        $checkEmailQuery = "SELECT * FROM userdetails WHERE email = '$email'";
        $checkPhoneQuery = "SELECT * FROM userdetails WHERE phn = '$phone'";

        $resultEmail = executeQuery($conn, $checkEmailQuery);
        $resultPhone = executeQuery($conn, $checkPhoneQuery);

        if (mysqli_num_rows($resultEmail) > 0) {
            // Email is already used
            echo json_encode(array("status" => "error", "message" => "Email is already registered."));
        } 
        elseif (mysqli_num_rows($resultPhone) > 0) {
            // Phone number is already used
            echo json_encode(array("status" => "error", "message" => "Phone number is already registered."));
        } 
        else {
            if($password != $confirmPassword) {
                echo json_encode(array("status" => "error", "message" => "Both the passwords do not match."));
                die();
            }

            $passStrength = checkPasswordStrength($password);
            // Check if the password is weak or very weak
            if ($passStrength === 'weak' || $passStrength === 'very_weak') {
                echo json_encode(array("status" => "error", "message" => "Password is too weak. Choose a stronger password.
                \nThe password should contain at least 1 capital letter, 1 small letter, 1 digit and 1 special character.
                \nThe minimum length of the password should be 8.
                "));
                die();
            }

            // Check if username already exists
            $checkUsernameQuery = "SELECT * FROM userdetails WHERE username = '$username'";
            $resultUsername = executeQuery($conn, $checkUsernameQuery);

            if (mysqli_num_rows($resultUsername) > 0) {
                // Username is already taken
                echo json_encode(array("status" => "error", "message" => "Username is already taken."));
            } else {
                // Insert the new user record
                $verificationToken = generateVerificationToken();
                $insertQuery = "INSERT INTO userdetails (name, phn, username, password, email, token) VALUES ('$name', '$phone', '$username', '$password', '$email', '$verificationToken')";

                if (mysqli_query($conn, $insertQuery)) {
                    $userId = mysqli_insert_id($conn);

                    // Store the token in the database
                    // $updateTokenQuery = "UPDATE userdetails SET token = '$verificationToken' WHERE email = '$email'";
                    // mysqli_query($conn, $updateTokenQuery);

                    // Send a verification email
                    // $subject = "Email Verification";
                    // $message = "Click the link to verify your email: http://legaldiary.000webhostapp.com/verify.php?id='.$userId.'&token='.$verificationToken";
                    // $headers = "From: [email protected]";  // Change this to your email

                    // // Use a proper mail sending function (e.g., mail())
                    // mail($email, $subject, $message, $headers);

                    // User registered successfully
                    echo json_encode(array("status" => "success", "message" => "Registration successful. Check your email for verification."));
                } else {
                    // Error in registration
                    echo json_encode(array("status" => "error", "message" => "Error in registration."));
                }
            }
        }
    }

    else {
        // Return error if username or email and password are not set
        echo json_encode(["error" => "All the fields are required."]);
    }
} 

else {
    // Return error if the request method is not POST
    echo json_encode(["error" => "Invalid request method. Use POST."]);
}



// Close the database connection
mysqli_close($conn);

Please help me with this. I have provided the code that I tried.

1

There are 1 answers

3
Muhammad Ali On

i do this in php and fluttter by adding column for the user named otp_code and another one called user_status after the user send data to signup you can generate otp code and save in the user rows then send email for the user using this function in backend without using function in flutter

function sendEmail($to, $title, $body)
{
$header = "From:[email protected] " . "\n" . "CC:";
mail($to, $title, $body, $header);
}

and send the value of otp to user mail you recived in post method in flutter make the user go to new route with input otp field when the user write the code they recived in email resend request to handle the user if otp correct or not then change the value of status from 0 to 1