You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter Flow布局实现求助:求Flow组件使用示例代码

Flutter中Flow组件实现流式布局的完整示例

嘿,我太懂找不到Flutter Flow类示例的苦恼了!它是个强大的底层布局组件,但不像Wrap那样有现成的开箱即用示例。下面我给你完整演示怎么用Flow实现你想要的流式布局,比如常见的标签流布局——一行放不下自动换行的那种:

完整代码示例

import 'package:flutter/material.dart';
import 'dart:math';

void main() => runApp(const FlowDemoApp());

class FlowDemoApp extends StatelessWidget {
  const FlowDemoApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Flow流式布局示例')),
        body: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Flow(
            // 自定义布局代理,核心布局逻辑全在这里
            delegate: TagFlowDelegate(
              spacing: 8.0, // 标签间的水平间距
              runSpacing: 8.0, // 行与行的垂直间距
            ),
            // 要排列的子组件(这里是一堆标签)
            children: [
              _buildTag('Flutter'),
              _buildTag('Flow布局'),
              _buildTag('自定义流式布局'),
              _buildTag('Stack Exchange'),
              _buildTag('示例代码'),
              _buildTag('Widget'),
              _buildTag('布局组件'),
              _buildTag('流式排列'),
            ],
          ),
        ),
      ),
    );
  }

  // 构建单个标签Widget
  Widget _buildTag(String text) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
      decoration: BoxDecoration(
        color: Colors.blue.shade100,
        borderRadius: BorderRadius.circular(16),
      ),
      child: Text(text, style: const TextStyle(color: Colors.blue)),
    );
  }
}

// 自定义FlowDelegate,处理核心布局逻辑
class TagFlowDelegate extends FlowDelegate {
  final double spacing; // 水平间距
  final double runSpacing; // 垂直行间距

  TagFlowDelegate({required this.spacing, required this.runSpacing});

  @override
  Size getSize(BoxConstraints constraints) {
    // 返回Flow布局的整体尺寸,这里直接使用父组件提供的最大可用空间
    return constraints.biggest;
  }

  @override
  void paintChildren(FlowPaintingContext context) {
    double currentX = 0; // 当前行的起始X坐标
    double currentY = 0; // 当前行的起始Y坐标
    double currentLineMaxHeight = 0; // 当前行的最大高度,用于计算下一行位置

    for (int i = 0; i < context.childCount; i++) {
      final childSize = context.getChildSize(i)!; // 获取当前子组件的尺寸

      // 判断当前子组件放入当前行是否会超出父组件宽度
      if (currentX + childSize.width > context.size.width) {
        // 换行操作:更新Y坐标,重置X坐标和当前行最大高度
        currentY += currentLineMaxHeight + runSpacing;
        currentX = 0;
        currentLineMaxHeight = 0;
      }

      // 绘制当前子组件到指定位置
      context.paintChild(
        i,
        transform: Matrix4.translationValues(currentX, currentY, 0),
      );

      // 更新当前行的X坐标
      currentX += childSize.width + spacing;
      // 更新当前行的最大高度
      currentLineMaxHeight = max(currentLineMaxHeight, childSize.height);
    }
  }

  @override
  bool shouldRepaint(covariant TagFlowDelegate oldDelegate) {
    // 当间距参数变化时,触发重绘布局
    return spacing != oldDelegate.spacing || runSpacing != oldDelegate.runSpacing;
  }
}

关键逻辑解释

  • FlowDelegate是核心:Flow本身不处理布局,所有的排列逻辑都在自定义的FlowDelegate中实现,你需要重写三个关键方法:
    • getSize:定义整个Flow布局的尺寸,这里直接复用父组件的最大可用空间;
    • paintChildren:最核心的布局逻辑——我们在这里判断子组件是否需要换行,计算每个子组件的绘制位置,实现流式排列;
    • shouldRepaint:判断何时需要重新布局,比如当间距参数变化时,就会触发重绘。
  • 子组件位置控制:通过Matrix4.translationValues来设置每个子组件的绘制坐标,实现精准的位置控制。

你可以根据自己的需求修改paintChildren里的逻辑,比如调整对齐方式、添加内边距、修改换行规则等等,Flow给了你极高的自定义自由度。

内容的提问来源于stack exchange,提问作者user3924438

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:41:37