そのDefaultTextStyle本当に大丈夫？？

# はじめに
この記事は[Flutter Advent Calendar 2021](https://qiita.com/advent-calendar/2021/flutter)の14日目の記事です。


DefaultTextStyleつかっていますか？

子のTextに複数の同じTextStyleを設定したいときに親をDefaultTextStyleで囲って下のように書くことで、子のすべてのTextに対して同じTextを設定できて毎回TextでTextStyleを冗長に書くことを避けることができます。

```dart
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Sample'),
      ),
      body: DefaultTextStyle(
        style: TextStyle(
          fontSize: 40.0,
          color: Colors.blue,
        ),
        child: Column(
          children: [
            Text('こんにちは'),
            Text('さようなら'),
            Text('Hello, world.'),
          ],
        ),
      ),
    );
  }
```

<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185927/daaf0678-3de0-b402-456f-099d62ebbe8b.png  width=300>


# Materialと組み合わせたとき
最初に以下のようなコードを用意します。

```dart
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Sample'),
      ),
      body: DefaultTextStyle(
        style: TextStyle(
          fontSize: 14.0,
          color: Colors.black,
        ),
        child: Column(
          children: [
            Text('こんにちは'),
            Text('さようなら'),
            Text('Hello, world.'),
          ],
        ),
      ),
    );
  }
```

<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185927/ac03a551-55a5-e1e1-168f-5ad352e72cca.png  width=300>



では、次に何か背景色がついた領域に `Text('さようなら')`と `Text('Hello, world.')`を入れて、タップ可能にしてタップ時にrippleを出したいときにMaterialを使ってこういうコードを書いたとします。

```dart
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Sample'),
      ),
      body: DefaultTextStyle(
        style: TextStyle(
          fontSize: 14.0,
          color: Colors.black,
        ),
        child: Column(
          children: [
            Text('こんにちは'),
            Material(
              color: Colors.grey,
              child: InkWell(
                onTap: () {},
                child: Column(
                  children: [
                    Text('さようなら'),
                    Text('Hello, world.'),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
```

<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185927/3dd13804-fe69-a7a1-f33e-de7b969de0fb.gif  width=300>


うまく動いてそうですね。
<br>
<br>
<br>
<br>
<br>
ほんとでしょうか、、、？

ちょっとTextStyleで色を変えてみましょう。

```dart
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Sample'),
      ),
      body: DefaultTextStyle(
        style: TextStyle(
          fontSize: 14.0,
          color: Colors.blue, // Colors.black → Colors.blueに変更
        ),
        child: Column(
          children: [
            Text('こんにちは'),
            Material(
              color: Colors.grey,
              child: InkWell(
                onTap: () {},
                child: Column(
                  children: [
                    Text('さようなら'),
                    Text('Hello, world.'),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
```

<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185927/052f8d26-f40b-e353-4356-2608bef74d9b.png  width=300>

なんだか`Colors.grey`で囲った中のTextにTextStyleが適用されてなさそうですね🤔


これはMaterialで囲っているのが原因です。MaterialではtextStyleのプロパティが用意されていて、何も設定しないnullの状態だと中で勝手に別のTextStyleが使われる実装になっています。なので、親では `Colors.blue`のTextStyleを設定していますがMaterialで囲ったTextは別のTextStyleが設定されていてこのような実行結果になっています。

`material.dart`の中をちょっと覗いてみると、 `AnimatedDefaultTextStyle`というDefaultTextStyleでwrapされており、これによって外側で自分が設定したDefaultTextStyleが効いてくれません。

```dart:material.dart
      contents = AnimatedDefaultTextStyle(
        style: widget.textStyle ?? Theme.of(context).textTheme.bodyText2!,
        duration: widget.animationDuration,
        child: contents,
      );
```

青や極端に大きい文字をDefaultTextStyleで設定した場合は気が付きやすいとおもいますが、 `AnimatedDefaultTextStyle`で設定されているようなTextStyleを自分でも設定した場合違いに気が付かず意図しないTextStyleが設定されてしまうことがあるので注意が必要です。

# 余談
そもそもFlutterではどこでTextStyleが設定されてTextが描画されているのでしょうか。
みなさん画面を作るときにまず最初に書き始めるのが `Scaffold`ではないでしょうか。
親を`Scaffold`で囲わないでTextを配置して下のような画面をみたことがある人はいませんか？


```dart
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text('あいうえお'),
    );
```

<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185927/3018ec5e-8359-9f89-5230-572ad2b8f7f0.png  width=300>

このWidgetを `Scaffold`で囲うと

<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185927/792cf5a1-cea4-5254-939e-c4bd205c77aa.png  width=300>

ちゃんと表示されましたね。

これはなぜなんでしょうか。

`scaffold.dart`の中を覗いてみると、 `_ScaffoldScope`でラップされた `Material`をreturnさせているのがわかります。
そのため、`Scaffold`で囲わないとさきほどの `AnimatedDefaultTextStyle`も適用されず変なTextが表示されていたわけです。

```dart:scaffold.dart
    return _ScaffoldScope(
      hasDrawer: hasDrawer,
      geometryNotifier: _geometryNotifier,
      child: ScrollNotificationObserver(
        child: Material(
          color: widget.backgroundColor ?? themeData.scaffoldBackgroundColor,
          child: AnimatedBuilder(animation: _floatingActionButtonMoveController, builder: (BuildContext context, Widget? child) {
            return CustomMultiChildLayout(
              delegate: _ScaffoldLayout(
                extendBody: _extendBody,
                extendBodyBehindAppBar: widget.extendBodyBehindAppBar,
                minInsets: minInsets,
                minViewPadding: minViewPadding,
                currentFloatingActionButtonLocation: _floatingActionButtonLocation!,
                floatingActionButtonMoveAnimationProgress: _floatingActionButtonMoveController.value,
                floatingActionButtonMotionAnimator: _floatingActionButtonAnimator,
                geometryNotifier: _geometryNotifier,
                previousFloatingActionButtonLocation: _previousFloatingActionButtonLocation!,
                textDirection: textDirection,
                isSnackBarFloating: isSnackBarFloating,
                snackBarWidth: snackBarWidth,
              ),
              children: children,
            );
          }),
        ),
      ),
    );
  }
}
```

Materialで囲うとき、textStyleに特になにも指定しなかった場合勝手にTextStyleが設定されてしまうことは覚えておきましょう。