Access navigation pane from stacked page - blackberry

I'm new to Blackberry Cascades and I want to create a simple NavigationPane. I'm very used to Qt's StackView which allouse you to push and pop pages to and from the stack. However, Qt's [StackView][1] has this attached property called Stack.view which allows you to access the stack from any child page.
How can I do this in Blackberry's Cascades? Is it possible to access the NavigationPane stack of pages from the current page you're in? I see this extremely useful.
For example I have this simple project with two qml files each representing one page:
Main page:
// main.qml
import bb.cascades 1.4
NavigationPane {
id: nav
Page {
Container {
ListView {
dataModel: XmlDataModel {
source: "data.xml"
}
onTriggered: {
if (indexPath.length > 1) {
var chosenItem = dataModel.data(indexPath);
var contentpage = itemPageDefinition.createObject();
contentpage.itemPageTitle = chosenItem.name
nav.push(contentpage);
}
}
accessibility.name: "Listing"
}
}
}
attachedObjects: [
ComponentDefinition {
id: itemPageDefinition
source: "ItemPage.qml"
}
]
onPopTransitionEnded: {
page.destroy();
}
}
Secondary page:
// ItemPage.qml
import bb.cascades 1.4
Page {
property alias itemPageTitle: titlebar.title
titleBar: TitleBar {
id: titlebar
}
content: Container {
}
}
Is it possible to access nav or NavigationPane from ItemPage.qml? I would like e.g. to create a new page from ItemPage.qml and push it on the stack. Is it possible to do so?
EDIT:
For example there is this documentation page on the Blackberry developers website which has this example with a main page:
and a secondary page:
As you can see, in the secondary myPage.qml he uses the navigationPane. How is that possible? How does he have access to an ID present in another component?

Related

Jetpack Compose navigation: login screen and different screen with bottom navigation

My goal is to to have a LoginScreen from which I can navigate to an InternalScreen. The InternalScreen should have/be a bottom navigation bar that can navigate to multiple other screens/routes in the internal space.
This is what I imagined my NavGraph was supposed to look like:
- LoginScreen
- internal space
- InternalScreen with BottomNavigation
- some fragment
- some other fragment
My idea was to create a Scaffold with a BottomNavigationBar in the InternalScreen composable but I do not no where to put it in my NavGraph since said NavGraph also has to contain the different routes for the BottomNavigationBar.
How should I approach this? I am sorry if this has already been answered, I couldn't find anything about this particular case.
I think the login screen/flow must be part of the application navigation flow. In summary, your application must react to a isLoggedIn state, which should be global, and in case of the user is not logged in, the login screen must be displayed.
This is what I did:
#Composable
fun MainNavigation(
viewModel: MainViewModel,
navController: NavHostController,
) {
val auth = viewModel.auth
val initialRoute =
if (auth.isLoggedIn()) BooksFeature.route else LoginFeature.route
AnimatedNavHost(
navController,
startDestination = initialRoute
) {
loginGraph(auth, navController)
booksGraph(auth, navController)
settingsGraph(navController)
}
}
The MainNavigation composable is the root of my app (which is called in setContent at MainActivity). Each feature of the app has a navigation graph. Like booksGraph:
fun NavGraphBuilder.booksGraph(
auth: Auth, // this is a simple class which
// knows if the user is logged in
navController: NavHostController
) {
navigation(
route = BooksFeature.route,
startDestination = BooksList.route,
) {
composable("ScreenA") {
ScreenA()
}
...
}
}
In my activity (I'm using just one activity), I'm observing the login state and redirecting to the login screen properly.
private fun launchLoginObserver() {
lifecycleScope.launch(Dispatchers.Main) {
mainViewModel.isLoggedIn.collect { isLoggedInState ->
if (isLoggedInState == false) {
navigationController.navigate(LoginScreen.route) {
popUpTo(0) // reset stack
}
}
}
}
}
If you want to take a look into the full implementation, here is the link for my repository.

SwiftUI - NavigationLink Content loaded too early

I have a basic list in my first View as Follows:
func buildList(sections: [Client]) -> some View {
let list = List {
ForEach(sections) { client in
Section(header: Text(client.name)) {
ForEach(client.projects) { project in
NavigationLink(destination: BuildsRouter.build(forProject: project)) {
HStack {
Text("Test \(project.id)").fontWeight(.ultraLight)
}
}
}
}
}
}
return list
}
I'm using NavigationLink to provide the details view for my Project object.
Thing is, when I make a Memory analysis graph I can see that BuildsView ( created from BuildsRouter.build(forProject: project) are created before I actually tap the navigation Link.
Question:
Is there any way to create the details View once the link is tapped?
True, I wrote a blog post on this. You can wrap the destination views in a Lazy Container as a workaround. Update: From Xcode 11.4.1 NavigationLinks are lazy by default.

Calling of function of one page from another page in blackberry cascades using qml

In my application I have a main.qml which has a navigation pane that goes to homepage.qml and from there to profilepage.qml. When I come back from profilepage to homepage I need to trigger a function in home page. I noticed that whenever I pop back I get a call onPopTransitionEnded in the main page. Since homepage is pushed from main.qml there is no navigation pane on homepage and I cant access onPopTransitionEnded on homepage. Below are the sample structures of my 3 qml views.
main.qml
NavigationPane {
id: nav
peekEnabled: false
onPopTransitionEnded: {
console.log("POP PAGE from main");
if(page.objectName=="newProfilePage")
{
//I tried to access the function using the homepage id but didnt work
menuScreenPage.reloadView(); // This doesnt work, shows error unknown symbol menuScreenPage
}
page.destroy();
}
Page {
id: mainPage
Container {
//some code
}
onCreationCompleted: {
//Some code and then push to homepage
nav.push(homePageDefenition.createObject());
}
}
}
homepage.qml
Page {
id: menuScreenPage
objectName: "menuScreenPage"
function reloadView() //This is the function that is needed to be called on page pop from profile page
{
//some code
}
Container {
//some code
Button { //a button to push to profile page
id:pushButton
horizontalAlignment: HorizontalAlignment.Right
verticalAlignment: VerticalAlignment.Bottom
onClicked: {
console.log("I was clicked!")
nav.push(profilePageDefinition.createObject());
}
}
}
}
profilepage.qml
Page {
id: newProfilePage
objectName: "newProfilePage"
Container {
//some code
Button { //a button to pop to home page
id:popButton
horizontalAlignment: HorizontalAlignment.Right
verticalAlignment: VerticalAlignment.Bottom
onClicked: {
console.log("I was clicked!")
nav.pop();
}
}
}
}
So is there a way that I can access the function of homepage.qml from main.qml? Or is there any other function like onPopTransitionEnded which I can access on homepage.qml itself when I pop from profilepage? Please advice.
Seems that you created unnamed object with this line:
homePageDefenition.createObject()
If you want to access it later, you should save it in some property, for ex.
property var myHomePage: null
...
myHomePage = homePageDefenition.createObject()
nav.push(myHomePage )
...
myHomePage.reloadView();
Keep in mind that "menuScreenPage" is local name (id), it works only inside homepage.qml and nobody can access it beyond that file.
UPD
You can even use such code:
page.reloadView(); // Use local variable "page" instead of internal id "menuScreenPage"

BB10 Cascades: how it`s made?

How implement many custom ListItems, like its implemented in standart blackberry calendar app.
The following screenshot shows what I mean
Especially I interested what is the second control with right arrow.
Thanks.
You can have multiple "types" of list items.
Attach your different types of listItemComponents each with a different type. e.g.
listItemComponents: [
ListItemComponent {
type: "itemA"
Container {
Label {
text: ListItemData.title
textStyle.color: Color.Blue
}
}
},
ListItemComponent {
type: "itemB"
Container {
Label {
text: ListItemData.title
textStyle.color: Color.Red
}
}
}
]
Then add this function to your listview (I'm using a property of "mytype" but you could check any property of the data model or even base it on the indexpath):
function itemType(data, indexPath) {
if (data.mytype == "typea") {
return "itemA";
} else {
return "itemB";
}
}
Now when you add your data to your datamodel make sure you specify "mytype" and the listview will automatically use the ListItemComponent for the relative type.
You can easily have different sized list items, different designs even have them work with different data structures.

Anyway to have an onLoad function in qml when adding to NavigationPane?

I'm new to Blackberry 10 dev. So I'm wondering what's the best way to do this as I'm not getting any clear answers from the dev docs.
What I want is to start a separate view in my app from a navigation screen. The new page will then create a http request and update the UI based on the output.
The best way seems to be using the NavigationPane and add a qml view. However how do I invoke a C++ function when it's pushed onto the stack? Something similar to android onActivityCreated() in Fragments. There is the Http example docs, but the program started the http request from the constructor of the inherited QObject.
How to I have a function executed as the new qml is added to the navigation stack as
// navigationpane.qml
NavigationPane {
id: navigationPane
Page {
Container {
Label {
text: "First page"
}
}
actions: [
ActionItem {
title: "Next page"
ActionBar.placement: ActionBarPlacement.OnBar
onTriggered: {
var page = pageDefinition.createObject();
navigationPane.push(page);
}
attachedObjects: ComponentDefinition {
id: pageDefinition;
source: "secondpage.qml"
}
}
]
}
onPopTransitionEnded: { page.destroy(); }
}
I think the onCreationCompleted function may be what you're looking for.
In the Page object of your secondpage.qml file, add this:
Page {
id: secondpage
onCreationCompleted: {
// use Javascript to call the exposed C++ function
}
}
If you want something more in the spirit of "onActivityCreated()", you can use the signal transitionEnded:
NavigationPane {
onPushTransitionEnded{
top.callYourPageFunction();
}
}

Resources