Flutter:「FittedBox」の中だと「Row」の「mainAxisAlignment」が機能しない
結論:FittedBoxを個々のWidgetに対して設定する
2022/12/4 Flutter エラー・バグ日記
Widgetを横に並べる「Row」で、地味にハマってしまったので記録。
各Widgetを画面幅内に均等配置する「MainAxisAlignment.spaceEvenly」のプロパティを設定したが、どうしても左寄せになってしまい、機能しなかった。
実現したかったこと
各Widget(この例では2つのText Widget)が、所定のサイズをハミ出ないようにしつつ、左右に均等配置したかった。
この時点のコードは以下のとおり。ハミ出ないようにする部分には、「FittedBox」クラスの「fit: BoxFit.scaleDown」プロパティを使用した。
// クラス名、メソッド名、プロパティ名(変数名)について、筆者が作成したもの(名前変更可のもの) // の名前の末尾には、大文字のオー「O」をつけています // ※ライブラリ(パッケージ)で予め決められているもの(名前の変更不可のもの)と、 // 自分で作成したもの(名前の変更可のもの)の区別をしやすくするため import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: "Test", theme: ThemeData.light(), home: SampleScreen(), ); } } class SampleScreen extends StatefulWidget { @override _SampleScreenState createState() => _SampleScreenState(); } class _SampleScreenState extends State<SampleScreen> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("TestApp"), ), body: Container( color: Colors.grey, height: 30.0, child: FittedBox( fit: BoxFit.scaleDown, child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, // ← これが機能しない children: [ Container( color: Colors.green, child: Text("左のWidget", style: TextStyle(fontSize: 40.0)), ), Container( color: Colors.yellow, child: Text("右のWidget", style: TextStyle(fontSize: 40.0)), ), ], ), ), ), ); } }
ちなみに、「MainAxisAlignment.」の部分は、「.spaceEvenly」に限らず、「.start」や「.end」であっても、同様に全て左寄せになってしまう。
Containerで幅を規定したり、IntrinsicWidthでラップしたりするも変わらず
こちらの情報によると、幅が指定されていないことが原因とのこと。
そこで、親WidgetのContainerにwidthを設定してみたが、今度は2つのWidgetが中央に寄ってしまい、やはり均等配置できなかった。
また、「IntrinsicWidth」というクラスでラップしても解消できるそうなので、試してみたが、これも状況は変わらずだった。
FittedBoxが原因らしい
自分のコードでは、「Row」を「FiitedBox」でラップしているので、そのせいかもしれないと思い、調べてみると、下記情報を見つけた。
まさにズバリの内容。
しかし、期待した回答は示されておらず、「FittedBox」内では、「Row」の「mainAxisAlignment」は機能しないので、「SizedBox」を間に挟むなどして、Widgetの間隔を手動で広げるしか無いとのこと。
「SizedBox」だと、間隔を固定値で設定することになるので、柔軟性に欠け、正直使用したくはない。。
FittedBox→Rowではなく、Row→FittedBoxの順番にしたら解消
画面幅に応じて、最適な間隔が設定されるような、もう少しスマートな方法が欲しい。。
考えた結果、個別のWidgetがサイズオーバーにならなければ良いので、Row全体をFittedBoxでラップするのではなく、Rowの中で、各Widgetを個別にFittedBoxでラップするよう変更してみた。
変更後のコードは以下のとおり。※変更部分にコメントを記載
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: "Test", theme: ThemeData.light(), home: SampleScreen(), ); } } class SampleScreen extends StatefulWidget { @override _SampleScreenState createState() => _SampleScreenState(); } class _SampleScreenState extends State<SampleScreen> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("TestApp"), ), body: Container( color: Colors.grey, height: 30.0, // ここにあった FittedBox を削除 child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ // 各 Text Widget を個別に FittedBox でラップ FittedBox( fit: BoxFit.scaleDown, child: Container( color: Colors.green, child: Text("左のWidget", style: TextStyle(fontSize: 40.0)), ), ), FittedBox( fit: BoxFit.scaleDown, child: Container( color: Colors.yellow, child: Text("右のWidget", style: TextStyle(fontSize: 40.0)), ), ), ], ), ), ); } }
この方法により、無事、ハミ出しを抑えつつ、均等配置を実現できた。
FittedBox→Rowの順番だと機能しない理由
下記日記でも取り上げたが、
恐らく、「Container」クラスの公式情報の「Layout behavior」の項目のところに記載のある、
「Containerは、配置(alignment)を尊重し、子に合わせてサイズを調整し、幅、高さ、制約を尊重し、親に合わせて拡張し、可能な限り小さくしようとします。」(※Google翻訳を使用)
という内容が根拠だと思われる。
当初のコードだと、Container→FittedBox→Row→Text Widgetの順なので、FittedBoxができるだけRowを小さくしようとし、親であるContainerもそれに合わせて縮むので、結果、2つのText Widgetギリギリのサイズまで小さくなってしまったと思われる。
※Containerにはグレーの色を付けているが、冒頭の図ではグレーの部分が全く見えないことからも、Containerが2つのText Widgetギリギリまで縮んでいるのがわかる。
これに対し、Container→Row→FittedBox→Text Widgetの順に変えると、RowがFittedBoxの制約から逃れることができるため、Containerの幅が画面いっぱいまで広がり、その中で均等配置できるようになるのだろう。
サイズ関連の話は、Flutter熟練者の方々には常識の事かもしれないのだが、、手探りでやっていると、こういう基本的なことが未だに難しかったりする。。
\一般的なエラー対処法をまとめた記事はこちら/
リリースしたアプリ(全てFlutterで開発)
個人アプリ開発で役立ったもの
おすすめの学習教材
\超初心者向けでオススメな元Udemyの講座/
\キャンペーン時を狙えば安価で網羅的な内容が学べる(日本語訳あり)/
\Gitの基礎について無料で学べる/
おすすめの学習書籍
\実用的。image_pickerに関してかなり助けられた/
\Dartの基礎文法を素早くインプットできる/