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

Flutter如何实现多行文本溢出后将‘more’紧跟在省略号后?

多行文本溢出后让“more”紧跟省略号的实现方案

问题复盘

要实现多行文本溢出时显示省略号,还得让“more”紧跟在省略号后面。之前试了俩方案都不行:

  • 单行的时候效果正常,但多行时“more”和主文本垂直对齐,完全不是想要的样子;
  • 用RichText组件也没搞定。

可行解决方案:自定义溢出文本组件

用LayoutBuilder结合TextPainter来动态计算文本是否溢出,然后拼接截断后的文本、省略号和“more”,就能实现预期效果。

完整代码示例

import 'package:flutter/material.dart';

class OverflowTextWithMore extends StatelessWidget {
  final String text;
  final String moreText;
  final TextStyle? textStyle;
  final TextStyle? moreStyle;
  final int maxLines;
  final double textScaleFactor;

  const OverflowTextWithMore({
    super.key,
    required this.text,
    required this.moreText,
    this.textStyle,
    this.moreStyle,
    this.maxLines = 2,
    this.textScaleFactor = 1.0,
  });

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        final defaultStyle = DefaultTextStyle.of(context).style;
        final textStyle = this.textStyle ?? defaultStyle;
        final moreStyle = this.moreStyle ?? textStyle.copyWith(color: Colors.blue);

        // 先算原始文本的布局情况
        final textPainter = TextPainter(
          text: TextSpan(text: text, style: textStyle),
          maxLines: maxLines,
          textScaleFactor: textScaleFactor,
          textDirection: TextDirection.ltr,
        )..layout(maxWidth: constraints.maxWidth);

        // 没溢出的话直接显示完整文本
        if (!textPainter.didExceedMaxLines) {
          return Text(text, style: textStyle, maxLines: maxLines);
        }

        // 溢出的话,逐步截断文本,直到加省略号和more后刚好不超行数
        const ellipsis = '...';
        final ellipsisSpan = TextSpan(text: ellipsis, style: textStyle);
        final moreSpan = TextSpan(text: moreText, style: moreStyle);

        String truncatedText = text;
        while (true) {
          if (truncatedText.isEmpty) break;
          final testSpan = TextSpan(
            children: [
              TextSpan(text: truncatedText, style: textStyle),
              ellipsisSpan,
              moreSpan,
            ],
          );
          final testPainter = TextPainter(
            text: testSpan,
            maxLines: maxLines,
            textScaleFactor: textScaleFactor,
            textDirection: TextDirection.ltr,
          )..layout(maxWidth: constraints.maxWidth);
          if (!testPainter.didExceedMaxLines) {
            break;
          }
          truncatedText = truncatedText.substring(0, truncatedText.length - 1);
        }

        return RichText(
          text: TextSpan(
            children: [
              TextSpan(text: truncatedText, style: textStyle),
              ellipsisSpan,
              moreSpan,
            ],
          ),
          maxLines: maxLines,
          overflow: TextOverflow.visible,
        );
      },
    );
  }
}

// 用的时候直接这么写
class DemoPage extends StatelessWidget {
  const DemoPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('多行溢出测试')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: OverflowTextWithMore(
          text: '这是一段用来测试的超长文本,就是为了验证多行溢出时,省略号后面能精准跟着more按钮,不会出现垂直错位的情况,完全符合设计要求。',
          moreText: 'more',
          maxLines: 2,
          moreStyle: const TextStyle(color: Colors.blue, fontSize: 14),
        ),
      ),
    );
  }
}

方案说明

  1. 先通过LayoutBuilder拿到组件能使用的宽度,保证计算出来的结果准确;
  2. 用TextPainter判断原始文本会不会溢出,没溢出就直接显示完整内容;
  3. 如果溢出了,就一点点截断原始文本,直到加上省略号和“more”后,整体刚好不超过设定的最大行数;
  4. 最后用RichText把截断后的文本、省略号和“more”拼起来,完美实现效果。

为啥之前的方案不行?

  • 单行的方案靠Text组件的overflow属性就能搞定,但多行时默认的省略号是在最后一行末尾,直接拼“more”会被挤到下一行或者垂直对齐出错;
  • 直接用RichText没做溢出计算,没法动态截断文本并控制总行数,自然达不到想要的效果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 11:57:32