官方文档说它可以是一个 Item、一个 Component 或一个 url——三种传法对应三种典型的页面管理方式。
这篇用一个 demo 把三种 push 各做一遍,它们推入的都是同一个 PushItem.qml 页面,但加载路径完全不同:一种是"页面对象已在 QML 里声明好,直接推实例",另两种是"把页面当模板,用时再创建"。顺带看 push 返回的页面对象怎么用——返回后立刻 connect 它发出的信号,实现页面与栈的解耦回调。
- Push Item — push 一个已存在的页面实例
- Push Component —
Qt.createComponent()创建组件后 push - Push URL — push 一个 QML 文件地址,内部自动加载
三种 push 方式
页面顶部三个按钮,分别演示 push 一个 Item、push 一个 Component、push 一个 URL。被推入的页面是同一个 PushItem.qml:白底圆角卡片,中间一行标题(颜色随 push 传入的参数变化)、一段说明文字和一个 Pop 按钮。点 Pop 会通知外层把这一页弹掉。
演示代码
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
FadeInAnimation {
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 15
// ... 省略标题组件 TitleSeparator ...
StackView {
id: stack
initialItem: mainView
Layout.fillWidth: true
Layout.fillHeight: true
spacing: 15
clip: true
}
}
Component {
id: mainView
ColumnLayout {
spacing: 10
property color textColor: "#333"
Button {
text: "Push Item"
Layout.fillWidth: true
Layout.preferredHeight: 30
onClicked: {
var info = {"textColor":"#3498db", "textTitle":"Push Item", "textInfo":"用于测试【Push Item】, 调用StackView - push" }
var page = stack.push(stackItem, info)
page.popClicked.connect(slotPopItem)
}
}
Button {
text: "Push Component"
Layout.fillWidth: true
Layout.preferredHeight: 30
onClicked: {
var info = {"textColor":"#e74c3c", "textTitle":"Push Component", "textInfo":"用于测试【Component】, 调用Qt.createComponent()" }
var com = Qt.createComponent(Qt.resolvedUrl("PushItem.qml"))
if (com.status === Component.Ready) {
var page = stack.push(com, info)
page.popClicked.connect(slotPopItem)
}
}
}
Button {
text: "Push URL"
Layout.fillWidth: true
Layout.preferredHeight: 30
onClicked: {
// 通过 URL 加载 PushItem.qml
var info = {"textColor":"#2ecc71", "textTitle":"Push URL", "textInfo":"用于测试【Push URL】- Qt.resolvedUrl" }
var page = stack.push(Qt.resolvedUrl("PushItem.qml"), info)
page.popClicked.connect(slotPopItem)
}
}
Item { Layout.fillHeight: true }
}
}
PushItem {
id: stackItem
visible: false
}
function slotPopItem() {
stack.pop()
}
}
被推入的页面 PushItem.qml:
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
Rectangle {
width: 250
height: 250
border.color: "#ccc"
border.width: 0
radius: 6
visible: false
property color textColor: "#333"
property string textTitle: ""
property string textInfo: ""
signal popClicked
ColumnLayout {
anchors.fill: parent
anchors.margins: 0
spacing: 15
Text {
text: textTitle
color: textColor
font.pointSize: 13
font.bold: true
}
Text {
text: textInfo
color: textColor
font.pointSize: 11
Layout.fillWidth: true
Layout.preferredHeight: 60
wrapMode: Text.Wrap
}
RoundButton {
text: "Pop"
Layout.preferredWidth: 90
Layout.preferredHeight: 30
onClicked: popClicked()
}
Item { Layout.fillHeight: true }
}
}
关键逻辑解析
push 的第二个参数 properties 会把值注入页面同名属性。三个按钮都构造了一个 info 对象,键名是 textColor、textTitle、textInfo,而 PushItem 恰好声明了这三个 property。push 时传入的 properties 会按名赋给新页面——这就是为什么三种方式推入同一个 qml,标题文字和颜色却各不相同。属性注入让"同一页面模板 + 不同数据"的复用模式成立。
Push Item:直接推一个已存在的实例。PushItem { id: stackItem; visible: false } 挂在文件根部但平时不可见,像一个"备用页面"。stack.push(stackItem, info) 把这个现成实例推入栈。适合页面对象已经声明好、需要重复使用同一份实例的场景——但要小心:它一直被复用的是同一个实例,适合"推入一次用完就弹"的页面,不适合需要同时存在多份副本的场景。
Push Component:先用 Qt.createComponent 造出组件。Qt.createComponent(Qt.resolvedUrl("PushItem.qml")) 把 qml 文件编译成一个 Component 对象,组件就绪后 push 它——StackView 会为每次 push 创建一份页面实例。本地文件通常同步加载完成,但加载远程组件或文件较多时可能未就绪,所以稳妥起见先查 com.status === Component.Ready 再 push(这是 createComponent 用法的标准姿势,避免拿一个没加载好的组件去 push)。适用于"页面文件可能较多、想统一管理加载时机"的场景。
Push URL:把地址直接交给 StackView。stack.push(Qt.resolvedUrl("PushItem.qml"), info) 不用自己创建组件,StackView 内部完成加载和实例化,代码最省。三种方式最终都会返回"成为当前页的那个 Item",所以 var page = stack.push(...) 拿到的就是新页面对象。
page.popClicked.connect(slotPopItem) 是页面与栈解耦的关键。PushItem 内部只声明 signal popClicked,Pop 按钮点了就发信号——它并不知道 StackView 的存在,也不知道自己该被谁弹掉。外层拿到 push 返回的 page 后,把它的 popClicked 信号连到 slotPopItem,后者执行 stack.pop()。这样页面文件可独立复用(放哪个 StackView 里都能用),栈操作统一由外层负责。
enabled 与状态保护:这里没给按钮加 enabled 判断,因为每次 push 后当前页会被新页面盖住,按钮点不到,天然防止重复触发。
三种方式怎么选
| 方式 | 加载时机 | 页面实例 | 适用场景 |
|---|---|---|---|
| Push Item | 页面已创建好,随用随推 | 复用同一个实例 | 单份常驻页面、动态数据 |
| Push Component | 手动 `Qt.createComponent`,可控制加载 | 每次 push 新建 | 需要管理加载状态、批量页面 |
| Push URL | StackView 内部自动加载 | 每次 push 新建 | 最省代码,页面独立成文件 |
日常开发里 Push URL 最常用——页面天然按文件拆分,push 时给个地址即可;需要先确认组件加载成功再动作时用 Component;页面对象已在界面里声明、只需要推一次的场景才用 Item。
运行验证
- Qt Creator 打开
qml_stackview/CMakeLists.txt,按Ctrl+R运行; - 左侧点「Push对象」,依次点 Push Item / Push Component / Push URL;
- 观察每次推入的卡片标题与颜色不同(蓝/红/绿),点卡片上的 Pop 按钮,页面被外层
slotPopItem弹回按钮页。
扩展复用方向
- properties 注入不止传文本颜色,传数据对象、回调函数都能按名赋到页面属性上,做成"通用详情页 + 数据驱动";
- 把 push 返回的
page存起来,connect 它的多个信号,就能实现页面内确认框、表单提交后外层统一收尾的协作模式; - 页面文件多了以后,把 URL 字符串集中到一个路由表(按页面名查 URL),push 时只传名字,页面跳转逻辑更清晰。
已验证环境:
- Qt 版本:Qt 6.8.2 / Qt 6.11.1
- 操作系统:Windows 11
- GitHub:QML-Minimal-Demos/qml_stackview
三种入栈方式对比清晰,properties 注入与 push 返回对象信号解耦是可直接复用的工程技巧。适合做 QML 页面跳转与路由设计的开发者参考,日常优先 Push URL。