BottomTabBar

API Reference: UI.BottomTabBar

BottomTabBar is an UI object. It is used for navigating between pages using tab bar items. Each tab bar item has title, icon and page. If the individual tab has an Icon, icons must be set two types as selected and normal.

TypeScript code blocks includes example of how to implement, override & add components with theme. Create page with UI-Editor to make your page themeable and then able to implement themeable components programmatically. Once page created with UI-Editor, it generates class under scripts/generated/pages. Extend that class with below typescript classes.

On Android side, you can not add more than five tab bar items to bottom tab bar.

Create 3 different page from UI editor named like

  • pgProfile

  • pgMessages

  • pgSettings

App
Route File
Profile Page
Message Page
Settings Page
App
scripts/app.ts
/* globals lang */
import "i18n/i18n.js"; // Generates global lang object
import Application from "sf-core/application";
import { errorStackBySourceMap } from "error-by-sourcemap";
import System from "sf-core/device/system";
import "theme";
import "sf-extension-utils";
import router from "routes";
// Set uncaught exception handler, all exceptions that are not caught will
// trigger onUnhandledError callback.
Application.onUnhandledError = function (e: UnhandledError) {
const error = errorStackBySourceMap(e);
alert({
title: e.type || lang.applicationError,
message: System.OS === System.OSType.ANDROID ? error.stack : (e.message + "\n\n*" + error.stack)
});
};
router.push("/btb");
Route File
scripts/routes/index.ts
import Image from "sf-core/ui/image";
import Router from "@smartface/router/src/native/NativeRouter";
import BottomTabBarRouter from "@smartface/router/src/native/BottomTabBarRouter";
import Route from "@smartface/router/src/router/Route";
import buildExtender from "sf-extension-utils/lib/router/buildExtender";
import Color from "sf-core/ui/color";
const router = Router.of({
path: "/",
isRoot: true,
routes: [
BottomTabBarRouter.of({
path: "/btb",
to: "/btb/tab1/page1",
tabbarParams: () => ({
ios: { visible: false },
itemColor: {
normal: Color.BLACK,
selected: Color.WHITE
},
backgroundColor: Color.create("#00A1F1")
}),
items: () => [{ title: "Profile", icon: Image.createFromFile("images://profile.png") },
{ title: "Messages", icon: Image.createFromFile("images://messages.png") },
{ title: "Settings", icon: Image.createFromFile("images://settings.png") }],
// tab1
routes: [
// tab1
Route.of({
path: "/btb/tab1/page1",
build: buildExtender({
getPageClass: () => require('pages/pgProfile').default
})
}),
// tab2
Route.of({
path: "/btb/tab2/page2",
build: buildExtender({
getPageClass: () => require('pages/pgMessages').default
})
}),
// tab3
Route.of({
path: "/btb/tab3/page3",
build: buildExtender({
getPageClass: () => require('pages/pgSettings').default
})
})
]
})
]
});
export default router;
Profile Page
scripts/pages/pgProfile.ts
import PgProfileDesign from 'generated/pages/pgProfile';
import FlexLayout from 'sf-core/ui/flexlayout';
import Label from"sf-core/ui/label";
//You should create new Page from UI-Editor and extend with it.
export default class PgProfile extends PgProfileDesign {
lbl: Label;
constructor() {
super();
// Overrides super.onShow method
this.onShow = onShow.bind(this, this.onShow.bind(this));
// Overrides super.onLoad method
this.onLoad = onLoad.bind(this, this.onLoad.bind(this));
this.layout.flexDirection = FlexLayout.FlexDirection.ROW;
this.layout.justifyContent = FlexLayout.JustifyContent.CENTER;
this.layout.alignItems = FlexLayout.AlignItems.CENTER;
}
}
/**
* @event onShow
* This event is called when a page appears on the screen (everytime).
* @param {function} superOnShow super onShow function
* @param {Object} parameters passed from Router.go function
*/
function onShow(superOnShow: () => void) {
superOnShow();
}
/**
* @event onLoad
* This event is called once when page is created.
* @param {function} superOnLoad super onLoad function
*/
function onLoad(superOnLoad: () => void) {
superOnLoad();
this.lbl = new Label({
text: "Profile"
});
this.layout.addChild(this.lbl, "lbl", ".sf-label", {
textColor: "#808080",
font: {
size: 20,
bold: true,
italic: false,
family: "Arial",
style: "Semibold"
}
});
}
Message Page
scripts/pages/pgMessages.ts
import PgMessagesDesign from 'generated/pages/pgMessages';
import FlexLayout from 'sf-core/ui/flexlayout';
import Label from 'sf-core/ui/label';
//You should create new Page from UI-Editor and extend with it.
export default class PgMessages extends PgMessagesDesign {
lbl: Label;
constructor() {
super();
// Overrides super.onShow method
this.onShow = onShow.bind(this, this.onShow.bind(this));
// Overrides super.onLoad method
this.onLoad = onLoad.bind(this, this.onLoad.bind(this));
this.layout.flexDirection = FlexLayout.FlexDirection.ROW;
this.layout.justifyContent = FlexLayout.JustifyContent.CENTER;
this.layout.alignItems = FlexLayout.AlignItems.CENTER;
}
}
/**
* @event onShow
* This event is called when a page appears on the screen (everytime).
* @param {function} superOnShow super onShow function
* @param {Object} parameters passed from Router.go function
*/
function onShow(superOnShow: () => void) {
superOnShow();
}
/**
* @event onLoad
* This event is called once when page is created.
* @param {function} superOnLoad super onLoad function
*/
function onLoad(superOnLoad: () => void) {
superOnLoad();
this.lbl = new Label({
text: "Messages"
});
this.layout.addChild(this.lbl, "lbl", ".sf-label", {
textColor: "#808080",
font: {
size: 20,
bold: true,
italic: false,
family: "Arial",
style: "Semibold"
}
});
}
Settings Page
scripts/pages/pgSettings.ts
import PgSettingsDesign from 'generated/pages/pgSettings';
import FlexLayout from 'sf-core/ui/flexlayout';
import Label from 'sf-core/ui/label';
//You should create new Page from UI-Editor and extend with it.
export default class PgSettings extends PgSettingsDesign {
lbl: Label;
constructor() {
super();
// Overrides super.onShow method
this.onShow = onShow.bind(this, this.onShow.bind(this));
// Overrides super.onLoad method
this.onLoad = onLoad.bind(this, this.onLoad.bind(this));
this.layout.flexDirection = FlexLayout.FlexDirection.ROW;
this.layout.justifyContent = FlexLayout.JustifyContent.CENTER;
this.layout.alignItems = FlexLayout.AlignItems.CENTER;
}
}
/**
* @event onShow
* This event is called when a page appears on the screen (everytime).
* @param {function} superOnShow super onShow function
* @param {Object} parameters passed from Router.go function
*/
function onShow(superOnShow: () => void) {
superOnShow();
}
/**
* @event onLoad
* This event is called once when page is created.
* @param {function} superOnLoad super onLoad function
*/
function onLoad(superOnLoad: () => void) {
superOnLoad();
this.lbl = new Label({
text: "Settings"
});
this.layout.addChild(this.lbl, "lbl", ".sf-label", {
textColor: "#808080",
font: {
size: 20,
bold: true,
italic: false,
family: "Arial",
style: "Semibold"
}
});
}

Custom Icons for iOS

Please check this guideline before choosing your custom TabBarItem ​icons.

Android Tabbar Optimization

To save time by loading Tabbar, you can optionally call the constructor function in advance.

BottomTabbar Badge

BottomTabBar Badge has a circular or elliptical background, displayed on top-right to the BottomTabBarItem.

App File
Routes File
App File
scripts/app.ts
/* globals lang */
import "i18n/i18n.js"; // Generates global lang object
import Application from "sf-core/application";
import { errorStackBySourceMap } from "error-by-sourcemap";
import System from "sf-core/device/system";
import "theme";
import "sf-extension-utils";
import router from "routes";
// Set uncaught exception handler, all exceptions that are not caught will
// trigger onUnhandledError callback.
Application.onUnhandledError = function (e: UnhandledError) {
const error = errorStackBySourceMap(e);
alert({
title: e.type || lang.applicationError,
message: System.OS === System.OSType.ANDROID ? error.stack : (e.message + "\n\n*" + error.stack)
});
};
router.push("/btb");
Routes File
scripts/routes/index.ts
import buildExtender from "sf-extension-utils/lib/router/buildExtender";
import Color from 'sf-core/ui/color';
import TabBarItem from "sf-core/ui/tabbaritem";
import BottomTabBarRouter from "@smartface/router/src/native/BottomTabBarRouter";
import {
NativeRouter as Router,
Route
} from '@smartface/router';
import 'sf-extension-utils/lib/router/goBack'; // Implements onBackButtonPressed
let btbItemMessages = new TabBarItem();
btbItemMessages.title = "Tab2";
btbItemMessages.badge.text = "1000";
btbItemMessages.badge.visible = true; // default false
if (parseInt(btbItemMessages.badge.text) > 99) {
btbItemMessages.badge.text = "99+";
}
const router = Router.of({
path: "/",
isRoot: true,
routes: [
BottomTabBarRouter.of({
path: "/btb",
to: "/btb/tab1/page1",
tabbarParams: () => ({
ios: { visible: false },
itemColor: {
normal: Color.BLACK,
selected: Color.WHITE
},
backgroundColor: Color.create("#00A1F1")
}),
items: () => [
{ title: "Tab1" },
btbItemMessages,
{ title: "Tab3" },
{ title: "Tab4" },
{ title: "Tab5" },
],
// tab1
routes: [
// tab1
Route.of({
path: "/btb/tab1/page1",
build: buildExtender({ getPageClass: () => require("pages/page1").default, headerBarStyle: { visible: true } })
}),
// tab2
Route.of({
path: "/btb/tab1/page2",
build: buildExtender({ getPageClass: () => require("pages/page1").default, headerBarStyle: { visible: true } })
}),
// tab3
Route.of({
path: "/btb/tab1/page3",
build: buildExtender({ getPageClass: () => require("pages/page1").default, headerBarStyle: { visible: true } })
}),
// tab4
Route.of({
path: "/btb/tab1/page4",
build: buildExtender({ getPageClass: () => require("pages/page1").default, headerBarStyle: { visible: true } })
}),
// tab5
Route.of({
path: "/btb/tab1/page5",
build: buildExtender({ getPageClass: () => require("pages/page1").default, headerBarStyle: { visible: true } })
}),
]
})
]
});
export default router;

Badge Position On iOS

On iOS, when BottomTabBarItem icon's size is big, badge can be positioned wrong by default. You should call move function for fix this problem.

BottomTabbar Animation On Android

On Android BottomTabbarItems animates when clicked but badge have no animation. In order to have a better experience disableItemAnimation can be used.