Building Dynamic Lists in ArkUI for HarmonyOS

Axis Configuration

By default, the List component arranges its items vertically, automatically enabling vertical scrolling. To create a horizontally scrolling list, assign Axis.Horizontal to the listDirection property. The default value is Axis.Vertical.

List() {
  // ...
}
.listDirection(Axis.Horizontal)

Cross-Axis Layout

The cross-axis layout is managed using the lanes and alignListItem properties. The lanes property determines the number of items displayed along the cross-axis, facilitating adaptive layouts across different device sizes. It accepts an integer or a LengthConstrain object. The alignListItem property controls the alignment of child components within the cross-axis.

List() {
  // ...
}
.lanes(2)
List() {
  // ...
}
.lanes({ minLength: 200, maxLength: 300 })
List() {
  // ...
}
.alignListItem(ListItemAlign.Center)

Data Rendering Examples

Static Data Display

@Entry
@Component
struct NationList {
  build() {
    List() {
      ListItem() {
        Text('Canada').fontSize(24)
      }
      ListItem() {
        Text('Brazil').fontSize(24)
      }
      ListItem() {
        Text('Germany').fontSize(24)
      }
    }
    .backgroundColor('#FFF1F3F5')
    .alignListItem(ListItemAlign.Center)
  }
}
@Entry
@Component
struct UserProfileList {
  build() {
    List() {
      ListItem() {
        Row() {
          Image($r('app.media.app_icon'))
            .width(40)
            .height(40)
            .margin(10)
          Text('Alice').fontSize(20)
        }
      }
      ListItem() {
        Row() {
          Image($r('app.media.app_icon'))
            .width(40)
            .height(40)
            .margin(10)
          Text('Bob').fontSize(20)
        }
      }
    }
  }
}

Dynamic Data Iteration

import util from '@ohos.util';

class UserProfile {
  id: string = util.generateRandomUUID(true);
  username: string;
  avatar: Resource;

  constructor(username: string, avatar: Resource) {
    this.username = username;
    this.avatar = avatar;
  }
}

@Entry
@Component
struct DynamicUserList {
  private users = [
    new UserProfile('Alice', $r("app.media.app_icon")),
    new UserProfile('Bob', $r("app.media.app_icon")),
    new UserProfile('Charlie', $r("app.media.app_icon")),
    new UserProfile('Diana', $r("app.media.app_icon")),
  ]

  build() {
    List() {
      ForEach(this.users, (profile: UserProfile) => {
        ListItem() {
          Row() {
            Image(profile.avatar)
              .width(40)
              .height(40)
              .margin(10)
            Text(profile.username).fontSize(20)
          }
          .width('100%')
          .justifyContent(FlexAlign.Start)
        }
      }, profile => profile.id + profile.username)
    }
    .width('100%')
  }
}

Customizing List Styles

Content Spacing

List({ space: 10 }) {
  // ...
}

Dividers

List() {
  // ...
}
.divider({
  strokeWidth: 1,
  startMargin: 60,
  endMargin: 10,
  color: '#ffe9f0f0'
})

Scroll Bars

List() {
  // ...
}
.scrollBar(BarState.Auto)

Grouped Lists

Groups can be constructed manually using ListItemGroup:

@Component
struct GroupedUserList {
  @Builder groupHeader(title: string) {
    Text(title)
      .fontSize(20)
      .backgroundColor('#fff1f3f5')
      .width('100%')
      .padding(5)
  }

  build() {
    List() {
      ListItemGroup({ header: this.groupHeader('A') }) {
        // Render items for group A
      }
      ListItemGroup({ header: this.groupHeader('B') }) {
        // Render items for group B
      }
    }
  }
}

To dynamically render groups, iterate over a structured data source:

userGroups: object[] = [
  {
    title: 'A',
    members: [
      new UserProfile('Aaron', $r('app.media.iconA')),
      new UserProfile('Amanda', $r('app.media.iconB')),
    ],
  },
  {
    title: 'B',
    members: [
      new UserProfile('Blake', $r('app.media.iconC')),
      new UserProfile('Bella', $r('app.media.iconD')),
    ],
  }
]

List() {
  ForEach(this.userGroups, group => {
    ListItemGroup({ header: this.groupHeader(group.title) }) {
      ForEach(group.members, member => {
        ListItem() {
          // Render member
        }
      }, member => member.id)
    }
  })
}

Sticky Headers

List() {
  ForEach(this.userGroups, group => {
    ListItemGroup({ header: this.groupHeader(group.title) }) {
      // ...
    }
  })
}
.sticky(StickyStyle.Header)

Programmatic Scroll Control

private listController: Scroller = new Scroller();

Stack({ alignContent: Alignment.BottomEnd }) {
  List({ space: 20, scroller: this.listController }) {
    // ...
  }

  Button() {
    // ...
  }
  .onClick(() => {
    this.listController.scrollToIndex(0)
  })
}

Responding to Scroll Position

const indexChars = ['#', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];

@Entry
@Component
struct IndexedUserList {
  @State activeIndex: number = 0;
  private listController: Scroller = new Scroller();

  build() {
    Stack({ alignContent: Alignment.End }) {
      List({ scroller: this.listController }) {
        // ...
      }
      .onScrollIndex((first: number) => {
        this.activeIndex = first
      })

      AlphabetIndexer({ arrayValue: indexChars, selected: 0 })
        .selected(this.activeIndex)
    }
  }
}

Swipe Actions

@Entry
@Component
struct NotificationList {
  @State alerts: object[] = [ /* ... */ ];

  @Builder trailingAction(idx: number) {
    Button({ type: ButtonType.Circle }) {
      Image($r('app.media.ic_public_delete_filled'))
        .width(20)
        .height(20)
    }
    .onClick(() => {
      this.alerts.splice(idx, 1);
    })
  }

  build() {
    List() {
      ForEach(this.alerts, (item, index) => {
        ListItem() {
          // ...
        }
        .swipeAction({ end: this.trailingAction.bind(this, index) })
      }, item => item.id.toString())
    }
  }
}

Item Badges

Badge({
  count: 1,
  position: BadgePosition.RightTop,
  style: { badgeSize: 16, badgeColor: '#FA2A2D' }
}) {
  // Avatar Image
}

Pull-to-Refresh and Load More

Implementing pull-to-refresh functionality can be achieved using the native Refresh component or third-party libraries like OpenHarmony-SIG/PullToRefresh.

Editable Lists

import util from '@ohos.util';

export class TaskItem {
  id: string = util.generateRandomUUID(true);
  taskName: string;
}

Tags: HarmonyOS ArkUI List Component TypeScript

Posted on Tue, 12 May 2026 14:55:02 +0000 by freeloader