Felgo Apps includes components for developing feature-rich, cross-platform apps with a native look and feel to interfaces and user experience patterns.
This document contains code samples, design patterns, and useful information for developers starting to learn Felgo.
Use this guide to build upon your coding knowledge and quickly learn the similarities and differences compared to cross-platform development with Felgo.
Compiling your Felgo app for a specific platform will present you with a native application package. The build process relies on the native build tools of each platform. Similar to any other native app, you can e.g. run the application on your chosen device, or upload the bundle to a store for release.
If you do not yet have Felgo installed or want to learn more about deploying Felgo Apps, see these guides:
Learn to build Qt-based cross platform apps for Desktop, iOS and Android with QML and Felgo! We created a comprehensive online course on Udemy to show you how:
The online course is a perfect fit for both beginners and advanced developers, as it contains many QML best practices and QML architecture tips: QML Tutorial: How to Create Native Cross Platform Apps with Qt and Felgo | Free Udemy Course
Felgo utilizes QML + Javascript as the main coding language and is based on Qt, a well-known and feature-rich C++ framework.
This allows to save up to 90% of code compared to other languages and frameworks. It is also a cross-platform tool. This means you can compile your application for different platforms using the same code base. In this area, Felgo supports iOS, Android and even Desktop platforms and embedded systems.
One of the biggest advantages of Felgo lies in the way you can compose your UI views, create layouts and add animations. This is where the power of QML comes into action. Felgo moves the view composition and widget rendering away from the platform directly into your app. Instead of heavy-weight and hard to animate widgets that are rendered by the platform, you can create your UI with easily extensible and customizable items.
Building your UI this way saves code, makes it easier to maintain and feels natural. It also follows the trend to move away from 'cookie cutter' apps that were common a few years ago. Modern apps focus more on custom designs that follow your brand and provide a rich user experience.
The simplicity of QML speeds up development, but the customizability and extensibility make it so powerful. You have full control over your UI items and layout while taking advantage of theme-able UI components that match the native user experience of iOS or Android widgets. Combined with ready-to-use native features, backend services and the power of C++ Qt, Felgo is the perfect choice for any app use-case.
It is also possible to put your existing iOS or Android coding knowledge to use and extend Felgo with custom native integrations if needed. Felgo already offers many cross-platform plugins that integrate native frameworks and communicate with iOS or Android.
For an overview of how to tackle different tasks in Felgo, we've summed up the most important topics and questions for you:
For Felgo, everything that you see on the screen is a QML Item. The Item type comes with everything you need to show, layout and compose UI elements of your app.
Based on your UI code that consists of QML Items, the Felgo application builds a tree of Item instances. You do not require to handle when to redraw those Items. Whenever Items change their visual state, the QML engine updates the UI automatically.
The QML rendering engine is a highly-optimized piece of C++ code, which renders your QML UI within a native view in your app. Lightweight, fast and easy to work with. The Felgo App Components offer many different items that incorporate the native iOS or Android design guidelines. With these types, you can use the power of QML to create a native-styled app. Run the app on iOS or Android, and the components will change their look to use native styles and gestures instead - without touching the code.
iOS | Android |
---|---|
Every Felgo app begins with a single App component defined as the root of the QML tree. The following example shows a content AppPage with a centered text item:
import Felgo App { AppPage { AppText { text: "Hello World" anchors.centerIn: parent } } }
This is a question that you no longer need to worry about. The QML Items update automatically if required. Furthermore, whenever properties of an Item change, other elements get notified. This is possible with the power of property bindings:
import Felgo import QtQuick App { // property for counting # of clicks property int clickCount: 0 // main content page with a text AppPage { // text, anchored to the page center AppText { text: "You clicked " + clickCount + " times!" // property binding! anchors.centerIn: parent } // handle touch/click on full page MouseArea { anchors.fill: parent onClicked: clickCount = clickCount + 1 // AppText updates automatically } } }
These few lines of QML code present us with a full-screen AppPage that comes with a white background. It contains a text, which is centered and prints the value of the App's
clickCount
property. A touch anywhere on the Page is handled by the MouseArea, which increases the counter.
Due to the property binding feature, each change of the clickCount
property triggers a new evaluation of the AppText's text
property. This is because the
bound expression relies on the clickCount
property. The AppText then updates accordingly and shows the new text value.
In Felgo, you use QML to layout your app. By nesting QML items you can compose UI elements on the fly. For example, to show a text and a button on a page:
iOS | Android |
---|---|
import Felgo import QtQuick // App is the root item and represents the app window App { // NavigationStack can hold pages includes a navigation bar NavigationStack { // the main page with a title and content items AppPage { title: "Sample App" // centered text item AppText { text: "I am centered." anchors.centerIn: parent } // button at the bottom AppButton { text: "I am centered at the bottom." onClicked: console.log("Button clicked!") anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: parent.bottom } } } }
Every Felgo app starts with a single App component defined as the root of the QML tree. Every app then usually includes a navigation item and some AppPage items for your content views.
If you don't mind keeping the item instance in the QML tree alive, you can control the visibility of each item as you like. Items thus do not require to be re-created and can keep their state while being hidden.
This example toggles between two items with an AppButton:
import Felgo App { AppPage { AppText { id: appTextItem text: "Some Text" anchors.centerIn: parent } AppButton { text: "A Button" visible: !appTextItem.visible // hide button if text is visible and vice versa onClicked: console.log("Button clicked!") anchors.centerIn: parent } AppButton { anchors.bottom: parent.bottom flat: false text: "Toggle" onClicked: appTextItem.visible = !appTextItem.visible // show/hide text } } }
Felgo offers an extensive animation framework with the Animation QML components. It is possible to animate every basic property of your UI elements. You can even define behaviors to automatically animate properties whenever they change.
The following example allows to fade a text by animating its opacity
:
import Felgo import QtQuick App { AppPage { // button at the bottom allows to toggle the text AppButton { anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: parent.bottom text: "Toggle Text Item" onClicked: textItem.opacity = textItem.visible ? 0 : 1 // toggle textItem visibility } // centered text which fades when opacity changes AppText { id: textItem anchors.centerIn: parent text: "Hello World!" visible: opacity != 0 // also set invisible when fully transparent // when opacity changes ... Behavior on opacity { NumberAnimation { duration: 500 } // ... animate to reach new value within 500ms } } } }
Here is another example of how you can create complex animations. For this, you can use the SequentialAnimation and ParallelAnimation types to nest your animations. You can combine those types in any order.
import Felgo import QtQuick App { NavigationStack { AppPage { title: "Complex Animation" AppButton { anchors.centerIn: parent text: "Move!" flat: false onClicked: { // Start the animation by calling start() of the top-most item complexAnimation.start() } } Rectangle { id: rectangle width: dp(100) height: dp(100) color: "black" } // All child animations will be executed one after the other SequentialAnimation { // You can start the whole animation by calling start() of the top-most item id: complexAnimation // 1. Move to right NumberAnimation { target: rectangle property: "x" to: dp(200) duration: 1000 } // 2. Move down and change color // All child animations will be executed in parallel ParallelAnimation { NumberAnimation { target: rectangle property: "y" to: dp(400) duration: 1000 easing.type: Easing.InOutQuad } ColorAnimation { target: rectangle property: "color" to: "blue" duration: 1000 } } // 3. Move left, change color and rotate // All child animations will be executed in parallel ParallelAnimation { NumberAnimation { target: rectangle property: "x" to: 0 duration: 1000 } ColorAnimation { target: rectangle property: "color" to: "black" duration: 1000 } NumberAnimation { target: rectangle property: "rotation" from: 0 to: 360 duration: 1000 } } // 4. Move up NumberAnimation { target: rectangle property: "y" to: 0 duration: 1000 easing.type: Easing.InBack } } } } }
You can find even more animation examples on this page: Add Animations to your App
Felgo and QML offer the Canvas item that allows drawing straight and curved lines, simple and complex shapes, graphs, and referenced graphic images. It can also add text, colors, shadows, gradients, and patterns, and do low-level pixel operations.
The width and height properties of the Canvas item define the drawing area. If it requires rendering, the paint signal fires. When handling the signal, you can draw to the Canvas using a Context2D object:
import Felgo import QtQuick App { Canvas { id: mycanvas width: 100 height: 200 onPaint: { var ctx = getContext("2d"); ctx.fillStyle = Qt.rgba(1, 0, 0, 1); ctx.fillRect(0, 0, width, height); } } }
Each .qml
file of your project defines a reusable QML Component. The specified root QML Item determines the base component, which your new item extends. The new QML type can then add properties, signals, and
functions, which form the public interface of the component.
To define the layout for the item, add some child elements and position them in the view. Each child element may also be extended with new features and properties directly where it is used. You do not require to create an own QML Item to make small additions to a single item instance in your UI.
The following example shows a clickable CustomButton.qml
component with a background and text. Once imported, you can use the new type in your app:
import Felgo import "relative-path-to-directory-of-custom-button" App { CustomButton { anchors.centerIn: parent // new count property only for this button instance property int count: 0 text: "You clicked "+count+" times." backgroundColor: "yellow" onClicked: count++ } }
import QtQuick // CustomButton extends Item, which is the base type for all UI elements Item { // the default (implicit) size is based on the size of the child text element // the width and height thus depend on the actual text value implicitWidth: textItem.implicitWidth implicitHeight: textItem.implicitHeight // public properties and signals property color backgroundColor: "green" property string text: "Click Me!" signal clicked() // child elements Rectangle { anchors.fill: parent color: parent.backgroundColor } Text { id: textItem // id for referencing this text item within the component text: parent.text anchors.centerIn: parent } // handle touch/click and fire signal when the click happens MouseArea { anchors.fill: parent onClicked: parent.clicked() // emits clicked signal of CustomButton } }
You can rely on the App Navigation components to switch between different Pages in your app:
The most basic navigation model is a page stack which can push and pop page items. Usually a page stack makes use of a navigation bar for navigating back and forth between pages.
For that purpose, Felgo provides an item called NavigationStack.
import Felgo App { NavigationStack { AppPage { title: "Main Page" AppText { text: "Hello World" anchors.centerIn: parent } } } }
This will look like the following on iOS and Android:
iOS | Android |
---|---|
To add new pages to the stack you call NavigationStack::push(), providing the new page as a parameter. To navigate back to a previous page your users can either use the back button in the provided navigation bar or you can manually pop back in the stack by calling NavigationStack::pop().
For passing data between pages, the easiest solution is to make relevant settings or properties available in a common parent scope. Public properties, functions, and signals of an ancestor in the QML tree are available for direct access:
import Felgo import QtQuick App { id: app property int count: 0 // main page NavigationStack { AppPage { id: mainPage title: "Main" Column { anchors.centerIn: parent // text to show the current count and button to push the second page AppText { anchors.horizontalCenter: parent.horizontalCenter text: "Count " + app.count } AppButton { text: "Push Counter Page" onClicked: mainPage.navigationStack.push(counterPageComponent) } } } } // inline-definition of a component, which is later pushed on the stack Component { id: counterPageComponent AppPage { title: "Change Count" property AppPage target: null Column { anchors.centerIn: parent // buttons to increase or decrease the count, which is displayed on the main page AppButton { text: "Count ++" onClicked: { app.count++ } } AppButton { text: "Count --" onClicked: { app.count-- } } } } } }
Apart from the NavigationStack, which which allows to navigate back and forth between pages, the Navigation type is the main component to create your app menu:
import Felgo App { Navigation { // enable both tabs and drawer for this demo // by default, tabs are shown on iOS and a drawer on Android navigationMode: navigationModeTabsAndDrawer NavigationItem { title: "Home" iconType: IconType.home NavigationStack { AppPage { title: "Main Page" } } } NavigationItem { title: "Lists" iconType: IconType.list NavigationStack { AppPage { title: "Lists" } } } } }
This example shows how to add two root navigation options to your app. On iOS the Navigation item displays an iOS-style tab bar on the bottom of the screen. On Android the navigation options are provided within a navigation drawer, sliding in from the left edge of the screen.
As you can see in the example it's also possible to stack different navigation concepts into each other. The example shows two tabs as the main navigation concept with an included NavigationStack each.
For more information about the Navigation item, read the full documentation here.
In iOS, to send the user to another application, you use a specific URL scheme. You can use those URL schemes with Felgo with the NativeUtils::openApp() or NativeUtils::openUrl() methods.
As Felgo relies on QML and Javascript, updates to the UI are already handled in a highly asynchronous and event-driven way. You weave-in your Javascript code as part of signal handlers that execute when certain events or user interactions happen. The QML rendering engine is highly optimized, so you do not need to worry about blocking the UI when adding your view logic.
Communication with your application backend is also easy, as you can use the asynchronous HttpRequest.
This example fetches a JSON response containing the URL to an image. The request happens asynchronously. The returned URL is then set as the AppImage source, which is able to asynchronously load an image from an URL:
import Felgo import QtQuick App { property string serverUrl: "https://jsonplaceholder.typicode.com/photos/1" property var jsonData: undefined // handler function to be executed when the App Item is fully created, starts web requests Component.onCompleted: { // the 3rd parameter of open(...) is the asynchronous flag var request = HttpRequest .get(serverUrl) .then(function(res) { jsonData = res.body // keep JSON result }) .catch(function(err) { console.log(err.message) console.log(err.response) }); } AppPage { // just some spinning icon to show asynchronous loading of image AppIcon { anchors.centerIn: parent iconType: IconType.refresh NumberAnimation on rotation { loops: Animation.Infinite from: 0 to: 360 duration: 1000 } } AppImage { // expression for source relies on the jsonData property (property binding!) // the REST api returns the web url to an image, which we can set directly to load the image source: jsonData !== undefined ? jsonData.url : "" anchors.fill: parent fillMode: AppImage.PreserveAspectFit // additionally you can make the image loading itself asynchronous // this is recommended for images loaded from web source asynchronous: true } } }
After request completion, the result is stored to the jsonData
property. The expression for the image source relies upon on the property, so the AppImage will automatically
update and show the image as soon as the data arrives - nothing more to do.
REST and RESTful web services are the most common way to access data through the Internet. In addition to the Felgo HttpRequest type of the above example, you can also use the XMLHttpRequest object, which QML supports by default. This article gives detailed insights on how to create an App and connect to a REST service with Felgo using XmlHttpRequest: How to Access REST Services: Weather Service Example App
However, we recommended to connect to REST services with the new Felgo HttpRequest type, as it offers more features and an easier API.
For long-running and data-intense operations, it still makes sense to actively use a separate thread and notify your UI once the work is done. To quickly spawn a new thread in QML, you can add a WorkerScript item:
import Felgo import QtQuick App { AppText { id: myText text: 'Click anywhere' } WorkerScript { id: myWorker source: "script.js" // script to run on new thread onMessage: msg => myText.text = msg.reply } MouseArea { anchors.fill: parent onClicked: myWorker.sendMessage({ 'x': mouse.x, 'y': mouse.y }) } }
The above worker script specifies a JavaScript file, script.js
, which is handled in a new thread. Here is an example script.js, which simply returns the passed mouse position:
WorkerScript.onMessage = function(message) { // ... long-running operations and calculations are done here WorkerScript.sendMessage({ 'reply': 'Mouse is at ' + message.x + ',' + message.y }) }
For more control over your threads and the best possible performance, you also have the full power of Qt C++ at your hands. You can find a guide how to mix Felgo QML code with Qt C++ components here: How to Expose a Qt C++ Class with Signals and Slots to QML.
Stepping into the C++ world also gives access to e.g. advanced networking features like a socket or Bluetooth connection.
The demo is also available with the Felgo SDK here: <Path to Felgo>/Examples/Felgo/appdemos/cpp-qml-integration
Felgo projects come with an assets
folder by default. To bundle the folder content along with your application, the CMakeLists.txt
project configuration includes a setup like:
file(GLOB_RECURSE AssetsFiles RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} assets/*)
deploy_resources("${AssetsFiles}")
Assets are any type of file, not just images. For example, you might also have a JSON file located in the assets folder.
In addition, your project also holds platform directories, like the android
or ios
folder. They handle platform-specific configurations like the android/AndroidManifest.xml
,
ios/Project-Info.plist
file or your app icon configuration.
For images, Felgo follows a simple density-based format. The MultiResolutionImage component automatically chooses the best image version for each device's screen.
By default, Felgo allows you to provide three different images, for example:
This code snippet selects the best version of the above image automatically:
import Felgo App { MultiResolutionImage { source: Qt.resolvedUrl("../assets/imageSwitching.png") // relative path from QML file to image // for the size, we specify density independent pixels width: dp(50) height: dp(20) } }
To specify density-independent sizes for your UI elements, you can rely on the App::dp() function. Many of the Felgo Apps Components are already set up to have the same physical size on all screens - regardless of their density.
For more information about density-independence in Felgo, see Supporting Multiple Screen Sizes & Screen Densities with Felgo Apps.
Felgo is based on the Qt platform, so you can rely on Qt Linguist tools for your translations. Adding translations to your app then requires these three steps:
qsTr()
function to mark texts for translation in your QML code.See here for a full guide about internationalization and string translation: Internationalization with Felgo
For iOS, Felgo creates a native iOS IPA app file using the same iOS build tools that you already know. To configure the iOS app and build, you have access to a generated Xcode
project file that you can configure
to your needs. To configure the Android build, you have access to the AndroidManifest.xml
and build.gradle
in the android
folder of your project.
When working with Felgo Plugins for native features, only a single dependency per plugin includes all required frameworks. See the plugin documentation page for more information about plugin integration.
There are many different layouting mechanisms and components available with Felgo. For a short introduction, please see Create Layouts and Position Components in Your App.
The App::portrait or App::landscape properties allow to optimize your layout for different orientations. Similar other properties in QML, you can add handlers for property changes or use property bindings to adapt the UI automatically.
import Felgo App { id: app onPortraitChanged: { if(portrait) console.log("orientation changed to portrait") else console.log("orientation changed to landscape") } AppText { text: app.landscape ? "Landscape Mode" : "Portrait Mode" anchors.centerIn: parent } }
You can also manually change the orientation settings at runtime, e.g. like this:
import Felgo import QtQuick App { NavigationStack { AppPage { title: "Orientation Lock" Column { AppButton { text: "Lock to landscape" onClicked: NativeUtils.preferredScreenOrientation = NativeUtils.ScreenOrientationLandscape } AppButton { text: "Remove orientation lock" // Resets to the orientation defined in AndroidManifest.xml / Project-Info.plist onClicked: NativeUtils.preferredScreenOrientation = NativeUtils.ScreenOrientationDefault } } } } }
If an item already supports user interaction, you can directly handle the events in your code. The following example adds an AppButton and handles the clicked
signal:
import Felgo App { AppButton { text: "Click Me!" anchors.centerIn: parent onClicked: text = "Thanks!" } }
To handle clicks for a custom Item, you can add a MouseArea:
import Felgo import QtQuick App { Rectangle { width: dp(200) height: dp(200) color: "lightyellow" anchors.centerIn: parent MouseArea { anchors.fill: parent onClicked: parent.color = "lightgreen" } } }
You can work with different MouseArea signals to adapt it for your use-case. However, the MouseArea can only handle a single touch point. For more information on gestures and handling multiple touch points, please see: Handle Touch and Gestures in Your App.
The easiest way to create a list is to use the AppListView type together with a SimpleRow delegate for the list cells:
Android | iOS |
---|---|
import Felgo App { NavigationStack { AppPage { title: "Basic List Example" AppListView { delegate: SimpleRow {} model: [ { text: "Apple", detailText: "A delicious fruit with round shape", iconType: IconType.apple }, { text: "Beer", detailText: "A delicous drink", iconType: IconType.beer } ] } } } }
The model-delegate system also supports JSON data structures for your data model. This makes it easy to work with and display data from REST APIs.
The SimpleRow::selected signal allows to handle click events on a list cell. For custom delegate items, you can add your own MouseArea for handling touch input.
import Felgo App { NavigationStack { AppPage { title: "Clickable List Example" AppListView { delegate: SimpleRow { onSelected: console.log("Clicked Item #"+index+": "+JSON.stringify(modelData)) } model: [ { text: "Apple", detailText: "A delicious fruit with round shape", iconType: IconType.apple }, { text: "Beer", detailText: "A delicous drink", iconType: IconType.beer } ] } } } }
The list view updates its UI automatically whenever the model changes - at least when you describe your model as a ListModel type. For plain JSON-based models, the list is not notified when some data values within the JSON structure change. In this case, you can manually trigger a property-changed signal, which in turn updates the UI of the list:
import Felgo App { NavigationStack { AppPage { id: page title: "Append List Item Example" property var listData: [ { text: "Apple", detailText: "A delicious fruit with round shape", iconType: IconType.apple }, { text: "Beer", detailText: "A delicous drink", iconType: IconType.beer } ] AppListView { id: listView model: page.listData delegate: SimpleRow { onSelected: { page.listData.push(modelData) // add copy of clicked element at end of model page.listDataChanged() // signal change of data to update the list } } } } } }
More list examples are available here: Use ScrollViews and ListViews in Your App
Every Felgo application comes with a set of default styles, depending on the underlying platform. As seen above, a simple app with a navigation stack looks like the following on iOS and Android:
iOS | Android |
---|---|
Usually you want an app's style to match your app or company CI, or just want to add some color to it. For that purpose Felgo provides a global Theme object which makes it easy to change styling for all components within your app.
The root component of all apps, the App component, offers a signal handler which allows overriding the default styles. Let's have a look at some examples:
A lot of Felgo Apps components use a special color reference, called the tint color, for their main outline color. You can change that color by overriding the Theme's tintColor property:
import Felgo App { onInitTheme: { Theme.colors.tintColor = "red" } AppPage { AppSwitch { anchors.centerIn: parent } } }
This example sets the global tint color to red
, which is then also applied to the AppSwitch component.
This will look like the following on iOS and Android:
iOS | Android |
---|---|
If you use the NavigationStack component you can style the included navigation bar in the following manner:
import Felgo App { onInitTheme: { Theme.navigationBar.titleColor = "white" Theme.navigationBar.backgroundColor = "#57adee" // blue color Theme.colors.statusBarStyle = Theme.colors.statusBarStyleWhite } NavigationStack { AppPage { title: "Main Page" AppText { text: "Hello World" anchors.centerIn: parent } } } }
The resulting app includes a blue navigation bar with white title text and a white status bar on iOS.
This will look like the following on iOS and Android:
iOS | Android |
---|---|
The default Theme settings implement platform-specific styles, like the default iOS style on iOS or Material Style on Android, for a native look and feel on both platforms. The following example shows how to manually change the used platform theme in your code:
import Felgo App { Navigation { NavigationItem { title: "Main" iconType: IconType.heart NavigationStack { AppPage { title: "Main Page" AppButton { text: "Switch Theme" onClicked: Theme.platform = Theme.isAndroid ? "ios" : "android" anchors.centerIn: parent } } } } NavigationItem { title: "Second" iconType: IconType.thlarge NavigationStack { AppPage { title: "Second Page" } } } } }
Felgo allows you to style your app with many app-wide theme settings. This is in most cases more convenient than styling every single component itself. You can see Style Your App with Themes for some simple theming examples.
Felgo components make use of two different fonts, the Theme::normalFont and the Theme::boldFont. By default these fonts match the platform's default font for Felgo Apps components.
If you want to explicitly provide your own font you can override the Theme properties with a FontLoader object:
import Felgo import QtQuick App { onInitTheme: { Theme.normalFont = arialFont } FontLoader { id: arialFont source: "fonts/Arial.ttf" // loaded from your assets folder } AppPage { AppText { text: "I'm in Arial" } } }
Make sure to add your custom fonts to your app's assets and to provide the correct path in the FontLoader object.
You can then also use the custom font in your own app components, like the following example:
Text { // Set reference to the global app font font.family: Theme.normalFont.name text: "Custom text item" }
Instead of replacing the general Theme font, you can also use a FontLoader and only overwrite the font.family
property for certain text
items.
Along with the font, you can customize many other styling properties of a Text item, such as:
Input controls like AppTextInput or AppTextField own a placeHolderText property, which you can use for your hint text.
import Felgo import QtQuick App { AppPage { // remove focus from textedit if background is clicked MouseArea { anchors.fill: parent onClicked: textEdit.focus = false } // background for input Rectangle { anchors.fill: textEdit anchors.margins: -dp(8) color: "lightgrey" } // input AppTextInput { id: textEdit width: dp(200) placeholderText: "What's your name?" anchors.centerIn: parent } } }
The text input components provide predefined validators that you can use, they are called inputMethodHints.
You can also add custom validators to restrict the accepted input to a certain input type or expression. Input that does not match the validator is not accepted. To do custom validations and show errors for accepted input, you can add simple checks and control the visibility of errors with property bindings:
import Felgo import QtQuick App { // background for input Rectangle { anchors.fill: textInput anchors.margins: -dp(8) color: "lightgrey" } // input AppTextInput { id: textInput width: dp(200) placeholderText: "What's your name?" anchors.centerIn: parent // only allow letters and check length validator: RegularExpressionValidator { regularExpression: /[A-Za-z]+/ } property bool isTooLong: textInput.text.length >= 6 } // show error if too long AppText { text: "Error: Use less than 6 letters." color: "red" anchors.top: textInput.bottom anchors.topMargin: dp(16) anchors.left: textInput.left visible: textInput.isTooLong } }
Felgo allows you to access various device sensors, with the Qt Sensors QML Types. The components for Positioning, Maps and Navigation are found in the Qt Positioning and Qt Location modules.
For an easy and convenient way to show a map with the current user position, you can also rely on the AppMap type:
import Felgo import QtLocation import QtQuick App { // show the map AppMap { anchors.fill: parent // configure map provider 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 the 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() } } }
The Felgo NativeUtils allow to conveniently trigger native device features from within your QML code. For example to show dialogs, open the camera, access the gallery or work with contacts.
This simple example allows to take a photo with the device camera:
import Felgo import QtQuick App { AppImage { id: image anchors.fill: parent fillMode: AppImage.PreserveAspectFit autoTransform: true } AppButton { text: "Take Photo" anchors.centerIn: parent onClicked: NativeUtils.displayCameraPicker() } Connections { target: NativeUtils onCameraPickerFinished: (accepted, path) => { image.source = "" if(accepted) { image.source = path } } } }
To embed a camera view directly in your app, you can use the Camera and VideoOutput types:
import Felgo import QtQuick import QtMultimedia App { CaptureSession { camera: Camera { active: true } videoOutput: videoOutput } VideoOutput { id: videoOutput anchors.fill: parent fillMode: VideoOutput.PreserveAspectCrop } }
Felgo supports both server-triggered and local notifications with Felgo Plugins. All plugins offer a platform-agnostic API and rely on native frameworks for each platform.
See the GoogleCloudMessaging Plugin or the OneSignal Plugin to add server-triggered notifications. Local notifications are available with the Notification Plugin. The following example schedules a notification to be fired after 5 seconds and trigger the notificationFired signal. You can also try to put the app in the background, to get the notification on your home screen. In this case, the notificationFired signal will be called when you enter the app after clicking on the notification.
import QtQuick import Felgo App { NotificationManager { id: notificationManager onNotificationFired: notificationId => { NativeUtils.displayMessageBox("Notification", "id: "+notificationId, 1) } } Notification { id: idleNotification notificationId: "idleNotification" message: "Anyone here? Haven't seen you in a while..." timeInterval: 5 // in seconds } AppButton { text: "Schedule Notification (5s)" anchors.centerIn: parent onClicked: { // Cancel old notification if scheduled notificationManager.cancelNotification(idleNotification.notificationId) // Schedule idleNotification notificationManager.scheduleNotification(idleNotification) } } }
On iOS and Android, you can use the native Keychain to store, read and delete data securely and persistently.
On iOS, each stored value is handled as a separate keychain item representing a generic password. On Android, each value is persisted in SharedPreferences
with AES-256 encryption. The automatically generated
encryption key is securely stored via the Android KeyStore system.
Here is an example how to use the Keychain storage functionality using the Felgo NativeUtils:
import Felgo import QtQuick App { AppButton { text: "Store in Keychain" onClicked: NativeUtils.setKeychainValue("identifier", "value") } AppButton { text: "Read Keychain" onClicked: NativeUtils.getKeychainValue("identifier") } AppButton { text: "Delete from Keychain" onClicked: NativeUtils.clearKeychainValue("identifier") } }
Felgo allows integrating Facebook with the Facebook Plugin. See the plugin documentation for more information and detailed integration steps.
The Firebase Plugin is the right tool for integrating Firebase Authentication, Real-time Database or Cloud Storage features. See the plugin page for more information and detailed integration steps.
The Firebase Plugin has seen lots of improvements and new features lately. In case you are missing anything or run into issues, don't hesitate to get in touch!
In Felgo you code with QML, so going native first means to step into the Qt C++ world. You can find a guide how to mix Felgo QML code with Qt C++ components here: How to Expose a Qt C++ Class with Signals and Slots to QML
The demo is also available with the Felgo SDK: <Path to Felgo>/Examples/Felgo/appdemos/cpp-qml-integration
Working with native Android code from C++ then requires JNI as the bridge between the two languages. Weaving in native iOS code is a little easier, as Objective-C is directly compatible with C++. But before you dive in too deep: Our developers are experts at building such native integrations, and we're happy to add features or build extensions as part of our support package offering!
The App::settings property allows to store simple key/value pairs in a local database. If you format the value as a JSON string, you can also store more complex data in a very easy way.
import Felgo import QtQuick App { id: app // this property holds how often the app was started property int numberAppStarts Component.onCompleted: { // getValue() returns undefined, if no setting for this key is found, so when this is the first start of the app numberAppStarts = app.settings.getValue("numberAppStarts") || 0 // defaults to '0' if 'undefined' is returned numberAppStarts++ app.settings.setValue("numberAppStarts", numberAppStarts) } NavigationStack { AppPage { title: "Settings" AppText { anchors.centerIn: parent text: "App starts: " + numberAppStarts } } } }
The App settings in the above example internally rely on the Storage component. You can also add your own Storage to save your key-value pairs to a distinct database. You can find more info on storage, data and Firebase here: Store Data Persistent
You can access a local SQLite Database using the Qt Quick Local Storage QML Types.
Fast-forward your development with the help of our video tutorials: Felgo Video Tutorials
There's also a comprehensive online course available on Udemy. It contains 44 lectures and more than 4 hours of video course material. The lectures focuse on many important topics for beginners as well as advanced developers: QML Tutorial: How to Create Native Cross Platform Apps with Qt and Felgo | Free Udemy Course
In the documentation navigation on the left-hand side, you can find tons of useful examples grouped into the main use-cases for app developers. Make sure to check them out!
Felgo Apps includes components for developing cross-platform apps with a native look and feel to interfaces and user experience patterns.
In addition to Felgo Apps components, you will probably make use of components from the QtQuick
module provided by the Qt framework itself.