のんびり精進

調べた情報などをおすそ分けできれば。

【Flutter】LicensePageのテキストのスタイリング

アプリでは、使っているフレームワーク、ライブラリ、パッケージ等のライセンスを表示する必要がありますね。

Flutter では LicensePage という widget を使うだけで表示することができます。 しかも Flutter 1.20 で 刷新 されて見やすくなりました。

ところが、そこに自分で追加したテキストをきれいに見せるのは簡単ではありませんでした。 この記事は下のような表示にするのに少し苦労した話です。 わかってしまえば簡単です。

f:id:kabochapo:20200827004026j:plain

適当に載せた場合

ライセンスページを見ていると、こんな表示になっているパッケージもあります。

f:id:kabochapo:20200827004248j:plain

これは酷いですね…。

でもライセンスページに自動追加されたものは、パッケージの作者がライセンステキストをどう書いているかによります。 使用者であるアプリ開発者がどうこう言う部分ではありません。

しかし、アイコン等の素材は別です。 そのライセンスは自分で追加しなければなりません。

まず何も考えずに追加してみた結果がこちらです。

f:id:kabochapo:20200827004412j:plain

インデントと箇条書きで見やすくしたつもりが、意図通りの表現になりませんでした。

素敵な素材の提供者に敬意と感謝の気持ちを表して、もっと見やすく表示したいのですが…。

きれいに表示される例

テキストによっては、一部がインデントされていたり太字でセンタリングされていたりします。 そのようにスタイリングする機能が一応備わっているのだろうと考えました。

f:id:kabochapo:20200827004834j:plain f:id:kabochapo:20200827004855j:plain

スタイリングの方法を調べた過程

ではどうやってやるのでしょうか? 方法を Web 検索してみても情報が全然出てきません。

そこで仕方なく ソースコード を見ました。

どうでしょう。ご自分で読み解けますか?

お急ぎの方(TL;DR)

この記事は、ソースコードを見て方法を調べた過程を書いた「読み物」(兼、著者のメモ)です。 「なんで簡単にわかるようになっていないの?」という不満も少し込めています。

手っ取り早く方法を知りたい方は末尾のまとめをご覧ください。

ソースコードを眺める

LicenseParagraph というクラスがあり、そこに indentcenteredIndent というプロパティが存在しています。 コメントを消して短くすると下記のようになります。

class LicenseParagraph {
  const LicenseParagraph(this.text, this.indent);

  final String text;
  final int indent;
  static const int centeredIndent = -1;
}

indent はインデントに関する値、centeredIndent はセンタリングさせる場合に使う定数、おそらく。

これを使えばできそうに思えましたが、LicenseEntryWithLineBreaks というクラスの中で使われているものでした。

class LicenseEntryWithLineBreaks extends LicenseEntry {
   ...

  @override
  Iterable<LicenseParagraph> get paragraphs sync* {
    ...

    LicenseParagraph getParagraph() {
      ...
      final LicenseParagraph result = LicenseParagraph(lines.join(' '), currentParagraphIndentation!);
      ...
      return result;
    }

    ...
  }
}

どうやらこの LicenseEntryWithLineBreaks がスタイルの詳細を実装している部分です。 それを使わずに自分で LicenseParagraph を利用するクラスを作ると、他の細かな実装も自分でしないといけなくなります。

LicenseEntryWithLineBreaks肝となる部分 は画像のとおり面倒くさい感じです。 一文字ずつ確認しては計算などしていくループになっています。

f:id:kabochapo:20200827004955p:plain

case '\t': のところに currentLineIndent += 8; という加算処理があります。 行頭に \t(タブ文字)があるとインデントが 8 文字分になるように読めます。

一方、半角スペース 1 つはそのまま 1 文字分とされています(currentLineIndent += 1)。 でも 1 文字分のインデントって変ですね。よくあるのは 2 文字・4 文字・8 文字あたり。

とりあえず試しにスペース 2 つを行頭に加えてみたところ、インデントされませんでした。

ソースコードを読み進めると、その理由となる 箇所 がありました。

f:id:kabochapo:20200827005010p:plain

ここでインデントのレベルが計算されています。

  • currentLineIndent を 3 で割った数(整数)がインデントのレベル
  • ただし割る前の数が 10 を超えていればセンタリング

currentLineIndent += 8;8 はそのまま表示する文字数ではなく、インデントを計算する基になる値なんですね。 10 を超えていないので 3 で割って深さ 3 のインデントとして扱われることになります。

これでだいたい理解できました。

検証

あと少し疑問が…。

  • タブ文字が 8 なら、タブ文字 (8) + スペース (3) で計 11 になってセンタリングされるの?
  • 改行するには改行を 2 つ入れれば良いみたいだけれど、3 つ以上入れるとどうなるの?

などなど。

で、試しました。

f:id:kabochapo:20200827005024j:plain

まとめ

  • インデント
    • 行頭のスペース 3 個ごとに 1 インデント
    • 行頭のタブ文字 1 個で 2 インデント(スペース 8 個扱い)
  • センタリング & 太字
    • 行頭のスペース 11 個以上
      • タブ文字 2 個でも OK(スペース 16 個扱い)
      • タブ文字 1 個 + スペース 3 個でも OK(スペース 11 個扱い)
      • スペース 11 個以上ならいくつ増やしても変わりなし
    • 前の行との間が少し広くなる
  • 改行
    • 改行文字連続 2 個以上で 1 改行(3 個以上でも 1 改行のみ)
  • 行途中のスペース / タブ文字
    • 1 個ごとに 1 余白
  • 行途中の改行文字
    • 1 個で 1 余白(2 個以上は改行)
  • テキスト先頭の改行文字 1 個
    • 効果なし

ちょっと Markdown に近い感じがありますが、似たところがあるだけで扱いづらいですね。 ソースコードも頭の良い人が作ったんだろうなぁ…という(個人的に保守しにくく感じる難解な)書き方でした。

以上。