はじめに
この記事はFlutter Advent Calendar 2021の14日目の記事です。
DefaultTextStyleつかっていますか?
子のTextに複数の同じTextStyleを設定したいときに親をDefaultTextStyleで囲って下のように書くことで、子のすべてのTextに対して同じTextを設定できて毎回TextでTextStyleを冗長に書くことを避けることができます。
@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.'),
],
),
),
);
}
Materialと組み合わせたとき
最初に以下のようなコードを用意します。
@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.'),
],
),
),
);
}
では、次に何か背景色がついた領域に Text('さようなら')
と Text('Hello, world.')
を入れて、タップ可能にしてタップ時にrippleを出したいときにMaterialを使ってこういうコードを書いたとします。
@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.'),
],
),
),
),
],
),
),
);
}
うまく動いてそうですね。
ほんとでしょうか、、、?
ちょっとTextStyleで色を変えてみましょう。
@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.'),
],
),
),
),
],
),
),
);
}
なんだかColors.grey
で囲った中のTextにTextStyleが適用されてなさそうですね🤔
これはMaterialで囲っているのが原因です。MaterialではtextStyleのプロパティが用意されていて、何も設定しないnullの状態だと中で勝手に別のTextStyleが使われる実装になっています。なので、親では Colors.blue
のTextStyleを設定していますがMaterialで囲ったTextは別のTextStyleが設定されていてこのような実行結果になっています。
material.dart
の中をちょっと覗いてみると、 AnimatedDefaultTextStyle
というDefaultTextStyleでwrapされており、これによって外側で自分が設定したDefaultTextStyleが効いてくれません。
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を配置して下のような画面をみたことがある人はいませんか?
@override
Widget build(BuildContext context) {
return Center(
child: Text('あいうえお'),
);
このWidgetを Scaffold
で囲うと
ちゃんと表示されましたね。
これはなぜなんでしょうか。
scaffold.dart
の中を覗いてみると、 _ScaffoldScope
でラップされた Material
をreturnさせているのがわかります。
そのため、Scaffold
で囲わないとさきほどの AnimatedDefaultTextStyle
も適用されず変なTextが表示されていたわけです。
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が設定されてしまうことは覚えておきましょう。