Qt Quick QML Tutorial: Master ListView, ListModel, Delegate, and Custom TextField Components

This article focuses on two of the most common UI capabilities in Qt Quick: QML ListView rendering and custom TextField inputs. It addresses the fragmented understanding many beginners have around model, delegate, ListModel, anchors, and style customization, then connects these core concepts through runnable examples. Keywords: QML ListView, TextField, custom components.

Technical Specification Snapshot

Parameter Description
Language QML
Framework Qt Quick / Qt Quick Controls
License Not explicitly stated in the original article; the page displays CC 4.0 BY-SA
GitHub Stars Not provided in the original article
Core Dependencies QtQuick 2.12, QtQuick.Window 2.2, QtQuick.Controls 2.5, QtQml 2.0

This note actually covers two high-frequency QML scenarios

The first part explains the ListView list component, with a focus on the difference between a numeric model and a custom ListModel, as well as how delegate determines the rendering of each item.

The second part covers a custom TextField, focusing on anchor-based layout, padding, background replacement, embedded icons, and the onTextChanged signal. Together, these two capabilities are enough to support most basic Qt Quick interface prototypes.

The core of ListView is a data-driven view

In QML, ListView itself does not care what your business data looks like. It only depends on model to provide data, and then uses delegate to describe how each data item should be displayed. As a result, the key to understanding lists is not the control’s appearance, but the data binding path.

ListView {
    width: 100
    height: 100
    anchors.centerIn: parent  // Center the list in the parent container
    spacing: 10               // Set spacing between list items
    model: 10                 // Generate 10 items with values from 0 to 9

    delegate: Label {
        text: "The number is: " + modelData  // modelData represents the current item value
        color: "#fff"
        font.pointSize: 14
    }
}

This code quickly generates a numeric list and works well for validating the minimum viable ListView + delegate pattern.

Using a fixed numeric model helps you quickly understand modelData

When model: 10 is used, QML automatically creates 10 anonymous data items with indexes ranging from 0-9. This type of model is ideal for teaching and debugging because it does not require you to declare an additional data structure.

The most important variable here is modelData. It is the default entry point inside the delegate for accessing the current item’s data. In a simple model, it directly represents the number itself. In an object-based model, you typically access named fields instead.

A custom ListModel is closer to real project data

In real projects, you usually do not display a single number. Instead, you display structured fields such as name, age, status, or avatar. In that case, you should use ListModel together with multiple ListElement objects to build an explicit data source.

ListModel {
    id: mymodel
    ListElement { name: "ycc";  age: 24 }
    ListElement { name: "yc";   age: 27 }
    ListElement { name: "yccd"; age: 29 }
    ListElement { name: "ycfc"; age: 28 }
    ListElement { name: "ydcc"; age: 20 }
}

This code defines a structured list model in which each ListElement is a data record that a view can consume.

Decoupling the model from the delegate significantly improves reusability

As the data structure becomes more complex, you should avoid inlining a large block of UI directly inside delegate. A more maintainable approach is to encapsulate the list item in a separate Component and then reference it from ListView.

ListView {
    width: 100
    height: 100
    spacing: 10
    anchors.centerIn: parent   // Center the entire list
    model: mymodel             // Bind the custom model
    delegate: mydelegate       // Use an external component as the delegate
}

Component {
    id: mydelegate

    Label {
        text: "Name: " + name + " Age: " + age  // Read model fields directly
        color: "#fff"
        font.pointSize: 14
    }
}

This example shows the standard ListModel + ListView + Component combination, which is suitable for small and medium-sized business lists.

The key to a custom TextField is not input itself, but style overriding

TextField works out of the box, but in real interfaces you usually need to customize the background, icons, and padding. The strength of QML is that you can directly rewrite control styling through declarative composition instead of relying on complex inheritance.

import QtQuick 2.12
import QtQuick.Window 2.2
import QtQuick.Controls 2.5

ApplicationWindow {
    visible: true
    width: 1000
    height: 700
    title: qsTr("Application Demo")

    Rectangle {
        anchors.fill: parent
        color: "orange"

        TextField {
            anchors.centerIn: parent      // Place the input field in the center of the window
            width: 200
            color: "red"
            font.pointSize: 24
            topPadding: 15
            bottomPadding: 15
            leftPadding: 70               // Reserve space for the left icon
            rightPadding: 20

            background: Rectangle {
                anchors.fill: parent
                color: "yellow"
                radius: 7                 // Draw a rounded background
            }

            Image {
                source: "qrc:/images/photo.png"
                width: 32
                height: 40
                anchors.left: parent.left
                anchors.verticalCenter: parent.verticalCenter
            }

            onTextChanged: {
                console.log("Text Change")  // Print a debug log when the text changes
            }
        }
    }
}

This code creates a custom input field with a left-side icon, a rounded background, and input change monitoring.

Anchor-based layout and padding together determine control usability

anchors.centerIn determines where a control is placed, while padding determines whether its content feels comfortable to use. Many input fields look functional but feel awkward in practice, and the root cause is often not the control itself, but the lack of enough space for text and icons.

In this example, leftPadding: 70 is the key line. Without it, the input text would overlap with the image on the left, reducing readability. Meanwhile, background replaces the default skin so the TextField can have its own visual style.

The original image is primarily decorative and does not carry core technical information

AI Visual Insight: This image is a “Run Code” entry icon used mainly for page interaction cues. It does not show the QML component tree, layout hierarchy, or runtime UI result, so it does not provide meaningful technical structure information.

These two examples reveal a unified learning path

Start with anchors and basic controls, then learn model/delegate, and finally move on to component encapsulation and style overriding. This is the most reliable path for Qt Quick beginners because it follows the cognitive sequence of layout first, data second, and abstraction third.

If you want to go deeper, the next topics to study should be Repeater, GridView, property binding, state machines, and the qrc resource system. That is how you move from writing demos to building practical interfaces.

During development, remove redundant imports and leftover comments first

The original code includes repeated QtQuick imports across versions and an unused Material module. For beginners, this kind of redundancy may not trigger immediate errors, but it increases cognitive load and can easily create confusion around versioning.

import QtQuick 2.12
import QtQuick.Window 2.2
import QtQuick.Controls 2.5
import QtQml 2.0

// Remove duplicate or unused modules to keep dependencies minimal

This example shows that QML dependency declarations should remain as minimal as possible to reduce maintenance cost and ambiguity.

FAQ

Q1: What are the responsibilities of ListView, model, and delegate?

ListView is the container responsible for scrolling and holding the list. model provides the data. delegate determines how each item is rendered. Without any one of these three parts, the list cannot work correctly.

Q2: When should you use modelData, and when should you use fields like name and age directly?

Use modelData when the model is a simple value such as model: 10. Use explicit field names such as name and age directly inside the delegate when the model is a ListModel with named fields.

Q3: Why do you need to set leftPadding when customizing an input field?

Because an Image icon is embedded on the left side. If you do not increase the left padding, the text will overlap the icon area. In essence, leftPadding redraws a safe boundary for the content area.

Core Summary: This article restructures a Qt Quick/QML study note around two high-frequency interface capabilities: the ListView list component and the custom TextField input field. It covers model, delegate, ListModel, Component, anchors, padding, background, Image, and signal handling, while also providing runnable examples, a structured summary, and frequently asked questions.