HarmonyOS ArkTS 创建瀑布流 WaterFlow:从“能跑起来”到“真正在项目里好用”的写法

「鸿蒙心迹」“2025・领航者闯关记“主题征文活动 10w+人浏览 509人参与

HarmonyOS ArkTS 创建瀑布流 WaterFlow:从“能跑起来”到“真正在项目里好用”的写法

鸿蒙第四期开发者活动

WaterFlow 适合那种 列宽相等、但每个卡片高度不一样 的场景:商品流、图片墙、内容推荐流……你要的不是“整齐”,而是“错落有致、空间利用率高”。官方文档明确 WaterFlow 用于构建瀑布流布局,并支持条件渲染、循环渲染、懒加载等方式生成子组件。developer.huawei.com+1


1)WaterFlow 的“脑内模型”先对齐

WaterFlow = 多列容器(列宽相等) + 高度不等的卡片(自动往最短列里落)。

你写的时候,只需要抓住三点:

  1. WaterFlow 是容器
  2. FlowItem 是每个卡片/单元
  3. 列数、间距、滚动事件 是你在项目里最常调的东西developer.huawei.com+1

2)最小可运行示例:两列瀑布流(先把骨架搭起来)

这段代码的目标很简单:两列、间距有、卡片高度不一样、能滚动。

@Entry
@Component
struct WaterFlowBasicDemo {
  @State private items: number[] = Array.from({ length: 40 }, (_, i) => i + 1)

  build() {
    WaterFlow() {
      ForEach(this.items, (n: number) => {
        FlowItem() {
          // 卡片内容:高度故意做成不一致,才能看到“瀑布”效果
          Column({ space: 8 }) {
            // 模拟封面
            Rect()
              .width('100%')
              .height(60 + (n % 5) * 18) // 高度变化
              .fill(0xFFEAF2FF)
              .radius(12)

            Text(`第 ${n} 个卡片`)
              .fontSize(14)
              .fontWeight(FontWeight.Medium)

            Text('这里放副标题/价格/标签都行')
              .fontSize(12)
              .fontColor(0xFF888888)
          }
          .padding(12)
          .backgroundColor(0xFFFFFFFF)
          .borderRadius(12)
        }
      }, (n: number) => `${n}`)
    }
    .columnsTemplate('1fr 1fr') // 两列
    .columnsGap(10)
    .rowsGap(10)
    .padding(12)
    .backgroundColor(0xFFF5F6F8)
    .width('100%')
    .height('100%')
  }
}

columnsTemplate / columnsGap / rowsGap 这套写法在社区实践里也非常统一:WaterFlow + FlowItem,再配列模板与间距,基本就成型了。bbs.itying.com+1


3)项目里最常见的需求:上拉加载更多(别等用户翻到底才尴尬)

你做内容流,迟早会遇到“滚到底就继续请求下一页”。

WaterFlow 的 API 参考里提供了不少与滚动/布局相关的能力(比如分组混合列数布局等),实际项目里你最先用上的通常是“到达末尾触发加载”。developer.huawei.com

下面给你一个实用写法:用一个状态控制 isLoading,并在触发时 append 数据。

@Entry
@Component
struct WaterFlowLoadMoreDemo {
  @State private items: number[] = Array.from({ length: 30 }, (_, i) => i + 1)
  @State private isLoading: boolean = false

  private async loadMore() {
    if (this.isLoading) return
    this.isLoading = true

    // 模拟网络延迟
    await new Promise<void>((r) => setTimeout(() => r(), 700))

    const start = this.items.length + 1
    const more = Array.from({ length: 20 }, (_, i) => start + i)
    this.items = [...this.items, ...more]

    this.isLoading = false
  }

  build() {
    Column() {
      // 顶部筛选栏(真实项目很常见)
      Row({ space: 10 }) {
        Text('推荐').fontSize(16).fontWeight(FontWeight.Bold)
        Text(this.isLoading ? '加载中…' : '正常').fontSize(12).fontColor(0xFF888888)
        Blank()
      }
      .padding(12)
      .backgroundColor(0xFFFFFFFF)

      WaterFlow() {
        ForEach(this.items, (n: number) => {
          FlowItem() {
            Column({ space: 6 }) {
              Rect().width('100%').height(70 + (n % 6) * 14).fill(0xFFEAF2FF).radius(12)
              Text(`卡片 #${n}`).fontSize(14)
            }
            .padding(12)
            .backgroundColor(0xFFFFFFFF)
            .borderRadius(12)
          }
        }, (n: number) => `${n}`)

        // 尾部 loading 占位(不做也行,但做了更像“真 App”)
        FlowItem() {
          Row() {
            Text(this.isLoading ? '正在加载更多…' : '滑到底会自动加载')
              .fontSize(12)
              .fontColor(0xFF999999)
            Blank()
          }
          .padding(12)
        }
      }
      .columnsTemplate('1fr 1fr')
      .columnsGap(10)
      .rowsGap(10)
      .padding({ left: 12, right: 12, bottom: 12 })
      .backgroundColor(0xFFF5F6F8)
      .width('100%')
      .height('100%')
      // 这里不同版本 WaterFlow 的末尾触发事件/命名可能有差异:
      // 你如果发现没有 onReachEnd,就去 API 参考页按“reach / end / scroll”关键词搜一下对应事件。
      .onReachEnd(() => this.loadMore())
    }
    .width('100%')
    .height('100%')
  }
}

我特意把“筛选栏 + 流式内容 + 尾部提示”放进来,因为这就是你做推荐流/商品流时最像项目的一套结构。WaterFlow 在“列表/网格概述”里也被定位为这种错列布局的典型选择。developer.huawei.com+1


4)别忽略这一点:WaterFlow 不是 Grid,它解决的是“高度不一致”

很多人第一次写瀑布流会纠结:
“我用 Grid 也能两列啊,为啥还要 WaterFlow?”

关键差异在于:

  • Grid 更像“表格”:行列规整,卡片高度最好差不多
  • WaterFlow 是“错列排布”:卡片高度不等也能自然填满空隙developer.huawei.com+1

你做商品图(高矮不同)、图文卡(内容长短不同),WaterFlow 才是那个“不别扭”的选择。


5)进阶:同一个瀑布流里“不同分组用不同列数”(你真写电商会用到)

比如:

  • 顶部“活动入口”用 4 列小图标
  • 下面商品流用 2 列卡片
  • 再下面“猜你喜欢”用 3 列小卡片

WaterFlow 的 API 参考里提到:可以通过 FlowItem 分组实现“同一个瀑布流内部各分组使用不同列数的混合布局”,并且这种模式会忽略 columnsTemplate/rowsTemplatedeveloper.huawei.com

这块我建议你等你真的要做“一个页面混多种网格密度”时再上,不然一开始就写混合分组,调试成本会明显增加。


6)我写 WaterFlow 时的“稳妥习惯”(少踩坑)

  • 给 ForEach 一个稳定 key:刷新/分页时不容易整片抖动
  • 卡片容器 width(‘100%’) + padding:不然视觉会忽大忽小
  • 间距一定要显式设置columnsGap/rowsGap 不写默认会挤
  • 先用 ForEach 跑通,再换 LazyForEach:数据量大了再优化更顺(WaterFlow 官方也强调支持懒加载思路)developer.huawei.com+1

7)你要是想把它变成“真正的项目模板”,我下一步可以直接给你

给你做一个完整的小项目页(和你之前要的“实战示例项目”同风格):

  • 顶部:筛选/排序(推荐 / 销量 / 价格)
  • 中间:WaterFlow 卡片(图片、标题、标签、价格)
  • 底部:触底自动分页 + 骨架屏占位
  • 点击卡片:进详情页(用 Navigation push)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值