DEV Community

HarmonyOS
HarmonyOS

Posted on

Per-Tab Immersive Status Bar and Programmatic TabBar Hiding with ArkUI Tabs

Read the original article:Per-Tab Immersive Status Bar and Programmatic TabBar Hiding with ArkUI Tabs

Per-Tab Immersive Status Bar and Programmatic TabBar Hiding with ArkUI Tabs

Requirement Description

  • Q1: When building pages with Tabs, how can a specific TabContent enable an immersive (edge-to-edge) top status bar effect?
  • Q2: When using Tabs, how can the TabBar be hidden programmatically?

Background Knowledge

  • expandSafeArea: controls how a component extends into system safe areas (e.g., status/nav bars).

Docs: https://developer.huawei.com/consumer/en/doc/harmonyos-references/ts-universal-attributes-expand-safe-area#expandsafearea

  • barHeight (and barWidth) on Tabs: controls TabBar size. Setting to 0 effectively hides the bar.

Docs (barHeight): https://developer.huawei.com/consumer/en/doc/harmonyos-references/ts-container-tabs#barheight

Implementation Steps

For Q1 — Immersive status bar only for the target TabContent:

  1. On the page/Tabs root, allow content to extend into system safe areas via expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]).
  2. On the target TabContent, set .clip(false) so its child layout can draw into the expanded area.
  3. Inside that TabContent, apply expandSafeArea to the container that should render edge-to-edge.

For Q2 — Hide the TabBar:

  1. Bind a boolean state (e.g., isTabBarVisible) to Tabs.
  2. Toggle .barHeight(this.isTabBarVisible ? -1 : 0) and .barWidth(this.isTabBarVisible ? -1 : 0).
    • 0 → hide; -1 → use default sizing.
  3. Optionally add a short animation via .animationDuration(ms) for smoother show/hide.

Code Snippet / Configuration

Q1 — Per-Tab immersive status bar

@Entry
@Component
struct TabsExample {
  build() {
    Tabs({ barPosition: BarPosition.End }) {
      TabContent() {
        Column() {
          Text('Homepage Content ')
            .fontSize(30)
            .width('100%')
            .height('100%')
            .backgroundColor('#0A59F7')
            .textAlign(TextAlign.Center)
            // Immersive effect for this tab's content
            .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
        }
        .height('100%')
        .width('100%')
      }
      // Allow drawing beyond the TabContent bounds
      .clip(false)
      .tabBar('Home ')

      TabContent() {
        Row() {
          Text('Recommended content ')
            .fontSize(30)
            .width('100%')
            .height('100%')
            .backgroundColor('#F1F3F5')
            .textAlign(TextAlign.Center)
        }.height('100%')
      }
      .tabBar('Recommendation ')
    }
    .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
    .width('100%')
    .height('100%')
  }
}
Enter fullscreen mode Exit fullscreen mode

Q2 — Programmatically hide/show the TabBar

@Entry
@Component
struct TabsExamIe {
  @State isTabBarVisible: boolean = true;
  pageStack: NavPathStack = new NavPathStack();
  @State fontColor: string = '#182431'
  @State selectedFontColor: string = '#007DFF'
  @State currentIndex: number = 1
  private controller: TabsController = new TabsController()

  @Builder
  tabBuilder(index: number, name: string) {
    Column() {
      Text(name)
        .fontColor(this.currentIndex === index ? this.selectedFontColor : this.fontColor)
        .fontSize(16)
        .fontWeight(this.currentIndex === index ? 500 : 400)
        .lineHeight(22)
        .margin({ top: 17, bottom: 7 })
      Divider()
        .strokeWidth(2)
        .color('#007DFF')
        .opacity(this.currentIndex === index ? 1 : 0)
    }.width('100%')
  }

  build() {
    Column() {

      Blank().height('5%')

      Row()
      {
        Text('Hide ').height('3%').onClick(() => this.isTabBarVisible = false)
        Blank().width('25%')
        Text('Display ').height('3%').onClick(() => this.isTabBarVisible = true)

      }.height('15%')


      Tabs({ barPosition: BarPosition.End, index: this.currentIndex, controller: this.controller }) {
        TabContent() { Column().width('100%').height('100%').backgroundColor('#00CB87') }
        .tabBar(this.tabBuilder(0, 'green'))

        TabContent() { Column().width('100%').height('100%').backgroundColor('#007DFF') }
        .tabBar(this.tabBuilder(1, 'blue'))

        TabContent() { Column().width('100%').height('100%').backgroundColor('#FFBF00') }
        .tabBar(this.tabBuilder(2, 'yellow'))
      }
      .vertical(false)
      .barMode(BarMode.Fixed)
      .barWidth(this.isTabBarVisible ? -1 : 0)
      .barHeight(this.isTabBarVisible ? -1 : 0)
      .animationDuration(400)
      .onChange((index: number) => this.currentIndex = index)
      .width('100%')
      .height('64%')
      .backgroundColor('#F1F3F5')
    }.width('100%')
  }
}
Enter fullscreen mode Exit fullscreen mode

Test Results

  • Verified immersive rendering on the first TabContent only; second tab remains standard layout.
  • Verified TabBar toggles visibility instantly with animation; selection and content paging continue to work.

g1.gif

Limitations or Considerations

  • barHeight: 'auto' only applies in horizontal mode; hiding uses 0 explicitly.
  • Immersive content must manage its own contrast/readability against status bar overlays (consider background color or top padding as needed).
  • Round wearable screens: confirm corners/radii aren’t clipped; ensure touch targets ≥ 44 vp.

Related Documents or Links

Written by Bunyamin Eymen Alagoz

Top comments (0)