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.
Source code:
16 responses to “QML User Login App with SQLite backend”
Thankyou very much
LikeLiked by 1 person
The default interface and very useful but it is it is using GPL license, too limit for use code in this demo.
LikeLike
Thanks for your comment. I have changed the License to MIT.
LikeLike
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
LikeLike
not able to set username and password icon. is is not displaying,showing wrong sign in app
LikeLike
Hey Suraj, It might happen that the font-awesome font is not loaded correctly. You could also try replacing it with the latest version of the font from https://fontawesome.com/ and try again.
LikeLike
How can i add login with facebook feature in it???
LikeLike
There is no built-in support in Qt or QML.
You might need to use third party framework. One is Felgo which is officially listed by Facebook as Qt/QML support https://felgo.com/doc/plugin-facebook/
LikeLike
[…] 3. QML User Login App with SQLite backend – Arun’s Qt Corner […]
LikeLike
How to track the database location file? Can I read and edit the database too?
LikeLike
hey Syahir,
The doc says,
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.
Read & Edit is supported by default.
You can have a look at the example here
https://doc.qt.io/qt-5/qtquick-localstorage-example.html
LikeLike
[…] 11. QML User Login App with SQLite backend – Arun’s Qt Corner […]
LikeLike
Why is the border line above my “sign up” not showing?
LikeLike
Hello,
What you meant by the borderline above the sign-up button was not clear to me.
Could you please explain what the issue is?
LikeLike
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)
LikeLike
[…] QML User Login App with SQLite backend – Arun’s Qt Corner […]
LikeLike