QML StackView:三种入栈方式(Item、Component 与 URL)

stack.push(参数) 里那个参数到底能传什么?

官方文档说它可以是一个 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 会通知外层把这一页弹掉。

演示代码

qml 复制代码
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:

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。

运行验证

  1. Qt Creator 打开 qml_stackview/CMakeLists.txt,按 Ctrl+R 运行;
  2. 左侧点「Push对象」,依次点 Push Item / Push Component / Push URL;
  3. 观察每次推入的卡片标题与颜色不同(蓝/红/绿),点卡片上的 Pop 按钮,页面被外层 slotPopItem 弹回按钮页。

扩展复用方向

  • properties 注入不止传文本颜色,传数据对象、回调函数都能按名赋到页面属性上,做成"通用详情页 + 数据驱动";
  • 把 push 返回的 page 存起来,connect 它的多个信号,就能实现页面内确认框、表单提交后外层统一收尾的协作模式;
  • 页面文件多了以后,把 URL 字符串集中到一个路由表(按页面名查 URL),push 时只传名字,页面跳转逻辑更清晰。

已验证环境:

相关推荐
kaixin_learn_qt_ing3 小时前
Qt开发工程师
qt
ShineWinsu18 小时前
对于Qt:信号与槽的解析
c++·qt·gui·lambda·信号·信号与槽·槽函数
努力实干1 天前
Qt/C++ 模块化设计与工程组织
开发语言·c++·qt
Lhan.zzZ1 天前
Qt:动态库模块框架设计
开发语言·c++·qt
qq_401700412 天前
TCP 长连接、心跳包与断线自动重连设计:工业级通信的“不死鸟”架构
网络·qt·tcp/ip
dyxal3 天前
A2A 协议 v1.0 完全指南:方法名、Agent Card、三种绑定与 FastAPI 实战
开发语言·qt·fastapi
金色熊族3 天前
在Qt中使用asan检测内存泄漏(一)
linux·qt·asan
前行中的小猪3 天前
Qt 5 踩坑实录:多显示器跨屏拖拽导致窗口异常放大与 setGeometry 报错的深度解析与根治
qt·多显示器·高分辨率·多屏幕
想做小南娘,发现自己是女生喵3 天前
QT项目实战第一周(嵌入式)
开发语言·qt
誰能久伴不乏3 天前
【无标题】
c++·qt·机器人