Page
API Reference: UI.Page
Basic Page
Page class stands for showing different interfaces. Pages can be considered as screens.
Every page has its own lifecycle: load, show and hide. Application should have at least one page otherwise user will just see a black screen.
Page has an embedded flex layout, into which you can add views.
Please refer to the related guides for best practices of page usage and page navigation.
See also UI.Pages to see how to display a page on the screen.
import PageSampleDesign from "generated/pages/pageSample";
import { Route, Router } from "@smartface/router";
//You should create new Page from UI-Editor and extend with it.
export default class Sample extends PageSampleDesign {
constructor(private router?: Router, private route?: Route) {
super({});
}
/**
* @event onShow
* This event is called when a page appears on the screen (everytime).
*/
onShow() {
super.onShow();
}
/**
* @event onLoad
* This event is called once when page is created.
*/
onLoad() {
super.onLoad();
}
}
On iOS, onShow() function is called when page is about to show.
On Android, it is called when page is shown on screen.
On Android device screen coordinates start after statusBar, the most top left; on iOS it starts the most top left.
Passing Data between Pages
Like in the example shown at Reveal Pages section, you can pass any data to another page while navigating to another page. However, There is no OOTB (Out of the Box) way to pass a variable while going back.
Check our Router Module to get more information about passing data between pages.
Best Practice of Passing data while going back or dismissing
When passing data in-between modal pages, you should use a mutual medium. In this example, a module scoped variable was used to get username which was received under login page. Normally, a state management like redux should be used. More info can be found at this guide.
Disabling Touch of Page
When dealing with services or async methods, you might want to disable all touch interaction to the page. There are two ways of doing that:
- Adding a full-page indicator to let user know that application is processing
- Adding an indicator to a component (e.g. button)
- Adding a label or something different to let the user know that you are loading something
For full-page indicator, refer to the Dialog document below:
DialogFor component indicators, you can have two different approach:
- Adding a fully transparent Dialog to prevent users from touching anything
- Disabling the touch interactions programmatically
If you are implementing an upload service or something that can be shut off during the process, transparent dialog will not be a good solution for you.
For other 2 solutions, you need to temporarily disable the touch action of the page. Here is an example scenario:
- Page UI
- Page Code
{
"components": [
{
"className": ".page",
"id": "59ae-5eb2-b7e9-b48f",
"initialized": true,
"props": {
"children": [
"729a-fbfb-2fa3-cef2",
"f5df-c94a-d994-7bd0",
"f3e7-d604-667c-0e8d",
"e9c6-4c11-7d9d-567b",
"43a6-8ff8-9b47-419f"
],
"name": "pgNoTouch",
"orientation": "PORTRAIT",
"parent": null
},
"type": "Page",
"userProps": {}
},
{
"className": ".statusBar",
"id": "729a-fbfb-2fa3-cef2",
"props": {
"children": [],
"isRemovable": false,
"name": "statusBar",
"parent": "59ae-5eb2-b7e9-b48f"
},
"type": "StatusBar",
"userProps": {
"visible": true
}
},
{
"className": ".headerBar",
"id": "f5df-c94a-d994-7bd0",
"props": {
"children": [],
"isRemovable": false,
"name": "headerBar",
"parent": "59ae-5eb2-b7e9-b48f",
"title": "pgNoTouch"
},
"type": "HeaderBar",
"userProps": {
"title": "pgNoTouch"
}
},
{
"className": ".button",
"id": "f3e7-d604-667c-0e8d",
"props": {
"children": [],
"name": "btnTimeout",
"parent": "59ae-5eb2-b7e9-b48f",
"text": "Press to wait for 3 seconds",
"usePageVariable": true
},
"type": "Button",
"userProps": {
"testId": "omUxalyxe",
"text": "Press to wait for 3 seconds",
"usePageVariable": true
}
},
{
"className": ".label",
"id": "e9c6-4c11-7d9d-567b",
"props": {
"children": [],
"name": "lblTouchIndicator",
"parent": "59ae-5eb2-b7e9-b48f",
"text": "Current Touch Status : Enabled",
"usePageVariable": true
},
"type": "Label",
"userProps": {
"testId": "_wfqiTALG",
"text": "Current Touch Status : Enabled",
"textAlignment": "MIDCENTER",
"usePageVariable": true
}
},
{
"className": ".button",
"id": "43a6-8ff8-9b47-419f",
"props": {
"children": [],
"name": "btnCounter",
"parent": "59ae-5eb2-b7e9-b48f",
"text": "Touch Counter : 0",
"usePageVariable": true
},
"type": "Button",
"userProps": {
"marginTop": 100,
"testId": "lh0q4UC9K",
"text": "Touch Counter : 0",
"usePageVariable": true
}
}
]
}
import PageSampleDesign from "generated/pages/pageSample";
import System from "@smartface/native/device/system";
import { Route, Router } from "@smartface/router";
export default class PageSample extends PageSampleDesign {
isPageTouchEnabled = true;
touchCounter = 0;
constructor(private router?: Router, private route?: Route) {
super({});
}
async yourServiceCall(): Promise<void> {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(), 3000);
});
}
initButtons() {
this.btnTimeout.onPress = async () => {
this.togglePageTouch(false);
await this.yourServiceCall();
this.togglePageTouch(true);
};
this.btnCounter.onPress = () => {
this.touchCounter++;
this.btnCounter.text = `Touch Counter : ${this.touchCounter}`;
};
}
togglePageTouch(touchEnabled: boolean) {
this.isPageTouchEnabled = touchEnabled;
this.btnTimeout.enabled = this.isPageTouchEnabled;
this.lblTouchIndicator.text = `Current Touch Status : ${
this.isPageTouchEnabled ? "Enabled" : "Disabled"
}`;
if (System.OS === System.OSType.IOS) {
this.layout.touchEnabled = this.isPageTouchEnabled;
} else {
/**
* If you need to use this event somewhere else, add the code snippet below.
*/
this.layout.android.onInterceptTouchEvent = () => {
if (this.isPageTouchEnabled === false) {
return !this.isPageTouchEnabled;
}
// ...your other code
};
}
}
onShow() {
super.onShow();
}
onLoad() {
super.onLoad();
this.initButtons();
}
}
If you have a SwipeView, TabbarController or BottomTabbar, those are outside of the page scope so their touches cannot be prevented by this method. You have to use a dialog to prevent their touches.
Padding for Pages
When adding padding to the layout of the entire page, specific padding values need to be entered for all edges.
When individual padding is given to all edges of UI elements on the page to avoid breaking their own paddings and allowing them to adjust automatically, you will be able to use this globally throughout the page.
Please note that if you assign and use this value through the 'this.layout.padding' property, it won't have any effect on the page.
export default class PageSample extends PageSampleDesign {
private disposeables: (() => void)[] = [];
lbl: StyleableLabel;
constructor(private router?: Router, private route?: Route) {
super({});
this.lbl = new StyleableLabel();
console.log('[page1] constructor');
}
/**
* @event onShow
* This event is called when a page appears on the screen (everytime).
*/
onShow() {
super.onShow();
console.log('[page1] onShow');
this.layout.paddingLeft = 100
this.layout.paddingRight = 100
this.layout.paddingTop = 100
this.layout.paddingBottom = 100
}
/**
* @event onLoad
* This event is called once when page is created.
*/
onLoad() {
super.onLoad();
}
}
Other Page Types
Aside from normal page which opens left to right (or right to left on RTL interfaces), there are some other page types that you can use.
Pop-up Pages (Modal)
Pop-up page act like full-screen dialog, but also it is a fully fledged page. After using the pop-up page, you can dismiss the pop-up page and will return the page that used before pop-up. To use pop-up page, set modal property of route in your router. You can find an example below.
In this example, styling scheme of this guide is used with a few addition. Also, the theme smartfaceLightTheme was used.
More info can be found on page below:
Using UI Editor and ClassesBefore running the sample code below, make sure you create the following pages on UI Editor:
- pgWelcome
- pgLogin
While creating the pages, UI Editor was used. Please make sure to add the pages and their UI style properly.
In this example, styling scheme of this guide is used with a few addition. Also, the theme smartfaceLightTheme was used.
Do not forget to change your route path defined on start.ts file.
- Welcome Page UI
- Login Page UI
- Router Code
- Welcome Page
- Login Page
- Service Code
{
"components": [
{
"className": ".page",
"id": "e891b86d",
"initialized": false,
"props": {
"children": [
"42b807d1",
"b7d28cff",
"88af-db55-523d-267e",
"e2d2-6802-42cf-d32d"
],
"isRemovable": true,
"name": "pgWelcome",
"orientation": "PORTRAIT",
"parent": null,
"safeAreaEnabled": true
},
"type": "Page",
"userProps": {
"flexProps": {
"justifyContent": "SPACE_BETWEEN",
"alignItems": "STRETCH"
},
"orientation": "PORTRAIT",
"paddingBottom": 20,
"paddingLeft": 16,
"paddingRight": 16,
"safeAreaEnabled": true
},
"version": "6.15.1"
},
{
"className": ".statusBar",
"id": "42b807d1",
"props": {
"children": [],
"isRemovable": false,
"name": "statusBar",
"parent": "e891b86d"
},
"type": "StatusBar",
"userProps": {}
},
{
"className": ".headerBar",
"id": "b7d28cff",
"props": {
"children": [],
"isRemovable": false,
"name": "headerBar",
"parent": "e891b86d",
"title": "Profile"
},
"type": "HeaderBar",
"userProps": {
"title": "Profile"
}
},
{
"className": ".label",
"id": "88af-db55-523d-267e",
"props": {
"children": [],
"name": "lblLogin",
"parent": "e891b86d",
"text": "You must login to see your name",
"usePageVariable": true
},
"type": "Label",
"userProps": {
"testId": "tuHBk9XRa",
"text": "You must login to see your name",
"usePageVariable": true
}
},
{
"className": ".button",
"id": "e2d2-6802-42cf-d32d",
"props": {
"children": [],
"name": "btnLogin",
"parent": "e891b86d",
"text": "Login",
"usePageVariable": true
},
"type": "Button",
"userProps": {
"testId": "YXfMaNYMP",
"text": "Login",
"usePageVariable": true
}
}
]
}
{
"components": [
{
"className": ".page",
"id": "e891b86d",
"initialized": true,
"props": {
"children": [
"42b807d1",
"b7d28cff",
"01ee-3b7d-fae4-a0fe",
"1920-1907-7edc-a065",
"230e-7a87-bbbe-d054",
"0855-e9ce-5840-6caf",
"0cff-66df-b749-b9f8",
"4c56-9a45-4bd6-d78a"
],
"isRemovable": true,
"name": "pgLogin",
"orientation": "PORTRAIT",
"parent": null,
"safeAreaEnabled": true
},
"type": "Page",
"userProps": {
"orientation": "PORTRAIT",
"safeAreaEnabled": true,
"paddingRight": 20,
"paddingLeft": 20,
"paddingTop": 20,
"flexProps": {
"alignItems": "STRETCH",
"alignContent": "CENTER",
"austifyContent": "SPACE_BETWEEN"
}
},
"version": "6.15.1"
},
{
"className": ".statusBar",
"id": "42b807d1",
"props": {
"children": [],
"isRemovable": false,
"name": "statusBar",
"parent": "e891b86d"
},
"type": "StatusBar",
"userProps": {}
},
{
"className": ".headerBar",
"id": "b7d28cff",
"props": {
"children": [],
"isRemovable": false,
"name": "headerBar",
"parent": "e891b86d",
"title": "Login"
},
"type": "HeaderBar",
"userProps": {
"title": "Login"
}
},
{
"className": ".label",
"id": "01ee-3b7d-fae4-a0fe",
"props": {
"children": [],
"name": "lblLoginText",
"parent": "e891b86d",
"text": "Login",
"usePageVariable": true
},
"type": "Label",
"userProps": {
"testId": "31XoZqypi",
"text": "Login",
"usePageVariable": true
}
},
{
"className": ".textBox",
"id": "1920-1907-7edc-a065",
"oldName": "txtBoxEmail",
"props": {
"children": [],
"hint": "Email",
"name": "txtEmail",
"parent": "e891b86d"
},
"type": "TextBox",
"userProps": {
"hint": "Email"
}
},
{
"className": ".textBox",
"id": "230e-7a87-bbbe-d054",
"oldName": "textBox2",
"props": {
"children": [],
"hint": "Password",
"isPassword": true,
"name": "txtPassword",
"parent": "e891b86d"
},
"type": "TextBox",
"userProps": {
"hint": "Password",
"isPassword": true
}
},
{
"className": ".label",
"id": "0855-e9ce-5840-6caf",
"props": {
"children": [],
"name": "lblForgotPassword",
"parent": "e891b86d",
"text": "Forgot Password?",
"usePageVariable": true
},
"type": "Label",
"userProps": {
"testId": "cxb0GrKoG",
"text": "Forgot Password?",
"usePageVariable": true
}
},
{
"className": ".button",
"id": "0cff-66df-b749-b9f8",
"props": {
"children": [],
"name": "btnLogin",
"parent": "e891b86d",
"text": "Login",
"usePageVariable": true
},
"type": "Button",
"userProps": {
"testId": "lU2lMxhdz",
"text": "Login",
"usePageVariable": true
}
},
{
"className": ".button",
"id": "4c56-9a45-4bd6-d78a",
"props": {
"children": [],
"name": "btnCancel",
"parent": "e891b86d",
"text": "Cancel",
"usePageVariable": true
},
"type": "Button",
"userProps": {
"testId": "ERnreq7sS",
"text": "Cancel",
"usePageVariable": true
}
}
]
}
import { NativeRouter, NativeStackRouter, Route } from "@smartface/router";
import Application from "@smartface/native/application";
import PgWelcome from "../pages/pgWelcome";
import PgLogin from "../pages/pgLogin";
Application.on("backButtonPressed", () => {
NativeRouter.getActiveRouter()?.goBack();
});
const router = NativeRouter.of({
path: "/",
to: "/root",
isRoot: true,
routes: [
NativeStackRouter.of({
path: "/root",
to: "/root/welcome",
routes: [
Route.of({
path: "/root/welcome",
build: (router, route) => new PgWelcome(router, route),
}),
NativeStackRouter.of({
path: "/root/user",
to: "/root/user/login",
modal: true, // This is essential
routes: [
Route.of({
path: "/root/user/login",
build: (router, route) => new PgLogin(router, route),
}),
],
}),
],
}),
],
});
export default router;
import PgWelcomeDesign from "generated/pages/pgWelcome";
import { withDismissAndBackButton } from "@smartface/mixins";
import { Router, Route } from "@smartface/router";
import { getUsername, logout } from "../services";
export default class PgWelcome extends withDismissAndBackButton(
PgWelcomeDesign
) {
isLoggedIn = false;
constructor(private router?: Router, private route?: Route) {
super({});
}
initLoginButton() {
this.btnLogin.on("press", () => {
if (this.isLoggedIn) {
this.logout();
} else {
this.router.push("user/login");
}
});
}
updateText() {
if (this.isLoggedIn) {
this.lblLogin.text = `Welcome, ${getUsername()}`;
this.btnLogin.text = `Logout`;
} else {
this.lblLogin.text = `You must login to see your name`;
this.btnLogin.text = `Login`;
}
}
logout() {
logout();
this.isLoggedIn = false;
this.updateText();
}
onShow() {
super.onShow();
this.isLoggedIn = getUsername().length ? true : false;
this.updateText();
this.initBackButton(this.router);
}
onLoad() {
super.onLoad();
this.initLoginButton();
}
}
import PgLoginDesign from "generated/pages/pgLogin";
import { withDismissAndBackButton } from "@smartface/mixins";
import { Router, Route, NativeStackRouter } from "@smartface/router";
import { login } from "../services";
export default class PgLogin extends withDismissAndBackButton(PgLoginDesign) {
constructor(private router?: Router, private route?: Route) {
super({});
}
initTextBoxes() {
this.txtEmail.text = `Smartface`;
this.txtPassword.text = `welcome`;
}
initButtons() {
this.btnLogin.on("press", async () => {
try {
const username = this.txtEmail.text;
const password = this.txtPassword.text;
this.btnLogin.text = `Logging in ...`;
await login(username, password);
if (this.router instanceof NativeStackRouter) {
this.router.dismiss();
}
} catch (e) {
// Empty text is not allowed
this.btnLogin.text = `Login`;
}
});
this.btnCancel.on("press", () => {
if (this.router instanceof NativeStackRouter) {
this.router.dismiss();
}
});
}
initMockUserPass() {
this.lblLoginText.on("touch", () => {
this.txtEmail.text = `Smartface`;
this.txtPassword.text = `welcome`;
});
}
onShow() {
super.onShow();
this.initDismissButton(this.router);
}
onLoad() {
super.onLoad();
this.initButtons();
this.initMockUserPass();
this.initTextBoxes();
}
}
let userName = "";
export function login(username: string, password: string): Promise<string> {
return new Promise((resolve, reject) => {
const token = "Your token goes here";
if (!username.length) {
reject("Empty username");
}
setTimeout(() => {
userName = username;
resolve(token);
}, 500);
});
}
export function logout() {
userName = "";
}
export function getUsername() {
return userName;
}
Reveal Pages
API Reference: TransitionViews
Transitions are the animated changes between two pages, states or views to provide visual continuity to the user interface. It works like PopupPage. Use transitionId to mark related View_s. Each _View must be linked with unique id.
TransitionId of View must be set and link before onShow methods and creating instance of second page.
Before running the sample code below, make sure you create the following pages on UI Editor:
- pgUserList
- pgUser
While creating the pages, UI Editor was used. Please make sure to add the pages and their UI style properly.
Do not forget to change your main route push on start.ts
- Main Page UI
- User List Page
- Detail Page UI
- Detail Page
- Component UI
- Service Code
- Router Code
{
"components": [
{
"className": ".page",
"id": "0216-a586-68ea-ef16",
"initialized": true,
"props": {
"children": [
"e251-dc93-1cce-a061",
"e5bc-0b15-1b36-b8d6",
"c8dc-c2bc-eac7-33e3"
],
"name": "pgMain",
"orientation": "PORTRAIT",
"parent": null
},
"type": "Page",
"userProps": {}
},
{
"className": ".statusBar",
"id": "e251-dc93-1cce-a061",
"props": {
"children": [],
"isRemovable": false,
"name": "statusBar",
"parent": "0216-a586-68ea-ef16"
},
"type": "StatusBar",
"userProps": {}
},
{
"className": ".headerBar",
"id": "e5bc-0b15-1b36-b8d6",
"props": {
"children": [],
"isRemovable": false,
"name": "headerBar",
"parent": "0216-a586-68ea-ef16",
"title": "Main Page"
},
"type": "HeaderBar",
"userProps": {
"title": "Main Page"
}
},
{
"className": ".gridView",
"id": "c8dc-c2bc-eac7-33e3",
"props": {
"children": ["a41a-fe3a-cfe8-7bb5"],
"name": "gvMain",
"parent": "0216-a586-68ea-ef16",
"usePageVariable": true
},
"type": "GridView",
"userProps": {
"flexProps": {
"flexGrow": 1
},
"layoutManager": {
"spanCount": 2
},
"testId": "pRB1URCFU",
"usePageVariable": true
}
},
{
"className": ".gridViewItem .gridViewItem-simple",
"hiddenComponent": false,
"id": "a41a-fe3a-cfe8-7bb5",
"initialized": true,
"props": {
"children": [],
"name": "simple_gridviewItem",
"parent": "c8dc-c2bc-eac7-33e3"
},
"source": {
"page": "__library__",
"type": "simple_gridviewItem",
"id": "aea8-55c1-833e-4baa"
},
"type": "GridViewItem",
"userProps": {
"flex": {
"positionType": 0
},
"flexProps": {
"positionType": "RELATIVE"
},
"height": 227,
"left": 0,
"testId": "6PcXnnHpZ",
"top": 0
}
}
]
}
import PgMainDesign from "generated/pages/pgMain";
import { Route, Router } from "@smartface/router";
import { getUserList, User } from "../services";
import Simple_gridviewItem from "components/Simple_gridviewItem";
export default class PgMain extends PgMain {
private data: User[] = [];
constructor(private router?: Router, private route?: Route) {
super({});
}
async getUsers() {
try {
this.data = await getUserList();
this.refreshGridView();
} catch (e) {
console.error(e);
}
}
initGridView() {
this.gvMain.onItemBind = (
gridViewItem: Simple_gridviewItem,
index: number
) => {
gridViewItem.lblTitle.text = `${this.data[index].first_name} ${this.data[index].last_name}`;
gridViewItem.lblSubtitle.text = ``;
gridViewItem.imgPreview.loadFromUrl({
url: this.data[index].avatar,
useHTTPCacheControl: true,
});
};
this.gvMain.onPullRefresh = async () => {
try {
await this.getUsers();
} finally {
this.gvMain.stopRefresh();
}
};
this.gvMain.onItemSelected = (
gridViewItem: Simple_gridviewItem,
index: number
) => {
//@ts-ignore
gridViewItem.imgPreview.transitionId = String(this.data[index].id);
//@ts-ignore
this.transitionViews = [gridViewItem.imgUser];
this.router.push("user/detail", {
transitionImage: gridViewItem.imgPreview.image,
imageURL: this.data[index].avatar,
transitionId: String(this.data[index].id),
title: gridViewItem.lblTitle.text,
subTitle: this.data[index].email,
});
};
}
refreshGridView() {
this.gvMain.itemCount = this.data.length;
this.gvMain.refreshData();
}
onShow() {
super.onShow();
}
onLoad() {
super.onLoad();
this.initGridView();
this.getUsers();
}
}
{
"components": [
{
"className": ".page",
"id": "f8cf-6172-1673-f91a",
"initialized": true,
"props": {
"children": [
"c67c-e972-e3a7-1b74",
"748e-84f9-8e99-af0c",
"f30b-c1ed-884e-4a1e",
"c651-f5e8-df46-a642",
"0d22-df6d-e330-2c57",
"9265-6282-5efb-696b"
],
"name": "pgDetail",
"orientation": "PORTRAIT",
"parent": null,
"safeAreaEnabled": true
},
"type": "Page",
"userProps": {
"safeAreaEnabled": true
}
},
{
"className": ".statusBar",
"id": "c67c-e972-e3a7-1b74",
"props": {
"children": [],
"isRemovable": false,
"name": "statusBar",
"parent": "f8cf-6172-1673-f91a"
},
"type": "StatusBar",
"userProps": {}
},
{
"className": ".headerBar",
"id": "748e-84f9-8e99-af0c",
"props": {
"children": [],
"isRemovable": false,
"name": "headerBar",
"parent": "f8cf-6172-1673-f91a",
"title": "pgUser"
},
"type": "HeaderBar",
"userProps": {
"title": "Detail",
"visible": false
}
},
{
"className": ".imageView",
"id": "f30b-c1ed-884e-4a1e",
"props": {
"children": [],
"name": "imgProfile",
"parent": "f8cf-6172-1673-f91a",
"usePageVariable": true
},
"type": "ImageView",
"userProps": {
"imageFillType": "ASPECTFIT",
"testId": "Hy6YUKnTK",
"usePageVariable": true
}
},
{
"className": ".label",
"id": "c651-f5e8-df46-a642",
"props": {
"children": [],
"name": "lblTitle",
"parent": "f8cf-6172-1673-f91a",
"text": "label1",
"usePageVariable": true
},
"type": "Label",
"userProps": {
"testId": "Coy_7oKpF",
"text": "label1",
"textAlignment": "MIDCENTER",
"textColor": "rgba( 0, 0, 0, 1 )",
"usePageVariable": true
}
},
{
"className": ".label",
"id": "0d22-df6d-e330-2c57",
"props": {
"children": [],
"name": "lblSubtitle",
"parent": "f8cf-6172-1673-f91a",
"text": "label2",
"usePageVariable": true
},
"type": "Label",
"userProps": {
"testId": "QbQyD2HTK",
"text": "label2",
"textAlignment": "MIDCENTER",
"textColor": "rgba( 0, 0, 0, 1 )",
"usePageVariable": true
}
},
{
"className": ".flexLayout",
"id": "9265-6282-5efb-696b",
"props": {
"children": ["3701-d69f-bbee-1d5f"],
"name": "flWrapper",
"parent": "f8cf-6172-1673-f91a"
},
"type": "FlexLayout",
"userProps": {
"flexProps": {
"flexGrow": 1,
"justifyContent": "FLEX_END"
},
"height": null,
"testId": "tKXqeskD0"
}
},
{
"className": ".button",
"id": "3701-d69f-bbee-1d5f",
"props": {
"children": [],
"name": "btnCancel",
"parent": "9265-6282-5efb-696b",
"text": "Cancel",
"usePageVariable": true
},
"type": "Button",
"userProps": {
"testId": "r0pETCd27",
"text": "Cancel",
"usePageVariable": true
}
}
]
}
import PgDetailDesign from "generated/pages/pgDetail";
import { NativeStackRouter, Route, Router } from "@smartface/router";
import Image from "@smartface/native/ui/image";
export default class PgDetail extends PgDetailDesign {
routeData: {
transitionImage: Image;
imageURL: string;
transitionId: string;
title: string;
subTitle: string;
};
constructor(private router?: Router, private route?: Route) {
super({});
}
initImage() {
this.transitionViews = [this.imgProfile];
this.imgProfile.transitionId = this.route.getState().routeData.transitionId;
this.imgProfile.image = this.route.getState().routeData.transitionImage;
}
initTexts() {
this.lblTitle.text = this.route.getState().routeData.title;
this.lblSubtitle.text = this.route.getState().routeData.subTitle;
}
initButton() {
this.btnCancel.on("press", () => {
if (this.router instanceof NativeStackRouter) {
this.router.dismiss();
}
});
}
onShow() {
super.onShow();
}
onLoad() {
super.onLoad();
this.initImage();
this.initButton();
this.initTexts();
}
}
{
"components": [
{
"className": ".gridViewItem .simpleGridviewItem",
"hiddenComponent": false,
"id": "aea8-55c1-833e-4baa",
"initialized": true,
"props": {
"children": [
"58e6-e69e-a1c1-d4b9",
"67de-5d59-e18c-7727",
"e92d-12a6-d5ab-b6da"
],
"name": "simple_gridviewItem",
"parent": "57f4-201f-4bfc-5fc6"
},
"source": {
"page": "__library__"
},
"type": "GridViewItem",
"userProps": {
"flexProps": {},
"height": 227,
"left": 102.77777777777779,
"top": 88.88888888888889
}
},
{
"className": ".imageView .simpleGridviewItem_preview",
"id": "58e6-e69e-a1c1-d4b9",
"props": {
"children": [],
"name": "imgPreview",
"parent": "aea8-55c1-833e-4baa"
},
"source": {
"page": "__library__"
},
"type": "ImageView",
"userProps": {
"autoHeight": 343.4782608695652,
"autoWidth": 343.4782608695652
}
},
{
"className": ".label .simpleGridviewItem_title",
"id": "67de-5d59-e18c-7727",
"props": {
"children": [],
"name": "lblTitle",
"parent": "aea8-55c1-833e-4baa",
"text": "Grid Title Here"
},
"source": {
"page": "__library__"
},
"type": "Label",
"userProps": {
"autoHeight": 20.652173913043477,
"text": "Grid Title Here"
}
},
{
"className": ".label .simpleGridviewItem_subtitle",
"id": "e92d-12a6-d5ab-b6da",
"props": {
"children": [],
"name": "lblSubtitle",
"parent": "aea8-55c1-833e-4baa",
"text": "Grid subtitle here"
},
"source": {
"page": "__library__"
},
"type": "Label",
"userProps": {
"autoHeight": 18.478260869565215,
"text": "Grid subtitle here"
}
}
]
}
const smartfaceItem = {
id: 0,
email: "info@smartface.io",
first_name: "Smartface",
last_name: "Inc.",
avatar: "https://upload.wikimedia.org/wikipedia/en/7/7d/Smartface_Logo.png",
};
export type User = {
id: number;
email: string;
first_name: string;
last_name: string;
avatar: string; // Url
};
type UserList = {
data: User[];
};
export async function getUserList(): Promise<UserList["data"]> {
return new Promise((resolve, reject) => {
setTimeout(() => {
let response: User[] = Array(16).fill(smartfaceItem);
response = response.map((item, index) => {
const newItem = Object.assign({}, item);
newItem.id = index;
return newItem;
});
resolve(response);
}, 500);
});
}
import { NativeRouter, NativeStackRouter, Route } from "@smartface/router";
import Application from "@smartface/native/application";
import * as Pages from "pages";
Application.on("backButtonPressed", () => {
NativeRouter.getActiveRouter()?.goBack();
});
const router = NativeRouter.of({
path: "/",
to: "/root",
isRoot: true,
routes: [
NativeStackRouter.of({
path: "/root",
to: "/root/userlist",
routes: [
Route.of({
path: "/root/userlist",
build: (router, route) => new Pages.pgMain(router, route),
}),
NativeStackRouter.of({
path: "/root/user",
to: "/root/user/detail",
modal: true, // This is essential
routes: [
Route.of({
path: "/root/user/detail",
build: (router, route) => new Pages.pgDetail(router, route),
}),
],
}),
],
}),
],
});
export default router;
The guides which are used:
GridViewUsing ListView with UI EditorAlso, take a look into Router Repository