はじめに
本記事ではFlutterのContainerウィジェットについてポイントを述べていきます。
Containerウィジェットは囲みを作るためのパーツです。スペースなどを調整するレイアウト系の働きもありますが、丸角の枠を作ったり背景色を塗ったりという目に見える側面もあります。丁度HTMLのdivタグで作る枠のようなものです。
なので必須とはいいませんが、そこそこかっこいいUIデザインをしていく上では欠かせないものになります。
Container
Containerウィジェットは囲みを作るレイアウト用の要素です。指定された大きさの囲みを作る、背景をペイントする、隙間を作るなどの用途で使います。
まずは色々試すためのベースのコードを作ります。試してみるためにはFlutterの新しいプロジェクトを作って、main.dartを下記の内容に置き換えてみてください:
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) { //(1) 文字列出力に必要 return Directionality( textDirection: TextDirection.ltr, //(2) 要素を縦に並べる child:Column( children: [ //(3) Containerウィジェットの例 Container( child: const Text('hello'), height:100, width:200, color: Colors.amber ), Container( child: const Text('hello'), height:100, width:200, color: Colors.blueGrey ), Container( child: const Text('hello'), height:100, width:200, color: Colors.deepOrange ), ] ) ); } }
このコードの出力結果は下のようになります:
コードのポイント
コードのコメントに付けた番号ごとにポイントを述べていきます。
(1) 文字列出力に必要
最初にDirectionalityで囲んでいるのは前回記事と同様、Textウィジェットを使用するためです。TextDirection.ltrによって左から右の横書きを指定しています。
サンプルの便宜上置いているだけです。Containerを理解する上では、あまり気にされなくてOKです。
(2) 要素を縦に並べる
前回使用したColumnウィジェットを挟んでいます。これによってchildrenで与えたウィジェットのリストの要素を縦に並べて表示します。
(3) Contaierウィジェットの例
Columnのchildrenとして3つのContainerをリストの要素として与えています。
Containerの使い方は単純で、高さや幅や色などを指定すると、そのサイズ・形状・色の枠ができるというものです。
ベースのコードでは各Containerに高さ100・幅200のサイズ、色、そして中身としてTextウィジェットを与えています。
主なContainerの属性値は以下の通りです:
属性 | 意味 |
---|---|
child | 中身の子ウィジェット |
height | 高さ |
width | 幅 |
alignment | 中身の位置 |
padding | 内側の余白 |
margin | 外側の余白 |
transform | 回転など |
decoration | 影やグラデーションの装飾 |
以下の節ではContainerに与える属性値をいろいろと変えて、出力の変化を見ていきます。各ンプルのコードは、上記のベースコードのうちColumnウィジェット以下の部分のみを示していきます。
中身の配置
Containerのalignment属性は、そのContainerの中身(child)に指定したウィジェットがどの位置に置かれるかを指定します。
前節のベースコードではalignmentを指定していませんので、Textは全て左上に寄せられていますね。
これを下記のように変えてみます:
child:Column( children: [ Container( child: const Text('hello'), height:100, width:200, alignment: Alignment.center, color: Colors.amber ), Container( child: const Text('hello'), height:100, width:200, alignment: Alignment.bottomLeft, color: Colors.blueGrey ), Container( child: const Text('hello'), height:100, width:200, alignment: Alignment.topRight, color: Colors.deepOrange ), ] )
これを実行した結果は以下になります:
文字の表示位置がそれぞれど真ん中、左下、右上になっていますね。
alignmentの値と位置の関係は以下の通りです:
alignment | 位置 |
---|---|
Alignment.topLeft | 左上 |
Alignment.topCenter | 真ん中上 |
Alignment.topRight | 右上 |
Alignment.centerLeft | 左中央 |
Alignment.center | 真ん中 |
Alignment.centerRight | 右中央 |
Alignment.bottomLeft | 左下 |
Alignment.bottomCenter | 真ん中下 |
Alignment.bottomRight | 右下 |
内側の隙間
Containerの内側に余白を作るにはpadding属性に値を与えていきます。丁度HTML/CSSの余白と同じです。
paddingの値はEdgeInsets型をとります。EdgeInsetsは上下左右の端(エッジ)に関する数値をまとめるクラスで、下記の表のようにいくつかの名前付きコンストラクタを持っています。こういうと複雑そうですが、よく見ると単純です:
コンストラクタ | パラメタ |
---|---|
EdgeInsets.all | double型のパラメタをとり、上下左右が同一の数値の場合に使用 |
EdgeInsets.symmetric | verticalが上下、horizontalが左右の数値を指定。上下・左右の組でそれぞれ同一の数値の場合に使用 |
EdgeInsets.only | top、left、right、bottomでそれぞれ上左右下のそれぞれの数値をバラバラに設定する場合に使用 |
次に使用例を示します:
child:Column( children: [ Container( child: const Text('hello'), height:100, width:200, color: Colors.amber, padding:const EdgeInsets.all(30), ), Container( child: const Text('hello'), height:100, width:200, color: Colors.blueGrey, padding:const EdgeInsets.symmetric(vertical:10, horizontal: 20), ), Container( child: const Text('hello'), height:100, width:200, color: Colors.deepOrange, padding:const EdgeInsets.only(top: 40, left: 60), ), ] )
これを実行した結果は以下になります:
一番上の箱は上下左右とも30、真ん中の箱では上下が10・左右が20、一番下の箱では上が40、左が60の設定でそれぞれ余白を入れています。
外側の隙間
Containerの外側に余白を作るにはmargin属性に値を与えていきます。marginの値はpaddingと同様、EdgeInsets型をとります。
下記コードではpaddingで示したコードのpaddingを全部marginに入れ替えてみました:
child:Column( Container( child: const Text('hello'), height:100, width:200, color: Colors.amber, margin:const EdgeInsets.all(30), ), Container( child: const Text('hello'), height:100, width:200, color: Colors.blueGrey, margin:const EdgeInsets.symmetric(vertical:10, horizontal: 20), ), Container( child: const Text('hello'), height:100, width:200, color: Colors.deepOrange, margin:const EdgeInsets.only(top: 40, left: 60), ), ] )
これを実行した結果は以下になります:
それぞれ隙間が空いていますね。一番上の箱の余白は全辺30になっていて、二番目の左右が20なのに横軸が揃っているのは、今回の場合縦に1つしかウィジェットがなく余白を含めて中央揃えになっているためです。左右対称なので横に余白があるのですが違いが見えません。
それに比べて一番下の箱は左の余白が60、右が0で差があるので箱の表示は中央からずれています。
丸角
decoration属性にBoxDecorationインスタンスを与えることで丸角など様々にContainer枠の形を変えることができます。
ここでは一番使われそうな丸角を試してみます:
child:Column( children: [ Container( child: const Text('hello'), height:100, width:200, alignment: Alignment.center, decoration: BoxDecoration( color: Colors.amber, borderRadius: BorderRadius.circular(10), ), ), Container( child: const Text('hello'), height:100, width:200, alignment: Alignment.center, decoration: BoxDecoration( color: Colors.blueGrey, borderRadius: BorderRadius.circular(20), ), ), Container( child: const Text('hello'), height:100, width:200, alignment: Alignment.center, decoration: BoxDecoration( color: Colors.deepOrange, borderRadius: BorderRadius.circular(30), ), ), ] )
これを実行した結果は以下になります:
丸角の丸みの半径を10、20、30と徐々に増やしています。
回転
Containerのtransform属性は形状を変形させるためのものです。変形は線形代数的な座標変換を任意の4次元行列を与えることで指定します。
とはいえ任意の変形が必要な場面はほとんどないと思いますので、ここでは回転に特化したMatrix4.rotationX/Y/Zを与える例を見ていきます。
Matrix4.rotationX/Y/Z(=名前付きコンストラクタ)は下記の表にある通りの回転を指定する行列になります:
コンストラクタ | 行列の作用 |
---|---|
Matrix4.rotationX(double r) | rラジアン分縦回転 |
Matrix4.rotationY(double r) | rラジアン分横回転 |
Matrix4.rotationZ(double r) | rラジアン分回転 |
縦回転、横回転の意味が伝わりにくいと思いますので、下記のサンプルコードと結果を参照してみてください:
child:Column( children: [ Container( child: const Text('hello'), height:100, width:200, alignment: Alignment.center, decoration: BoxDecoration( color: Colors.amber, borderRadius: BorderRadius.circular(15), ), transform: Matrix4.rotationX(0.7), ), Container( child: const Text('hello'), height:100, width:200, alignment: Alignment.center, decoration: BoxDecoration( color: Colors.blueGrey, borderRadius: BorderRadius.circular(15), ), transform: Matrix4.rotationY(0.7), ), Container( child: const Text('hello'), height:100, width:200, alignment: Alignment.center, decoration: BoxDecoration( color: Colors.deepOrange, borderRadius: BorderRadius.circular(15), ), transform: Matrix4.rotationZ(0.7), ), ] )
これを実行した結果は以下になります:
この結果から、縦と横の回転の意味合いは伝わるかと思います。この縦横回転も使う場面が限られるかと思います。
a / 180 * pi
ここで「pi」は円周率を表します。これを使用するためには「import 'dart:math'」を冒頭に書いてdartのmathライブラリをインポートしておく必要があります。
グラデーション
Containerにグラデーションをつけるには丸角のところでも利用したdecoration属性にBoxDecorationの値を指定します。
BoxDecorationのgradient属性値に下記表のクラスの値を与えることで様々な種類のグラデーションを表現できます:
gradient値 | 意味 |
---|---|
LinearGradient | 線形グラデーション。開始点から終了点に向かって色が徐々に変化 |
RadialGradient | 中央の点から始まって外側に向かって色が変化 |
SweepGradient | 中心の点を軸にぐるっと回転するように色が変化 |
これも下記サンプルコードとその出力結果を見比べていただくと意味が伝わると思います:
child:Column( children: [ Container( child: const Text('hello'), height:100, width:200, alignment: Alignment.center, decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), gradient: const LinearGradient( begin: Alignment.topRight, end: Alignment.bottomLeft, colors: [ Colors.lightGreenAccent, Colors.amber ] ), ), ), Container( child: const Text('hello'), height:100, width:200, alignment: Alignment.center, decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), gradient: const RadialGradient( colors: [ Colors.grey, Colors.blueGrey, ] ), ), ), Container( child: const Text('hello'), height:100, width:200, alignment: Alignment.center, decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), gradient: const SweepGradient( colors: [ Colors.amberAccent, Colors.deepOrange, Colors.amberAccent, ] ), ), ), ] )
これを実行した結果は以下になります:
色は2つ以上、複数の色を与えることができます。同じ色を適度に混ぜることで色の変化の割合を調整したりもできます。3番めの例では3色の色を与えて、最初と最後の色を同一にすることで、一周回転してきたときに同じ色でスムーズにつながるようにしています。
影
影をつけるにも丸角やグラデーションと同様、decoration属性を指定します。decoration属性に与えるBoxDecoration型の値のなかでboxShadow属性を指定します。
boxShadowの値は影の情報を表現するBoxShadow型で与えます。BoxShadow型の属性値は主に下記の通りです:
属性値 | 意味 |
---|---|
color | 影の色。灰色であればColors.greyなどで指定 |
spreadRadius | 影の広がり半径。数値を大きくすると影が大きくなります |
blurRadius | 影のぼかし半径。数値を大きくするとぼやけ方が増します |
offset | Offset(x, y)で影のずれ方、位置を指定します。数値を大きくするほど右・下にずれていきます。マイナスの数値で逆に上がります |
これらも言葉の説明よりもコードと結果の対応を見ていただくと雰囲気が伝わるかと思います。
ここでは丸角とグラデーションも合わせて複合的に効果を入れる例にしていますのですこしコードが長いですが、特にshadowの部分に注目してみてください:
child:Column( children: [ Container( child: const Text('hello'), height:100, width:200, alignment: Alignment.center, margin: const EdgeInsets.all(20), decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), gradient: const LinearGradient( begin: Alignment.topRight, end: Alignment.bottomLeft, colors: [ Colors.lightGreenAccent, Colors.amber ] ), boxShadow: const [ BoxShadow( color: Colors.grey, spreadRadius: 1, blurRadius: 5, offset: Offset(4,4) ) ] ), ), Container( child: const Text('hello'), height:100, width:200, alignment: Alignment.center, margin: const EdgeInsets.all(20), decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), gradient: const LinearGradient( begin: Alignment.topRight, end: Alignment.bottomLeft, colors: [ Colors.blue, Colors.blueGrey ] ), boxShadow: const [ BoxShadow( color: Colors.grey, spreadRadius: 5, blurRadius: 5, offset: Offset(4,4) ) ] ), ), Container( child: const Text('hello'), height:100, width:200, alignment: Alignment.center, margin: const EdgeInsets.all(20), decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), gradient: const LinearGradient( begin: Alignment.topRight, end: Alignment.bottomLeft, colors: [ Colors.amberAccent, Colors.deepOrange, ] ), boxShadow: const [ BoxShadow( color: Colors.grey, spreadRadius: 1, blurRadius: 20, offset: Offset(4,4) ) ] ), ), ] )
これを実行した結果は以下になります:
1番上はスタンダードな影、真ん中は濃い目に拡げた影、下はほわっとぶらした影です。色々と数値を変えて試してみてください。
お好みの影は見つかりましたでしょうか。
おわりに
Containerの例を色々とみてきました。最後の方の丸角やシャドウを駆使するとクールなUIが作れそうですよね。
そして記述の雰囲気はかなりHTML/CSSと似ています。似ていますが、非常に表現力の高いプログラミング言語でありますし、カチッと構文が決まっていて歴史的な経緯から非常にゆるい作りの言語になっているHTMLよりも安心感があります。
今現在Googleが進めているWebをはじめとするFlutterのマルチプラットフォーム化や、次世代OSのことを考え合わせると、将来的にはHTML/CSS/javascriptの位置に取って代わるものとしてFlutterが直接ブラウザ上でレンダリングされる将来も見えてくるのでは、と思えてきます。
HTML/CSS/javascriptの世界は歴史的に積み上がった技術的負債も多く、今後一気にダイナミックなコンテンツの需要が増えたりするときに、耐えられないのではないかと思いますが、いかがでしょうか。耐えられないならどうするか、という議論になったとき、一番取って代わる可能性がいま高いのはFlutterではないかなと思います。
これはまた将来への含みということで、推移を見守りましょう。