Overview
This is part 6 of a series of demo apps helping you to understand Qt 5.7 for x-platform development on Android and iOS. This app demonstrates using a Drawer to navigate and contains some concepts explained before. Please read the blogs abvout my other Examples before:
Bottom Navigation is good for Apps with 3 – 5 different areas. If your app covers more aspects, then a Drawer is the way to go. Read at Google Material Style Guide about Drawer Navigation and take a look at Qt documentation on Drawer – a Navigation Control from Qt Quick Controls 2.
I tried to implement different functionality into the Drawer:
- Header with Logo and Text
- Items with Icon + Text
- Items with Text-only (typically About, Help, Info)
- Dividers to separate different areas
- Subtitles
- Showing a Counter (per ex. Number of emails, Orders, …)
- Showing a colored Marker
- Highlighting current Selection
See this Screenshot:
As you know, I’m developing crossplatform Business Apps for BlackBerry10, Android, iOS. One of the most important requirements is a fast and smooth workflow through an App which includes short ways for the fingers to tap on buttons. To open a Drawer there are two ways:
- Swipe from the left site to see the destinations
- Tap on Menu Button (top-left)
Both actions are not easy for one-hand-use. That’s why I liked the introduction of Bottom Navigation by Google. HowTo use this with Qt 5.7 we talked about in my previous example app.
Why not combining both ? Drawer and Bottom Navigation ? From Settings you can switch Bottom Navigation for Favorites on:
As you can see I set Home, Car, Flight, Settings as ‘Favorite Destinations’. In this example app this decision was made fix – in a real life app the user should select favorites. Please note, that the left most Button is the Menu Button to open the Drawer.
Now it’s easy to open most-used Buttons or the Drawer from Menu at Bottom Navigation.
Favorites as Bottom Navigation will only be there if APP is in Portrait Mode.
Coming from BlackBerry 10 Cascades ? This looks similar to TabbedPane, where Tabs are shown on ActionBar. Makes it easy to have consistant workflow x-platform.
From Settings you also can Hide the TitleBar and decide if active selection should be highlighted.
Navigation Model
Items in Drawer and Buttons in Bottom Navigation are drawn by Repeater from Navigation Model, which is an array of:
[ { "type": "../navigation/DrawerNavigationButton.qml", "name": "Home", "icon": "home.png", "source": "../navigation/HomeNavigation.qml", "showCounter":false, "showMarker":false, "a_p":1}, {...} ]
‘type’ can be:
- DrawerNavigationButton.qml
- DrawerDivider.qml
- DrawerSubtitle.qml
‘name’, ‘icon’, ‘source’ are used to generate NavigationButtons
‘showCounter’, ‘showMarker’ adds this behavior to a NavigationButton
‘a_p’ means activationPolicy
From this NavigationModel the Drawer can be created easy. If Bottom navigation is in-use, same Model is used from a mapping:
property var favoritesModel: [ 0, 3, 7, 9 ]
Be Dynamic (ActivationPolicy)
Coming from BlackBerry10 Cascades ? TabbedPane is similar to Drawer and TabbedPane has a TabDelegateActivationPolicy for dynamic loading of Tabs. I implemented similar policies for Draqwer Navigation.
I’m using Loaders to create Destinations. There are three Activation Policies:
- IMMEDIATELY
- LAZY
- WHILE_CURRENT
IMMEDIATELY: .active = true for the Loader, so the Item was always loaded at the beginning and stays loaded.
LAZY: .active = false for the Loader. First time the Item inside the Loader will be used, activate is set to true and will stay true. LAZY is good for performance at the beginning and will be used for items user normaly will access meny times.
WHILE_CURRENT: .active = false for the Loader. .active will only set to true WHILE the Item is the currentItem. If user switches to another node, the Loader will set .active = false. Next time same node is used, Item will created again. This option is good for memory-hungry complex Pages, the User normaly only will use seldom. Attention: As soon as the Item was unloaded, you lost your state. If you want to remember last sate, you must store the values anywhere.
For this Example APP here are the Activation Policies used to demonstrate how it’s done:
- IMMEDIATELY: Home Page, Flight Page
- LAZY: Car, Bus, Truck, Color Schema Pages
- WHILE_CURRENT: Subway Page, Settings, About
Starting the App, all Destinations will be created from Repeater:
Repeater { id: destinations model: navigationModel // Destination encapsulates Loader // depends from activationPolicy how to load dynamically Destination { id: destinationLoader } Component.onCompleted: { // all destinations (Loader) created // all destinatation items w activationPolicy IMMEDIATELY activated // now show first destination (should always be IMMEDIATELY) rootPane.activateDestination(0) } }
All marked as IMMEDIATELY will become active and loaded. When all is done, the first one will replace the initial item on StackView. Drawer is using a StackView where the root Item always will be replaced by the Item selected from Drawer or Bottom Navigation. In a real life App perhaps it could take some time before all is created, so there’s an InitialItemPage showing a BusyIndicator.
Chances are great that you never will see this Page from Example App, because it’s only a demo app with less logic or data-loading behind.
Here’s the Destination.qml (Loader):
Loader { id: pageLoader property int pageActivationPolicy: modelData.a_p active: pageActivationPolicy == activationPolicy.IMMEDIATELY visible: false source: modelData.source onLoaded: { item.init() if(pageActivationPolicy != activationPolicy.IMMEDIATELY) { rootPane.replaceDestination(pageLoader) } } }
There are two functions – activateDestinations() and replaceDestination() – managing the load / unload and refresh() on StackView. You can study this from sources.
Pages, StackView, SwipeView / TabBar
It depends from your use-cases what happens if User selects a destination. You can use a Page if there’s only one Page needed to show the content. You can use a SwipeView with TabBar and/or PageIndicator to provide sub-navigation as done for the ColorSchema where you can swipe through 3 Pages to select Primary Color, Accent Color and Theme or you can use a StackView.
The concept is the same as already described in Bottom Navigation APP – Activation Policies are first time used here in this App.
If using some nodes with StackViews where the User wants to jump between and expects to get the same leaf, don’t use Activation Policy WHILE_SELECTED.
Take a look at the sources to see How I implemented all this stuff. As in previous Examples there’s a init() function and cleanup() function to simulate some biz logic.
Coming from BlackBerry10 Cascades ? Here’s (from a 4-years-old Blog 😉 How a typical Cascades App can be structured:
Compare this with a Qt 5.7 App designed with Material Style for Android and iOS as done for this Example, you’ll recognize a similar architecture. This is great for x-platform app development and why I prefer to use Qt 5.7 for Android / iOS Apps: much stuff from C++ is same, Event handling using SIGNAL / SLOT is similar and also UI Architecture as we have seen now.
Providing same Workflow for mobile Business Apps for all target Platforms is important – esp. in Enterprise.
Sources
as usual at Github: https://github.com/ekke/drawer_nav_x
Summary
You learned HowTo use Qt Quick Controls 2 to implement Drawer Navigation.
Stay tuned for the next Example Apps …
← Back (Bottom Navigation App)
→ Next Article (Business Data App)
⇐ Home (Overview / Topics)
“You can use a StackView with TabBar and/or PageIndicator to provide sub-navigation as done for the ColorSchema where you can swipe through 3 Pages to select Primary Color, Accent Color and Theme or you can use a StackView.”
It think this should be: “You can use a SwipeView with …”
thx. you’re right – a typo
Besides this, I would like to thank you for your great Blog and tutorials. I did learn a lot from you! Thank you!
Hi ekke, thank you for your great work on this blog. I found a bug on Android: When I open the Navigation-Drawer and tap on the Android BACK Key closes the APP. But this only happens if the Android BACK key has not been used elsewhere
yep.
in the new apps I published the back key should work better. have not added this to the older ones. Qt World Summit Conf App should contain newest.
Thank you for your fast answer. In your “Qt World Summit Conf App” from Google Play Store the back-key-button works properly, but when I test the source code from your git, then the back-key doesn’t work.
just tried. open some pages on stack, go back with Android BACK key. if root is reached, I’m opening the drawer to make it easy to jump to another part of the app.
user must use android buttons to minimze and close. I’ll also add a ‘quit app’ as last entry in drawer.
Normally, I use the system-back-key to close the drawer. In the Qt World Summit Conf App from Google Play this works properly (the drawer closing), but when I compile the source code and try to test it on my device, then the app is closed instead of the drawer.
curious. yesterday compiled, built, deployed to Android 6 device and works as expected. Are you using Qt 5.8 ?
Yes, I’m using Qt 5.8
QtWS2016 – Open – Schedule – Tap on Session – Tap on Room
BACK: Session Details
BACK: schedule
BACK: Opens Drawer
BACK: Closes Drawer
—-
My System: Qt 5.8, Qt Creator 4.2.1, android-ndk-r10e
same for me.
and your device and android OS ?
Android 7.0 on Samsung Galaxy S7; Android 6.0 on HTC Desire 816
tested on BlackBerry Android DTEK50, Android 6.0.1
later will test on Samsung (6.0.1) and Google (7.0) devices
Thank you for your help
thx for your help. it’s always my goal to provide apps with no issues.
esp as next weeks work starts on QtWS2017 APP with some more features 😉
@App-Monkey: This is not a bug in ekke’s source code. Drawer inherits from Popup, see QTBUG-59670
yep – fortunately the bug is fixed by J-P and will be in 5.9
Ah okay, thank you
QtWS2016 – Open – Schedule -Tap on Session -Tap on Room
BACK-Key: Session Details
BACK-Key: Schedule
BACK-Key: Opens Drawer
BACK: Closes Drawer
QtWS2016 – Open – Open Drawer
BACK-Key: Closes App
QtWS2016 from Play store – Open – Open Drawer
BACK-Key: Closes Drawer
BACK-Key: Popup (No more Pages)
Can you please tell me how you intercepted the android-back-key to close the drawer in the QtWS2016 (PlayStore version)?
Example:
QtWS2016 – Open – Open Drawer
BACK-Key: close Drawer <—- this one
take a look at main.qml line 338
sorry – have no time this week to help more – heavy work on customer projects 😉
Ok, thank you! Have fun
Hallo Ekke, erstmal vielen herzlichen Dank für deine arbeit an diesem Blog, durch den ich sehr viel über qt/qml gelernt habe. Viele Grüße aus Rosenheim, Manuel
das freut mich doch. …und aus Rosenheim 🙂