This page is a summary of common use-cases and shows how they are solved with the Felgo App Components.
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 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 Felgo App { NavigationStack { AppPage { 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 { iconType: IconType.gear height: parent.height onClicked: { listItem.text = "Option clicked" container.hideOptions() // hide button again after click } } } } // AppListView } } }
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 Felgo App { NavigationStack { AppPage { 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 {} } } } }
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 Felgo App { NavigationStack { AppPage { 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 {} } } } }
Each NavigationStack automatically adds a NavigationBar that shows the current page title and other NavigationBarItems. By setting the AppPage::navigationBarHidden property to true
, the navigation bar won't be shown for
that page.
The following example shows a Page with a button that allows to show or hide the navigation bar:
iOS | Android |
---|---|
import Felgo App { NavigationStack { AppPage { id: page title: "Hide Navigation Bar" // this is the default, the NavigationStack shows a navigation bar for this page navigationBarHidden: false AppButton { anchors.centerIn: parent text: "Show/Hide Navigation Bar" // when clicked, we switch the navigationBarHidden property onClicked: page.navigationBarHidden = !page.navigationBarHidden } } } }
The NavigationStack shows the AppPage::titleItem of the currently active page in the navigation bar. This default title item is a simple Text that shows the AppPage::title. By overwriting the AppPage::titleItem you can replace the item.
The following example replaces the default title item to show an image together with the page title:
iOS | Android |
---|---|
import Felgo import QtQuick App { NavigationStack { AppPage { id: page title: "My Title" // we define a custom titleItem, that consists of an image and a title text titleItem: Row { spacing: dp(6) Image { anchors.verticalCenter: parent.verticalCenter height: titleText.height fillMode: Image.PreserveAspectFit source: "../assets/felgo-logo.png" } AppText { id: titleText anchors.verticalCenter: parent.verticalCenter text: page.title font.bold: true font.family: Theme.boldFont.name font.pixelSize: dp(Theme.navigationBar.titleTextSize) color: "orange" } } // titleItem } } }
To implement logic in QML we can simply add JavaScript code to our QML items. JavaScript already comes with features to load and parse JSON data. With the XMLHttpRequest
Element we can load data from a file or web service. The JSON.parse
function then allows us to convert the data to a JSON object.
The following example dynamically loads JSON data from a file and displays it in a list:
iOS | Android |
---|---|
data.json
[ { "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" } ]
Main.qml
import Felgo import QtQuick App { id: app // property will store data property var jsonData: null // we load the data when the component was successfully created Component.onCompleted: { loadJsonData() } NavigationStack { // we display the json data in a list ListPage { id: page title: "Parse JSON" model: app.jsonData } } // loadJsonData - uses XMLHttpRequest object to dynamically load data from a file or web service function loadJsonData() { var xhr = new XMLHttpRequest xhr.onreadystatechange = function() { if (xhr.readyState === XMLHttpRequest.DONE) { var dataString = xhr.responseText app.jsonData = JSON.parse(dataString) } } xhr.open("GET", Qt.resolvedUrl("data.json")) xhr.send() } }
The easiest way to work with XML data is by using the XmlListModel type. After we set the XML source and add some queries to identify the items and item attributes, we can directly use this model to display the items with components like AppListView or Repeater.
The following example sets up a XmlListModel to load data from a local XML file and displays the items in a ListPage:
iOS | Android |
---|---|
data.xml
<?xml version="1.0" encoding="UTF-8" ?> <data> <item>Item 1</item> <item>Item 2</item> <item>Item 3</item> <item>Item 4</item> <item>Item 5</item> <item>Item 6</item> <item>Item 7</item> <item>Item 8</item> <item>Item 9</item> </data>
Main.qml
import Felgo import QtQuick.XmlListModel App { // model for loading and parsing xml data XmlListModel { id: xmlModel // set xml source to load data from local file or web service source: Qt.resolvedUrl("data.xml") // set query that returns items query: "/data/item" // specify roles to access item data XmlRole { name: "itemText"; query: "string()" } } NavigationStack { // we display the xml model in a list ListPage { id: page title: "Parse XML" model: xmlModel delegate: SimpleRow { text: itemText } } } }
Setting up a local database requires the Qt Quick Local Storage features. In many cases, it is enough to have a simple key-value store. For this special use-case, Felgo offers the Storage type as a convenience wrapper around the Local Storage features. The stored values are also available after the user updated the app.
The following example stores the number of app starts in a local database and displays the current value:
iOS | Android |
---|---|
import Felgo import QtQuick App { // define Storage item for loading/storing key-value data Storage { id: localStorage property int appStarts: 0 // update app starts counter Component.onCompleted: { var nr = localStorage.getValue("appstarts") if(nr === undefined) nr = 0 nr++ localStorage.setValue("appstarts", nr) appStarts = nr } } // define page that shows nr of app starts NavigationStack { AppPage { id: page title: "Local Storage" AppText { anchors.centerIn: parent text: "App Starts: "+localStorage.appStarts } } } }
The AppMap component is an extension of the QML Map item and can display a map and optionally the user location. To actually display the map, it is required to specify a Plugin that provides the map data. QML currently supports the following plugins:
Note: To use the map features, make sure that the Qt Location components are installed. In case you are missing the module, use the MaintenanceTool in you
Felgo installation folder to add it. Also include QT += positioning location
to your project configuration in the *.pro file of your project.
The following example creates a Map that uses the Maplibre GL Map plugin:
iOS | Android |
---|---|
import Felgo import QtLocation App { NavigationStack { AppPage { title: "Map Example" // show the map AppMap { anchors.fill: parent plugin: Plugin { name: "maplibregl" // configure your styles and other parameters here parameters: [ PluginParameter { name: "maplibregl.mapping.additional_style_urls" value: "https://api.maptiler.com/maps/streets/style.json?key=get_your_own_OpIi9ZULNHzrESv6T2vL" } ] } } } } }
The AppMap component allows to show a map and optionally the user location. It is based on the QML Map item. There are several overlay items that can be placed on a map, for a list of all possible overlay items see here. The MapQuickItem type allows to place custom QML items on the map, which is what we will use for this example.
Note: To use the map features, make sure that the Qt Location components are installed. In case you are missing the module, use the MaintenanceTool in you
Felgo installation folder to add it. Also include QT += positioning location
to your project configuration in the *.pro file of your project.
The following QML code creates a map with a custom overlay at its center:
iOS | Android |
---|---|
import Felgo import QtLocation import QtQuick App { NavigationStack { AppPage { title: "Map Overlay" // show the map AppMap { anchors.fill: parent plugin: Plugin { name: "maplibregl" // configure your styles and other parameters here parameters: [ PluginParameter { name: "maplibregl.mapping.additional_style_urls" value: "https://api.maptiler.com/maps/streets/style.json?key=get_your_own_OpIi9ZULNHzrESv6T2vL" } ] } MapQuickItem { // overlay will be placed at the map center coordinate: parent.center // the anchor point specifies the point of the sourceItem that will be placed at the given coordinate anchorPoint: Qt.point(sourceItem.width/2, sourceItem.height/2) // source item holds the actual item that is displayed on the map sourceItem: Rectangle { width: dp(150) height: dp(50) color: "white" AppText { text: "This is a marker!" anchors.centerIn: parent } } } // MapQuickItem } // AppMap } } }
The AppMap already comes with a built-in feature to detect and display the user location. Set the AppMap::showUserPosition property to
true
to enable this feature. The app then tries to detect the user location and displays a marker on the map if possible. The position can only be displayed if the device is capable of getting a position from
either GPS or other position sources.
Note: To use the map features, make sure that the Qt Location components are installed. In case you are missing the module, use the MaintenanceTool in you
Felgo installation folder to add it. Also include QT += positioning location
to your project configuration in the *.pro file of your project.
Note: Also make sure to add required configuration settings to AndroidManifest.xml
on Android or the Project-Info.plist
on iOS so your app may access gps location on the device.
The following example detects and displays the user location:
iOS | Android |
---|---|
import Felgo import QtLocation import QtQuick App { NavigationStack { AppPage { title: "User Position" // show the map AppMap { anchors.fill: parent plugin: Plugin { name: "maplibregl" // configure your styles and other parameters here parameters: [ PluginParameter { name: "maplibregl.mapping.additional_style_urls" value: "https://api.maptiler.com/maps/streets/style.json?key=get_your_own_OpIi9ZULNHzrESv6T2vL" } ] } // configure the map to try to display the user's position showUserPosition: true zoomLevel: 13 // check for user position initially when component is created Component.onCompleted: { if(userPositionAvailable) center = userPosition.coordinate } // once we successfully received the location, we zoom to the user position onUserPositionAvailableChanged: { if(userPositionAvailable) zoomToUserPosition() } } } } }
Qt already provides several different QML types to create graphical effects like blur, drop shadow, opacity masks and more. See here for an overview of all effects: Graphical Effects
The following example creates a list page that uses a custom navigation bar background with an image. When the list is scrolled, the background image is blurred using the FastBlur effect:
iOS | Android |
---|---|
import Felgo import QtQuick import Qt5Compat.GraphicalEffects App { NavigationStack { ListPage { id: page title: "Blur Effect" // get the total height of status bar and navigation bar readonly property real barHeight: dp(Theme.navigationBar.height) + Theme.statusBarHeight // navigation bar is 100 percent translucent, the page then also fills the whole screen // this allows us to display a custom navigation bar background for this page navigationBarTranslucency: 1.0 // list view only fills page area below navigation bar listView.anchors.topMargin: barHeight // add twenty dummy items to the list model: 20 delegate: SimpleRow { text: "Item #"+index } // custom navigation bar background that shows an image Rectangle { id: background width: parent.width height: page.barHeight color: Theme.navigationBar.backgroundColor // add the image Image { id: bgImage source: "../assets/felgo-logo.png" anchors.fill: parent fillMode: Image.PreserveAspectCrop // the blur effect displays the image, we set the source image invisible visible: false } // apply blur effect FastBlur { id: blur source: bgImage anchors.fill: bgImage // the strength of the blur is based on the list view scroll position radius: Math.max(0, Math.min(64, page.listView.contentY)) } } // Rectangle }// ListPage } }