QML User Login App with SQLite backend

QML User Login App with SQLite backend

Qt Quick provides an option for reading and writing SQLite databases using the module “QtQuick.LocalStorage”.  These databases are user-specific and QML-specific, but accessible to all QML applications. They are stored in the Databases subdirectory of QQmlEngine::offlineStoragePath(), currently as SQLite databases. Database connections are automatically closed during Javascript garbage collection.

In this blog post I am going to demonstrate how we can write a simple user based login application using QML which uses LocalStorage to store the user credentials. This demo application has following features.

  • New User Registration
  • User Login
  • Authentication and error handling
  • Password retrieval

This application is spitted into multiple screens and uses StackView show the screens. Following are the list of screens which we will be using in this demo application.

  • LogInPage
  • RegisterScreen
  • PasswordResetPage
  • UserInfoPage

The icons which you can see in this application are created by using “Font Awesome” font. I have used the FontLoader from QML for this purpose. And this font object is used for all the Text items to show their respective icons using Unicode strings.

FontLoader {
        id: fontAwesome
        name: "fontawesome"
        source: "qrc:/fontawesome-webfont.ttf"
    }

In this application I have used a PopUp item to show all the warnings and error messages of this application. This popup is placed at the bottom of the screen. I have also introduced a Timer along this popup to close it automatically after 2 seconds.

    //Popup to show messages or warnings on the bottom position of the screen
    Popup {
        id: popup
        property alias popMessage: message.text
        background: Rectangle {
            implicitWidth: rootWindow.width
            implicitHeight: 60
            color: "#b44"
        }
        y: rootWindow.height
        modal: true
        focus: true
        closePolicy: Popup.CloseOnPressOutside

        Text {
            id: message
            anchors.centerIn: parent
            font.pointSize: 12
            color: "#ffffff"
        }
        onOpened: popupClose.start()
    }

    // Popup will be closed automatically in 2 seconds after its opened
    Timer {
        id: popupClose
        interval: 1000
        onTriggered: popup.close()
    }

I have used a JavScript file “backend.js” to validate the user credentials for login and registration.

In my “main.qml” file I have used a StackView as a container and it loads “LogInPage.qml” as the initial page. And here I also initializes the Database and create a table to store user details. Instance of this database will be used across the application to store/retrieve user data.

Following are the main operation which I am performing on the database.

  • Create database and create a new table for user credentials
  • Insert user credentials in database
  • Retrieve password for the given user for authentication
  • Retrieve password for the given user and hint for password retrieval

Below is my application’s main.qml file.

main.qml:

import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.1
import QtQuick.LocalStorage 2.0

import "backend.js" as Backend

ApplicationWindow {
    id: rootWindow
    visible: true
    width: 420
    height: 680
    title: qsTr("Login Demo")
    flags: Qt.FramelessWindowHint

    property color backGroundColor : "#394454"
    property color mainAppColor: "#6fda9c" //"#FA6B65"
    property color mainTextCOlor: "#f0f0f0"
    property var dataBase

    FontLoader {
        id: fontAwesome
        name: "fontawesome"
        source: "qrc:/fontawesome-webfont.ttf"
    }

    // Main stackview
    StackView{
        id: stackView
        focus: true
        anchors.fill: parent
    }

    // After loading show initial Login Page
    Component.onCompleted: {
        stackView.push("qrc:/LogInPage.qml")   //initial page
        dataBase = userDataBase()
        console.log(dataBase.version)
    }

    //Popup to show messages or warnings on the bottom postion of the screen
    Popup {
        id: popup
        property alias popMessage: message.text
        background: Rectangle {
            implicitWidth: rootWindow.width
            implicitHeight: 60
            color: "#b44"
        }

        y: rootWindow.height
        modal: true
        focus: true
        closePolicy: Popup.CloseOnPressOutside

        Text {
            id: message
            anchors.centerIn: parent
            font.pointSize: 12
            color: "#ffffff"
        }
        onOpened: popupClose.start()
    }

    // Popup will be closed automatically in 2 seconds after its opened
    Timer {
        id: popupClose
        interval: 1000
        onTriggered: popup.close()
    }

    // Create and initialize the database
    function userDataBase()
    {
        var db = LocalStorage.openDatabaseSync("UserLoginApp", "1.0", "Login example!", 1000000);
        db.transaction(function(tx) {
            tx.executeSql('CREATE TABLE IF NOT EXISTS UserDetails(username TEXT, password TEXT, hint TEXT)');
        })
        return db;
    }

    // Register New user
    function registerNewUser(uname, pword, pword2, hint)
    {
        var ret  = Backend.validateRegisterCredentials(uname, pword, pword2, hint)
        var message = ""
        switch(ret)
        {
        case 0: message = "Valid details!"
            break;
        case 1: message = "Missing credentials!"
            break;
        case 2: message = "Password does not match!"
            break;
        }

        if(0 !== ret)
        {
            popup.popMessage = message
            popup.open()
            return
        }

        dataBase.transaction(function(tx) {
            var results = tx.executeSql('SELECT password FROM UserDetails WHERE username=?;', uname);
            if(results.rows.length !== 0)
            {
                popup.popMessage = "User already exist!"
                popup.open()
                return
            }
            tx.executeSql('INSERT INTO UserDetails VALUES(?, ?, ?)', [ uname, pword, hint ]);
            showUserInfo(uname) // goto user info page
        })
    }

    // Login users
    function loginUser(uname, pword)
    {
        var ret  = Backend.validateUserCredentials(uname, pword)
        var message = ""
        if(ret)
        {
            message = "Missing credentials!"
            popup.popMessage = message
            popup.open()
            return
        }

        dataBase.transaction(function(tx) {
            var results = tx.executeSql('SELECT password FROM UserDetails WHERE username=?;', uname);
            if(results.rows.length === 0)
            {
                message = "User not registered!"
                popup.popMessage = message
                popup.open()
            }
            else if(results.rows.item(0).password !== pword)
            {
                message = "Invalid credentials!"
                popup.popMessage = message
                popup.open()
            }
            else
            {
                console.log("Login Success!")
                showUserInfo(uname)
            }
        })
    }

    // Retrieve password using password hint
    function retrievePassword(uname, phint)
    {
        var ret  = Backend.validateUserCredentials(uname, phint)
        var message = ""
        var pword = ""
        if(ret)
        {
            message = "Missing credentials!"
            popup.popMessage = message
            popup.open()
            return ""
        }
        
        dataBase.transaction(function(tx) {
            var results = tx.executeSql('SELECT password FROM UserDetails WHERE username=? AND hint=?;', [uname, phint]);
            if(results.rows.length === 0)
            {
                message = "User not found!"
                popup.popMessage = message
                popup.open()
            }
            else
            {
                pword = results.rows.item(0).password
            }
        })
        return pword
    }

    // Show UserInfo page
    function showUserInfo(uname)
    {
        stackView.replace("qrc:/UserInfoPage.qml", {"userName": uname})
    }

    // Logout and show login page
    function logoutSession()
    {
        stackView.replace("qrc:/LogInPage.qml")
    }

    // Show Password reset page
    function forgotPassword()
    {
        stackView.replace("qrc:/PasswordResetPage.qml")
    }
}

 

Below you can see the screenshots from this application.

This slideshow requires JavaScript.

Source code:

https://github.com/arunpkqt/UserLoginApp

16 responses to “QML User Login App with SQLite backend”

  1. Thank you a bunch for sharing this with all folks you really recognise what you’re speaking approximately! Bookmarked. Kindly also talk over with my website =). We may have a hyperlink alternate contract among us

    Like

      • When I run it on the emulator, the color of the border line (left and right, bottom) is there; but only the upper side (the side near the Sigin-in button) is not.
        My AVD environment:
        Name: Nexus S API 29
        Resolution: 480×800(hdpi)
        API: 29
        Target: Android 10.0 (Google APIs)

        Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: