DEV Community

HarmonyOS
HarmonyOS

Posted on

How to Implement Rudder-style Bottom Navigation?

Read the original article:How to Implement Rudder-style Bottom Navigation?

Problem Description

The question explores how to make the center icon of a bottom navigation bar extend beyond the bar’s height to achieve a “rudder-style” navigation.

Background Knowledge

  • Tabs component is used for switching content views, where TabBar is the navigation bar and TabContent is the content area.
  • Rudder-style navigation is an extension of basic bottom navigation, where the center button (usually the core function) visually extends above the navigation bar, while side buttons remain standard.
  • margin is a size attribute that sets outer margins. Elevating the margin of a tab button’s container can make it appear raised.
  • offset allows a component to shift from its original layout position. A negative y value moves it upward.

Solution

Both offset and margin properties can achieve rudder-style navigation but require a custom navigation bar.

Option 1: Using the offset Property

Option 2: Using the margin Property

  • Elevate the tab button’s container by adjusting its margin.
@Entry
@Component
struct Index {
  @State tabArray: Array<number> = [0, 1, 2]
  @State focusIndex: number = 0
  private controller: TabsController = new TabsController()

  @Builder
  TabBuilder(tabName: string, tabIndex: number) {
    Column({ space: 10 }) {
      Stack() {
        Row() {
          Image($r('app.media.startIcon'))
            .width(40).height(40)
          Text(tabName).fontSize(14)
            .margin({ top: 60, left: -40 })
        }
        .width(80)
        .height(80)
        .backgroundColor($r('sys.color.white'))
        .justifyContent(FlexAlign.Center)
        .border({
          radius: {
            topLeft: 130,
            topRight: 130,
            bottomLeft: 40,
            bottomRight: 80
          },
        })
      }
      .margin({ bottom: tabIndex === 1 ? 60 : 0 })
    }
    .justifyContent(FlexAlign.Center)
    .onClick(() => {
      this.controller.changeIndex(tabIndex)
      this.focusIndex = tabIndex
    })
    .backgroundColor(Color.White)
    .width('33.34%')
    .height(100)
  }

  build() {
    Column() {
      Stack({ alignContent: Alignment.BottomStart }) {
        Tabs({ barPosition: BarPosition.End, controller: this.controller }) {
          ForEach(this.tabArray, (item: number, index: number) => {
            TabContent() {
              Text('The page ' + item + ' content')
                .height('30%')
                .width('100%')
                .fontSize(30)
                .padding({ bottom: 150 })
            }
            .backgroundColor('#5291FF')
          })
        }
        .barHeight(0)
        .animationDuration(100)
        .onChange((index: number) => {
          console.info('foo change')
          this.focusIndex = index
        })

        // Tab
        Row() {
          Scroll() {
            Row() {
              ForEach(this.tabArray, (item: number, index: number) => {
                this.TabBuilder('Tab ' + item, index)
              })
            }
            .margin({ top: 20 })
            .justifyContent(FlexAlign.SpaceBetween)
            .alignItems(VerticalAlign.Bottom)
          }
          .scrollable(ScrollDirection.Horizontal)
          .scrollBar(BarState.Off)
          .width('100%')
        }
        .justifyContent(FlexAlign.SpaceBetween)
        .alignItems(VerticalAlign.Bottom)
        .width('100%')
      }
      .width('100%')
    }
    .height('100%')
  }
}
Enter fullscreen mode Exit fullscreen mode

Result:

rudder.gif

Summary

  • Rudder-style navigation enhances UX by emphasizing the central action.
  • Customization via offset or margin offers flexibility in design.
  • You can check these links for details:
  • Tabs Component
  • Rudder Navigation Guide

This example supports API Version 20 Release and later versions, HarmonyOS 6.0.0 Release SDK and later versions, and requires DevEco Studio 6.0.0 Release or later for building and running.

Written by Taskhyn Maksim

Top comments (0)