🐶

【Flutter】改行文字(\n)を含む複数行のテキストでTextOverflow.ellipsisが正しく適用されないときの対処法

2025/01/08に公開

TextTextOverflow.ellipsisを指定することで、文字が省略されていることを表現できます。

しかし、表示したい文字列内に改行文字(\n)を含んでいるときに、想定とは違う挙動が起こったので、つまずいた点と(応急処置的)対処法をまとめました。

つまずいた点

例えば、
"今日は天気がいいので、外に遊びに行こうと思います。\n友達を誘ってサッカーをしよう。"
という文字列を最大2列のTextで描画したかったとします。

Scaffold(
    appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text("デモ"),
      ),
      body: Center(
        child: Container(
          padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
          margin: const EdgeInsets.symmetric(horizontal: 20),
          color: Colors.cyan,
          child: Text(
            "今日は天気がいいので、外に遊びに行こうと思います。\n友達を誘ってサッカーをしよう。",
            maxLines: 2,
            overflow: TextOverflow.ellipsis,
          ),
        ),
      ),
    );
  }
);

上記のように実装すると、

このように、"友達を誘ってサッカーをしよう。"の部分が表示されていないにも関わらず、省略文字(…)が表示されません。

同様のIssueが挙げられており、現在も未解決のようです。。。
参考:https://github.com/flutter/flutter/issues/50168

対処法

まずは全体のコードと、対処後の画面イメージを先に貼っちゃいます。

Scaffold(
    appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text("デモ"),
    ),
    body: Center(
        child: Container(
          padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
          margin: const EdgeInsets.symmetric(horizontal: 20),
          color: Colors.cyan,
          child: Text(
+           _getDisplayText("今日は天気がいいので、外に遊びに行こうと思います。\n友達を誘ってサッカーをしよう。"),
            maxLines: 2,
            overflow: TextOverflow.ellipsis,
          ),
        ),
    ),
);

/// 以下、追加
/// 表示する文字列を返すメソッド
String _getDisplayText(String text) {
   // 表示する文字列を改行文字(\n)区切りで分割し、リスト化
   List<String> lines = text.split("\n");

   // 1行目の文字列に折り返しが発生するとき、省略文字(…)を付け足す
   if (_checkTextOverflow(lines[0])) {
     return "${lines[0]}…";
   }
   return text;
}

/// 表示する文字列がオーバーフローになる(折り返しが発生する)かを確認するメソッド
bool _checkTextOverflow(String text) {
   final TextPainter textPainter = TextPainter(
     text: TextSpan(text: text),
     maxLines: 1,
     textDirection: TextDirection.ltr,
   )..layout(
       // 80という数字は、paddingの左右20ずつとmarginの左右20ずつを足したもの
       maxWidth: MediaQuery.of(context).size.width - 80,
     );
   return textPainter.didExceedMaxLines;
}

省略文字(…)を表示させることができました!

やっていること

bool _checkTextOverflow(String text) {
    final TextPainter textPainter = TextPainter(
      text: TextSpan(text: text),
      maxLines: 1,
      textDirection: TextDirection.ltr,
    )..layout(
        // 80という数字は、paddingの左右20ずつとmarginの左右20ずつを足したもの
        maxWidth: MediaQuery.of(context).size.width - 80,
      );
    return textPainter.didExceedMaxLines;
}

こちらの_checkTextOverflow()では、表示する文字列が折り返しになるかどうかをTextPainterを使って確認しています。

maxLines1maxWidth- 80の部分にあたる数値は各々の状況に合わせて適宜変えていただけたらと思います。

戻り値のtextPainter.didExceedMaxLinesは今回の場合、Text1行(=maxLines)を超えるときはtrue、超えないときはfalseを返します。

おわりに

ほかにこんなやり方があるよ!などありましたら、教えていただけますと幸いです🙇

ありがとうございました!

Discussion