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

如何实现文本环绕Icon Widget布局?优先无硬编码自定义方案

实现文本环绕Icon的动态布局方案

要实现文本环绕Icon,且文本高度超过Icon时从第四行开始换行到Icon下方的需求,这里提供一个无硬编码的自定义布局方案,核心思路是通过TextPainter预计算文本行数和内容,动态拆分文本并组合布局:

核心实现代码

class WrapTextAroundIcon extends StatelessWidget {
  final String text;
  final Widget icon;
  final TextStyle? textStyle;
  final int wrapAfterLines; // 前N行环绕,这里设置为3,对应第四行开始换行

  const WrapTextAroundIcon({
    super.key,
    required this.text,
    required this.icon,
    this.textStyle,
    this.wrapAfterLines = 3,
  });

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        return FutureBuilder<LayoutMetrics>(
          future: _calculateLayoutMetrics(constraints.maxWidth),
          builder: (context, snapshot) {
            if (!snapshot.hasData) return const SizedBox.shrink();

            final metrics = snapshot.data!;
            final totalLines = metrics.totalLines;
            final iconWidth = metrics.iconWidth;
            final iconHeight = metrics.iconHeight;

            // 拆分文本:前3行保留在Icon右侧,剩余文本移到Icon下方
            final wrapText = _getTextForLines(metrics.textPainter, wrapAfterLines);
            final remainingText = text.substring(wrapText.length);

            return Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Row(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    SizedBox(width: iconWidth, height: iconHeight, child: icon),
                    const SizedBox(width: 8), // 图标和文本的间距
                    Expanded(
                      child: Text(wrapText, style: textStyle),
                    ),
                  ],
                ),
                // 只有当总行数超过3时才显示剩余文本
                if (totalLines > wrapAfterLines)
                  Padding(
                    padding: const EdgeInsets.only(top: 8),
                    child: Text(remainingText, style: textStyle),
                  ),
              ],
            );
          },
        );
      },
    );
  }

  // 计算图标尺寸和文本布局信息
  Future<LayoutMetrics> _calculateLayoutMetrics(double maxWidth) async {
    final iconRenderBox = await _getWidgetSize(icon);
    final iconWidth = iconRenderBox.width;
    final iconHeight = iconRenderBox.height;

    // 文本可用宽度 = 总宽度 - 图标宽度 - 间距
    final textAvailableWidth = maxWidth - iconWidth - 8;

    final textPainter = TextPainter(
      text: TextSpan(text: text, style: textStyle),
      textDirection: TextDirection.ltr,
      maxLines: null,
    )..layout(maxWidth: textAvailableWidth);

    return LayoutMetrics(
      iconWidth: iconWidth,
      iconHeight: iconHeight,
      textPainter: textPainter,
      totalLines: textPainter.computeLineMetrics().length,
    );
  }

  // 获取Widget的实际尺寸
  Future<Size> _getWidgetSize(Widget widget) async {
    final renderObject = widget.createRenderObject(WidgetsBinding.instance.context);
    renderObject.layout(const BoxConstraints());
    return renderObject.size;
  }

  // 根据行数截取文本内容
  String _getTextForLines(TextPainter painter, int lineCount) {
    final lineMetrics = painter.computeLineMetrics();
    if (lineMetrics.length <= lineCount) return text;
    final endOffset = lineMetrics[lineCount - 1].end;
    return text.substring(0, endOffset);
  }
}

class LayoutMetrics {
  final double iconWidth;
  final double iconHeight;
  final TextPainter textPainter;
  final int totalLines;

  LayoutMetrics({
    required this.iconWidth,
    required this.iconHeight,
    required this.textPainter,
    required this.totalLines,
  });
}

使用示例

WrapTextAroundIcon(
  text: "这是一段测试文本,当文本内容足够长,超过3行之后,第四行及以后的内容就会自动换行到图标的下方区域显示,完全不需要硬编码高度或行数,适配不同屏幕尺寸和文本长度。",
  icon: const Icon(Icons.info_outline, size: 48),
  textStyle: const TextStyle(fontSize: 16),
)

方案优势

  • 完全无硬编码:通过动态计算图标尺寸、文本行数和内容,适配不同屏幕宽度和文本长度
  • 自动适配:文本行数不足3行时,正常显示在Icon右侧;超过3行时自动拆分布局
  • 灵活可调:通过wrapAfterLines参数可以自由调整从第几行开始换行到Icon下方

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 17:03:18