I have a QtQuick StackView application that involves some quite heavy pages. I want to pre-load some of the heaviest pages during app startup to reduce lag and wait time later on.
For reference, the heaviest page involves several backend calls, many visual elements, a few loaders, and buttons who's text and functionality depends on a parameter passed to the page.
I have tried to set up a minimal application that achieves the same result: This app has a loading screen for while everything is set up (and ideally pre-loading heavy pages), and a landing screen from where the user can navigate to the heavy screen.
Main.qml - Set up StackView
import QtQuick 2.15
import QtQuick.Controls 2.15
ApplicationWindow {
width: 640
height: 480
visible: true
title: qsTr("Pre-loading stack view application")
StackView {
id: stackView
anchors.fill: parent
initialItem: LoadingScreen {}
}
}
LoadingScreen.qml - Display this while everything is loaded, then move to landing screen
import QtQuick 2.15
import QtQuick.Controls 2.15
import "LoadingFunction.js" as LoadingFunction
Rectangle {
id: loadingScreen
width: stackView.width
height: stackView.height
Rectangle {
anchors.fill: parent
color: "lightgray"
Text {
anchors.centerIn: parent
text: "Loading..."
}
}
// Do some loading from database..
property double dbLoadTime: LoadingFunction.simulateLoadTime(loadingScreen)
// Pre-load heavy page
Loader {
id: preloadHeavy
asynchronous: true
source: "HeavyScreen.qml"
visible: false
onLoaded: {
console.log("HeavyScreen preloaded.");
}
}
Component.onCompleted: {
stackView.replace("LandingPage.qml");
}
}
LandingPage.qml - The application landing page
import QtQuick 2.15
import QtQuick.Controls 2.15
Rectangle {
id: landingPage
width: stackView.width
height: stackView.height
Column {
anchors.centerIn: parent
spacing: 10
Text {
text: "Landing Page"
font.pointSize: 20
}
Button {
text: "Go to Heavy Screen"
onClicked: {
stackView.push("HeavyScreen.qml", {displayString: "Hello from Landing Page"})
}
}
}
}
HeavyScreen.qml - I want this to be pre-loaded (and also not destroyed on pop so that it can be re-opened immediately under different configurations based on displayString)
import QtQuick 2.15
import QtQuick.Controls 2.15
import "LoadingFunction.js" as LoadingFunction
Rectangle {
id: heavyScreen
width: stackView.width
height: stackView.height
property string displayString
property double dbLoadTime: LoadingFunction.simulateLoadTime(heavyScreen)
Column {
anchors.centerIn: parent
Text {
text: "Your string: " + displayString
}
Button{
text: "Back"
onClicked: stackView.pop()
}
}
}
LoadingFunction.js - to simulate heavy components
function simulateLoadTime(parent) {
let randomTime = 3000 + Math.floor(Math.random() * 2000); // 3000-4999 ms
console.log("Simulated load time:", randomTime, "ms");
Qt.createQmlObject(`
import QtQuick 2.15
Timer {
interval: ${randomTime};
repeat: false;
running: true;
onTriggered: {
console.log("Load completed after ${randomTime} ms");
destroy();
}
}
`, parent, "SimulatedLoadTimer");
return randomTime
}
Upon starting the application the following outputs are immediately printed to the console and the app goes to the landing screen.
qml: Simulated load time: 4960 ms
qml: Simulated load time: 3236 ms
qml: HeavyScreen preloaded.
After sitting on the landing page for a while the following is printed to console.
qml: Load completed after 3236 ms
qml: Load completed after 4960 ms
This seems to mean that the loading is not completed before moving to the landing page even though the Loader's onLoaded message is printed. Why is this happening?
Additionally, after clicking 'Go to heavy screen' from the landing page the following is printed to console.
qml: Simulated load time: 3550 ms
qml: Load completed after 3550 ms
This indicates that the pre-loaded page is not being used since the loading function in the heavy page is getting called again. How can I make sure to push the pre-loaded page to the StackView?
How can I make sure to push the pre-loaded page to the StackView?
You can bind a property to the dynamically loaded item to push the preloaded item :
LoadingScreen.qml :
Component.onCompleted: {
stackView.replace("LandingPage.qml",
{"rec": Qt.binding(function() { return preloadHeavy.item;})})
}
LandingPage.qml :
Rectangle {
id: landingPage
property Rectangle rec // add a Rectangle property
width: stackView.width
height: stackView.height
Column {
anchors.centerIn: parent
spacing: 10
Text {
text: "Landing Page"
font.pointSize: 20
}
Button {
text: "Go to Heavy Screen"
onClicked: {
// push the property bound to the dynamically loaded item
stackView.push(rec, {displayString: "Hello from Landing Page"})
}
}
}
}