実践Flutter

Flutterでスマホを中心にアプリ制作していきます。

Flutterのテキストの出力と装飾・Text

はじめに

本記事ではFlutterの手始めとして、テキストを出力するTextWidgetの基本的な使い方を見ていきます。

これを通して徐々にWidgetの設定をしていく雰囲気をつかんでいければOKです。

最初に意味のわからないおまじないのように見える部分もあるかもしれませんが、それは徐々に慣れていって意味がわかってきたりしますので、細いところはあまり気にしなくて大丈夫です。





Textウィジェット

まずはいろいろ試していく土台になるコードを作っていきます。単純なコードがいいのでエントリポイントのrunAppに対して単純にTextウィジェットを返すようにしてみます:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context){
    return const Text("text");
  }
}

デフォルトのmain.dartの内容からMyAppの実装以下を全部消して、buildメソッドのところでTextウィジェットを返す構成にしてみました。

基本的にウィジェットをツリー状に構成していくとき、子ウィジェットのbuild関数が順次呼ばれて数珠つなぎになります。なのでbuildメソッドのところにウィジェットの構成を記述していきます。

これでコンパイルも通りますし問題なさそうです。でも実行してみると下記のような実行時エラーをもらってしまいます:


f:id:linkedsort:20211013215851p:plain

いきなりいやらしい感じのメッセージですね。

No Directionality widget found. 以下、エラーメッセージにはざっと下記のようなことが書いてあります:

Directionalityウィジェットがみつからない。

RichTextはDirectionalityウィジェットを(ウィジェットツリーの)先祖に持たないといけない。

Directionalityは典型的にはMaterialAppやWidgetsAppをウィジェットツリーのトップに置くことで導入される。


つまりTextウィジェット(実体はRichTextウィジェットの様ですね)を使うにはDirectionality、あるいはこれを継承しているウィジェットをトップに持ってきて、その配下にTextウィジェットを置かなければならないということです。

MaterialAppはAndroidのマテリアルデザイン調のUIを実現するものでもう少しあとの記事で扱います。ここでは素直にDirectionalityウィジェットを使ってみます

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context){
    return const Directionality(           //追加
      textDirection: TextDirection.ltr,   //必須パラメタなので追加
      child: Text("text"),
    );
  }
}

DirctionalityウィジェットはtextDirectionとchildが必須です。textDirectionは文字列の方向を与えるもので、ここは素直にleft to right(=左から右、ltr)を与えておきます。childは子ウィジェットですので、ここにTextウィジェットを入れます。

こうすることで、無事にテキストが表示されるようになりました。

これからTextウィジェットに色々と属性値を与えて表示のされ方の変化を見ていこうと思います。少し手を加えて3行表示できるようにColumnウィジェットを追加して、縦にTextウィジェットを並べます:

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Directionality(
      textDirection: TextDirection.ltr,
      child:Column(
          children: const [
            Text("This is an English sentence."),
            Text("It's nice to code in Flutter."),
            Text("日本語の文章も問題なく出力できます")
          ]
    )
    );
  }
}

Columnウィジェットはchildrenにウィジェットのリストを受け取って、リストの要素のウィジェットを縦に並べるレイアウト系ウィジェットです。Columnについての詳しい記事はまた後ほど。今回はTextに集中します。

上記コードの出力結果はこのようになります:

f:id:linkedsort:20211013224035p:plain

日本語もきれいに表示されていますね。

それではこのコードをベースに、Textウィジェットの属性を色々と変えていってみます。

以下ではベースになる上記コードのColmnウィジェットの部分のみをサンプルコードとして示します。お手元で試される場合は、「child:Column」以下の行を入れ替えてみて下さい。


ところでサンプルコード中の「const MyApp({Key? key}) : super(key: key);」とは
(気にならない方は一旦スキップでOKです)

これがないと怒られますよね。これはDart入門の「継承」の回で触れた『親クラスがデフォルトコンストラクタ(引数のないコンストラクタ)を持っていない場合、明示的に親のコンストラクタを呼び出して初期化しなければならない』というルールによって必要なものです。

つまり親クラスのStatelessWidgetは引数なしのコンストラクタを持っていないということですね。なのでこの記述は必要になるのです。

MyApp({Key? key})はKey型の引数があってもなくてもよく、nullでも良いコンストラクタという意味です。KeyはウィジェットのIDを表すもので必要に応じて明示的に親にID何番のウィジェットがいます、ということを伝える形になっています。明示的に指定しなくても自動的に付番されるので必要がなければKeyを意識しなくても大丈夫です。

そして親のコンストラクタsuper(key:key)が初期化リストのところにあります。初期化リストに書かねばならないというルールでしたからね。MyAppのコンストラクタの本体を書き足したい場合は、初期化リストのsuper(key: key){ 本体 }のように本体のブロックを書き足します。

 

Textウィジェットの属性

Textの様々な出力様式はstyle属性値にTextStyleクラスのインスタンスを与えることで指定します。TextStyleには色や太字など様々なスタイルを表現する属性が用意されています。

色と太字

Textの色はcolor属性で与えます。color属性はColorクラスのインスタンスを値にとります。

Colorのインスタンスは:

  • Colors.XXXで定数定義されている色の名前で指定する方法(下のコードの1番上)
  • Color(int)のコンストラクタの引数に数値で表す色を指定する方法(下のコードの2番目)

があります。

Color(int)の引数は16進数で2桁ごとにRGBの数値+最後の2桁は透明度です。透明度は下記コードのようにFFだと不透明、数字を減らすとだんだん透明度が上がります。

Textの太さはfontWeight属性で与えます。FontWeight.boldを与えると太字になります。

      child:Column(
          children: const [
            Text("This is an English sentence.",
              style: TextStyle(color: Colors.blueAccent)
            ),
            Text("It's nice to code in Flutter.",
              style: TextStyle(color: Color(0xB8C045FF))
            ),
            Text("日本語の文章も問題なく出力できます",
                style: TextStyle(fontWeight: FontWeight.bold)
            )
          ]
    )

出力結果は下記の通りです:

f:id:linkedsort:20211013230747p:plain


フォントサイズ、イタリック、背景色

フォントのサイズはfontsize属性に数値を与えることで指定できます。

イタリック(斜字体)にするにはfontStyle属性にFontStyle.italicを与えます。

テキストの背景色を変えるにはbackgroundColor属性にColorクラスのインスタンスを与えます。

それぞれの例を以下に挙げます:

      child:Column(
          children: const [
            Text("This is an English sentence.",
              style: TextStyle(fontSize: 24)
            ),
            Text("It's nice to code in Flutter.",
              style: TextStyle(fontStyle: FontStyle.italic)
            ),
            Text("日本語の文章も問題なく出力できます",
                style: TextStyle(backgroundColor: Colors.blueAccent)
            )
          ]

出力結果は下記の通りです:

f:id:linkedsort:20211013234517p:plain



下線、二重下線、取り消し線

下線、二重下線、取り消し線は下記のようにして描画できます。

ほかにも波線や点線などもdecorationStyle属性を変えることで出力できます。

      child:Column(
          children: const [
            Text("This is an English sentence.",
              style: TextStyle(decoration: TextDecoration.underline)
            ),
            Text("It's nice to code in Flutter.",
              style: TextStyle(decoration: TextDecoration.underline, decorationStyle: TextDecorationStyle.double)
            ),
            Text("日本語の文章も問題なく出力できます",
                style: TextStyle(decoration: TextDecoration.lineThrough)
            )
          ]

出力結果は下記の通りです:

f:id:linkedsort:20211014003816p:plain





おわりに

Flutterに慣れていくには、まずプロジェクトを実行して、いじってみることです。

このときいきなり上で書いた赤と黄色の画面がでて、嫌になってしまうという方も一定数いるのではないでしょうか。なのでまずは最低限動くコードを用意して、そこからちょっとずつ自分の思ったとおりに変えていくのが最初の一歩としていいですね。

それじゃあ調子が出たところで、どんどん行きましょう!



(以下、余談です)

それにしても最初の一歩として出がちなあの赤黄色の画面、あれはなんとかしたほうがいいと思ってしまいますね。設計上のエレガントさは分かるのですがMateriialAppやDirectionalityなんかすっ飛ばしてTextウィジェットをrunAppに直撃させても、おすすめじゃないにしても最低限画面を出してしまうくらいの胆力は見せてもらってもいいですよね。

もちろんそれはすぐに必要なくなるのですが、最初の一歩を踏み出したばっかりの皆さんを踏みつけるようなことをしては、何割かの人はそこでいなくなってしまいそうです。

同様にStatelessWidgetのコンストラクタも推奨しないにしてもデフォルトコンストラクタを用意しておけばいいのにと思ってしまいます。これはちょっと議論になってしまいそうですが、最初から「const MyApp({Key? key}) : super(key: key);」と書けと言われると嫌ですよね。ボイラープレートコード(決り文句のグダグダした意味不明なコード)を最小限にした、というのもFlutterの謳い文句なんですが、早速これかよと思われてしまいかねません

子クラスに必要な実装を強制するのは、オブジェクト指向の美しい設計を実現する上で素晴らしい仕組みなのですが、最初の入口ですからね。

などと思ってしまいます。でもちゃんとプロジェクト実行できた方は進むにつれて気にならなくなりますので大丈夫。次に進みましょう~

f:id:linkedsort:20211107142003j:plain