Use ScrollViews and ListViews in Your App

Create a ScrollView with AppFlickable

A ScrollView is a scrollable container that holds content. With V-Play the AppFlickable component is used to create such a view. Basically any time you have content that will exceed the screen size, you will need to make it scrollable. Depending on your type of content, you can then use an AppFlickable or check out the next section about the ListView.

 import VPlayApps 1.0
 import QtQuick 2.0

 App {

   NavigationStack {

     Page {
       id: page
       title: "AppFlickable"

       AppFlickable {
         anchors.fill: parent                // The AppFlickable fills the whole page
         contentWidth: contentColumn.width   // You need to define the size of your content item
         contentHeight: contentColumn.height

         // Using a Column as content item is very convenient
         // The height of the column is set automatically depending on the child items
         Column {
           id: contentColumn
           width: page.width // We only need to set the width of a column

           // We use a repeater to create 4 colored Rectangles
           Repeater {
             // We use a simple array of colors as model
             model: ["red","green","yellow","blue"]

             Rectangle {
               color: modelData    // This will be "red", "green", ...
               width: parent.width
               height: dp(200)
             }
           }
         }
       }
     }
   }
 }

Create a ListView to Display Rows

A ListView is a scrollable container that displays rows defined by the model and delegate properties. In V-Play, you can use the AppListView component to get a native scrolling behavior and look-and-feel to display your content in lists.

Display Items in a List

The easiest way to to use the AppListView is by choosing the SimpleRow type as the ListView::delegate. If the model is an array, the SimpleRow properties are automatically initialized with the matching array properties.

 import VPlayApps 1.0

 App {
   NavigationStack {
     Page {
       title: "Basic List Example"

       AppListView {
         model: [
           {
             text: "Apple",
             detailText: "A delicious fruit with round shape",
             icon: IconType.apple
           },

           {
             text: "Beer",
             detailText: "A delicous drink",
             icon: IconType.beer
           }
         ]

         delegate: SimpleRow {}
       }
     }
   }
 }

The SimpleRow is a convenience component to quickly display data from a model specified as a JSON array. It requires the model to have a property text, and can optionally also show other properties like detailText, icon. See here for all the properties it supports.

Display Custom List Items

Although the SimpleRow is useful if you want to display native looking items in a list, you will want more control how your list items look. A list item is called AppListView::delegate in QML & V-Play.

The following example shows:

  • How to make a custom list delegate.
  • How to query the current index of the list delegate in the model array: the first 2 items are set to Today and Tomorrow in the list. Use the in a delegate to access the current index number in the array model, starting with 0 for the first entry in the array.
  • How to display data from the model by using the modelData property. With modelData.<propertyName>, you can access any property from your model.

This is how the example looks like on Android (with a native title in Material design):

 import VPlayApps 1.0
 import QtQuick 2.0

 App {

   onInitTheme: {
     Theme.colors.backgroundColor = "#51b4de" // light blue
     Theme.colors.secondaryBackgroundColor = Theme.colors.backgroundColor
     Theme.colors.textColor = "white"
   }

   NavigationStack {

     Page {
       title: "Custom List Delegate"

       AppListView {
         id: myListView

         // UI properties
         x: dp(10) // left margin
         y: dp(10) // top margin
         property real widthDay: dp(90)
         property real widthTempMaxMin: dp(60)
         property real widthRain: dp(40)
         property real itemRowSpacing: dp(20)
         spacing: dp(5) // vertical spacing between list items/rows/delegates

         // the model will usually come from a web server, copy it here for faster development & testing
         model: [
           {day: "Monday",    tempMax: 21, tempMin: 15, rainProbability: 0.8, rainAmount: 3.153},
           {day: "Tuesday",   tempMax: 24, tempMin: 15, rainProbability: 0.2, rainAmount: 0.13},
           {day: "Wednesday", tempMax: 26, tempMin: 16, rainProbability: 0.01, rainAmount: 0.21},
           {day: "Thursday",  tempMax: 32, tempMin: 21, rainProbability: 0, rainAmount: 0},
           {day: "Friday",    tempMax: 28, tempMin: 20, rainProbability: 0, rainAmount: 0},
           {day: "Saturday",  tempMax: 26, tempMin: 19, rainProbability: 0, rainAmount: 0},
           {day: "Sunday",    tempMax: 25, tempMin: 19, rainProbability: 0, rainAmount: 0}
         ]

         header: Row {
           spacing: myListView.itemRowSpacing

           // empty list item to reserve the space in the row
           Item {
             width: myListView.widthDay
             height: 1
           }

           AppText {
             id: maxMinTempHeader
             text: "Max/Min"
             horizontalAlignment: Text.AlignHCenter
             width: myListView.widthTempMaxMin
           }

           AppText {
             text: "Rain"
             horizontalAlignment: Text.AlignHCenter
             width: myListView.widthRain
           }
         }

         delegate: Row {
           id: dailyWeatherDelegate
           spacing: myListView.itemRowSpacing

           AppText {
             // if it is the first entry, display "Today", if it is the second, display "Tomorrow"
             // otherwise display the day property from the model
             text: index === 0 ? "Today" :
                   index === 1 ? "Tomorrow" :
                   modelData.day

             // make all days the same width
             width: myListView.widthDay
             anchors.verticalCenter: parent.verticalCenter
           }

           AppText {
             text: modelData.tempMax + "°/" + modelData.tempMin + "°"
             horizontalAlignment: Text.AlignHCenter
             width: myListView.widthTempMaxMin
             anchors.verticalCenter: parent.verticalCenter
           }

           Column {
             width: myListView.widthRain
             anchors.verticalCenter: parent.verticalCenter
             AppText {
               text: Math.round(modelData.rainAmount*10)/10 + "l" // round to 1 decimal
               fontSize: 18
               anchors.horizontalCenter: parent.horizontalCenter
             }
             AppText {
               id: precipProbability
               text: Math.round(modelData.rainProbability * 1000)/10 + "%" // round percent to 1 decimal
               fontSize: 12
               anchors.horizontalCenter: parent.horizontalCenter
             }
           }

         }// dailyWeatherDelegate
       }//ListView

     }// Page
   }
 }

In a real world application, you will probably receive the data for the model from a web server with a REST interface. Please see Access a REST Service how to fetch data from a server.

Create Foldable Sub-Sections in List Delegates

Another common use case is to fold/unfold subsections within a list if you press any of the list item. One solution how you can do this, is with a Repeater and using positioning components like a Column, which is used to place objects below each other.

See this example how this looks like and the source code below:

 import VPlayApps 1.0
 import QtQuick 2.0

 App {

   NavigationStack {

     Page {
       title: "Custom List Delegate"

       AppListView {
         id: myListView

         // the model will usually come from a web server
         model: [
           {letter: "A"},{letter: "B"},{letter: "C"},{letter: "D"},{letter: "E"},
           {letter: "F"},{letter: "G"},{letter: "H"},{letter: "I"},{letter: "J"}
         ]

         // one sub-array for each list item
         property var subsectionModel: [
           [{word: "Apple"}, {word: "Ananas"}],
           [{word: "Banana"}, {word: "Ball"}, {word: "Balloon"}],
           [{word: "Clock"}],
           [{word: "Dog"}, {word: "Donut"}, {word: "Dollar"}],
           // etc. etc., for demo purposes only list first few entries
         ]

         spacing: dp(20) // to have some space between list items

         delegate: Item {
           x: dp(10) // left margin
           // the height is bigger if the subsection is visible
           height: letterText.height +
                   (subsectionColumn.visible ? subsectionColumn.height : 0)
           width: parent.width // set width of whole ListView, makes clicking with MouseArea easier

           AppText {
             id: letterText
             text: modelData.letter
             font.bold: true // make it bold
           }

           MouseArea {
             // fill the whole area, so if a subsection is clicked it will be unfolded
             anchors.fill: parent
             onClicked: {
               // unfold subsection here
               console.debug("letter clicked:", modelData.letter)
               subsectionColumn.visible = !subsectionColumn.visible // toggle subsection visibility when clicked
             }
           }

           Column {
             id: subsectionColumn
             anchors.top: letterText.bottom // position below letterText area
             anchors.topMargin: dp(10)
             visible: false // start invisible, only show when clicked
             spacing: dp(10)
             Repeater {
               // automatically set the model here, we could also set the model dynamically when clicking to load later
               model: myListView.subsectionModel[index]

               delegate: AppText {
                 text: modelData.word
                 color: index%2 === 1 ? "red" : "blue" // alternate red & ble color
               }
             }// Repeater
           }// Column


         }//List Delegate
       }//ListView

     }// Page
   }
 }

Make a ListView Row Swipe-able

Many apps allow to show additional options for list items by swiping them to the left or the right. Imagine a mail app that shows a list of mails. Clicking a mail will open it. But it's also possible to swipe it to the right to show additional options like deleting the mail or forwarding it.

The solution to create a swipe-able ListView like that requires two components:

  • The AppListView for showing the list items in a scroll-able view.
  • The SwipeOptionsContainer for making the items swipe-able and displaying options when swiping to the left or the right.

The following example shows a simple swipe-able list within a Page. To display the list items and the swipe option the SimpleRow and SwipeButton components are used. They are convenience types with a platform-specific look that can handle the most common use-cases:

iOS Android

 import VPlayApps 1.0

 App {
   NavigationStack {

     Page {
       title: "Swipe-able List"

       AppListView {
         anchors.fill: parent
         model: [
           { text: "Item 1" },
           { text: "Item 2" },
           { text: "Item 3" }
         ]

         delegate: SwipeOptionsContainer {
           id: container

           // the swipe container uses the height of the list item
           height: listItem.height
           SimpleRow { id: listItem }

           // set an item that shows when swiping to the right
           leftOption: SwipeButton {
             icon: IconType.gear
             height: parent.height
             onClicked: {
               listItem.text = "Option clicked"
               container.hideOptions() // hide button again after click
             }
           }
         }

       } // AppListView
     }

   }
 }

Add a New Row to a ListView Dynamically

Each ListView displays its data based on a model. In the simplest case, this will be an array that holds data objects. Adding a row to the list then means adding a data object to the array model.

The following example displays a list view and a button that allows to add rows:

iOS Android

 import VPlayApps 1.0

 App {
   NavigationStack {

     Page {
       id: page
       title: "Add List Items"

       // the data model for the list
       property var dataModel: [
         { text: "Item 1" },
         { text: "Item 2" },
         { text: "Item 3" }
       ]

       // button to add an item
       AppButton {
         id: button
         anchors.horizontalCenter: parent.horizontalCenter
         text: "Add Row"
         onClicked: {
           // create and add new item
           var itemNr = page.dataModel.length + 1
           var newItem = { text: "Item "+itemNr }
           page.dataModel.push(newItem)

           // signal change in data model to trigger UI update (list view)
           page.dataModelChanged()
         }
       }

       // list view
       AppListView {
         id: listView
         anchors.top: button.bottom
         anchors.bottom: parent.bottom
         width: parent.width

         model: page.dataModel
         delegate: SimpleRow {}
       }
     }

   }
 }

Remove a Row from a ListView Dynamically

Like in the previous example, the way to remove a list item is by modifying the data model of the ListView. If the model data is an array, this means removing data objects from the array.

The following example shows a ListView and a button that allows to remove a list item:

iOS Android

 import VPlayApps 1.0

 App {
   NavigationStack {

     Page {
       id: page
       title: "Remove List Items"

       // the data model for the list
       property var dataModel: [
         { text: "Item 1" },
         { text: "Item 2" },
         { text: "Item 3" },
         { text: "Item 4" },
         { text: "Item 5" },
         { text: "Item 6" },
         { text: "Item 7" },
         { text: "Item 8" },
         { text: "Item 9" }
       ]

       // button to add an item
       AppButton {
         id: button
         anchors.horizontalCenter: parent.horizontalCenter
         text: "Remove Row"
         onClicked: {
           // remove second item from the data model
           page.dataModel.splice(1, 1)

           // signal change in data model to trigger UI update (list view)
           page.dataModelChanged()
         }
       }

       // list view
       AppListView {
         id: listView
         anchors.top: button.bottom
         anchors.bottom: parent.bottom
         width: parent.width

         model: page.dataModel
         delegate: SimpleRow {}
       }
     }

   }
 }

Use Sections and a SectionSelect

The SectionSelect control provides a side-bar that allows to alphabetically jump to the corresponding sections of a ListView. The side-bar has a native look on iOS & Android and takes the Theme colors into account.

iOS Android

The following example uses a ListPage instead of a normal Page, which is a page with a built-in AppListView.

 import VPlayApps 1.0

 App {
   NavigationStack {

     ListPage {
       id: listPage
       title: "SectionSelect Example"

       // add dummy entries for list page
       model: {
         var model = []
         for(var i = 0; i < 26; i++) {
           for(var j = 0; j < 5; j++) {
             var entry = {
               text: String.fromCharCode(65 + i) + " " + j,
               section: "Section: "+String.fromCharCode(65 + i), // for section display in list
               letter: String.fromCharCode(65 + i) // only for SectionSelect, the actual sections all start with 'Section ...'
             }
             model.push(entry)
           }
         }
         return model
       }
       delegate: SimpleRow { }

       // activate sections
       section.property: "section"

       // add section select
       SectionSelect {
         id: sectionSelect
         anchors.right: parent.right
         target: listPage.listView
         sectionProperty: "letter"
       }

     } // ListPage

   }
 }

Voted #1 for:

  • Easiest to learn
  • Most time saving
  • Best support

Develop Cross-Platform Apps and Games 50% Faster!

  • Voted the best supported, most time-saving and easiest to learn cross-platform development tool
  • Based on the Qt framework, with native performance and appearance on all platforms including iOS and Android
  • Offers a variety of plugins to monetize, analyze and engage users
FREE!
create apps
create games
cross platform
native performance
3rd party services
game network
multiplayer
level editor
easiest to learn
biggest time saving
best support