Web でアレやる時に Flutter だとコレやるリスト
intro
Web 開発者が Flutter を触るときに、「web のアレを flutter でやる時はどうするんだっけ」リストを作る。
思いついたものを一旦書き出し、今後も追記していく予定。
基本的には下記のリンク先を見れば解決すると思うが、学習も兼ねてサンプルを貼り付けてみる。
https://flutter.dev/docs/development/ui/widgets
<p>,<span>
が欲しい
https://flutter.dev/docs/development/ui/widgets/text
基本的に TextNode が欲しい場合は Text
Widget を使えば ok。
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text(
'Hello, How are you?',
);
}
}
連続した文章(web で言えば inline 方向に対して連続な)文字列の中で、特定の文字列に対してスタイルを当てたいという場合は RichText
を使う。
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RichText(
text: TextSpan(
text: 'Hello ',
style: DefaultTextStyle.of(context).style,
children: <TextSpan>[
TextSpan(text: 'This '),
TextSpan(text: 'is', style: TextStyle(fontWeight: FontWeight.bold)),
TextSpan(text: ' DOG!!'),
],
),
);
}
}
CSS かましたい
Widget 単位でかましたい場合、基本的には各 Widget に備わっている style
プロパティに対して、各 Style Object を渡してやるような感じだ。
基本的には型情報を参考にしてオブジェクトを組み立ていく。
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text('Hello, How are you?',
style: TextStyle(
background: Paint()..color = Colors.red, color: Colors.white));
}
}
全体的なスタイルに関する設定はこの辺を見ると良いと思う。
https://flutter.dev/docs/development/ui/widgets/styling
<img>
が欲しい
local の画像を読み込む方法と、ネットワーク越しの画像を読み込む方法がある。
local
対象の dir から画像を読み込む。
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Image.asset('assets/images/placeholder_image.png');
}
}
対象の dir は pubspec.yaml
に記述する。
# The following section is specific to Flutter.
flutter:
# To add assets to your application, add an assets section, like this:
assets:
- assets/images/
$ ls -G ./assets/images/placeholder_image.png
./assets/images/placeholder_image.png
ネットワーク
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return const Image(
image: NetworkImage(
'https://flutter.github.io/assets-for-api-docs/assets/widgets/owl.jpg'),
);
}
}
アイコンつけたい
<input>
が欲しい
基本ここ見れば ok
https://flutter.dev/docs/development/ui/widgets/input
<input>
に placeholder つけたい
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: SizedBox(
width: 300,
child: TextFormField(
decoration: const InputDecoration(
hintText: 'What do people call you?',
),
),
),
);
}
}
ラベルやアイコンなども簡単に付けられる。
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: SizedBox(
width: 300,
child: TextFormField(
decoration: const InputDecoration(
icon: Icon(Icons.person),
hintText: 'What do people call you?',
labelText: 'Name *',
),
),
),
);
}
}
focus すると色が変わって placeholder がつく。
<button>
が欲しい
ボタンの種類に応じていくつか Widget が用意されている。この中から望ましいものを選んで使うと良い。
https://flutter.dev/docs/development/ui/widgets/material
border-radius したい
Container + BoxDecoration
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 300.0,
height: 300.0,
decoration: BoxDecoration(
color: const Color(0xff7c94b6),
border: Border.all(
color: Colors.black,
width: 8,
),
borderRadius: BorderRadius.circular(12),
),
);
}
}
画像をクリップ(clip)したい / 画像に border-radius したい
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(color: Colors.redAccent),
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Image(
image:
NetworkImage('https://www.tutorialkart.com/img/hummingbird.png'),
),
),
);
}
}
background-image が欲しい
Container + BoxDecoration + DecorationImage
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 300,
height: 300,
alignment: Alignment.center,
decoration: BoxDecoration(
image: const DecorationImage(
image:
NetworkImage('https://www.tutorialkart.com/img/hummingbird.png'),
),
border: Border.all(
color: Colors.black,
width: 8,
),
borderRadius: BorderRadius.circular(12),
),
child: Text(
'feawffeafwefwaefawdogd',
style: TextStyle(color: Colors.red),
),
);
}
}
<div>
が欲しい
Container でほぼ web の <div>
を読み替えることができる。
flex
コンテナー作りたい
flex-direction
の向きに応じて 2 つの weiget を使い分ける。
Row
水平方向に対して子要素を配置していく。flex-direction: row
したいときはこっち。
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
children: [
Container(
decoration: BoxDecoration(
color: Colors.blueAccent,
),
padding: const EdgeInsets.all(30.0),
child: const Text('DOG', textAlign: TextAlign.center),
),
Container(
padding: const EdgeInsets.all(30.0),
decoration: BoxDecoration(
color: Colors.greenAccent,
),
child: const Text('DOG', textAlign: TextAlign.center),
),
Container(
padding: const EdgeInsets.all(30.0),
decoration: BoxDecoration(
color: Colors.redAccent,
),
child: const Text('DOG', textAlign: TextAlign.center),
),
],
);
}
}
justify-content
と align-content
は、それぞれ mainAxisAlignment
と crossAxisAlignment
に置き換えることができる。
Flutter 側の API 側の方が、主軸と交差軸という情報が API 名に顕現しているので好みである。(web の方はどっちがどっちかたまにわからなくなる)
Row の場合、水平方向が主軸となり、垂直方向が交差軸となる。
return Row(
mainAxisAlignment: MainAxisAlignment.center,
...
),
とりうる値は、補完でだいたい分かる。だいたい web と同じなのでそのまま読み替えて良いと思う。
Column
垂直方向に対して子要素を配置していく。flex-direction: column
したいときはこっち。
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
Container(
decoration: BoxDecoration(
color: Colors.blueAccent,
),
padding: const EdgeInsets.all(30.0),
child: const Text('DOG', textAlign: TextAlign.center),
),
Container(
padding: const EdgeInsets.all(30.0),
decoration: BoxDecoration(
color: Colors.greenAccent,
),
child: const Text('DOG', textAlign: TextAlign.center),
),
Container(
padding: const EdgeInsets.all(30.0),
decoration: BoxDecoration(
color: Colors.redAccent,
),
child: const Text('DOG', textAlign: TextAlign.center),
),
],
);
}
}
Column は Row の direction が交差した vresion なので割愛。
padding 効かせたい
やり方は主に 2 種類存在する。
Container
div みたいなやつに持たせるパターン
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.indigoAccent,
width: 7,
),
),
padding: const EdgeInsets.all(8.0),
child: const Text('DOG', textAlign: TextAlign.center),
);
}
}
Padding
Padding というクラスで囲ってしまうパターン
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.indigoAccent,
width: 7,
),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: const Text('DOG', textAlign: TextAlign.center),
),
);
}
}
どちらも便利なので、うまく使い分けたい。
margin 効かせたい
やり方は色々ある。
Container
div みたいなやつに持たせるパターン
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.indigoAccent,
width: 7,
),
),
child: Container(
margin: const EdgeInsets.all(8.0),
decoration: BoxDecoration(
border: Border.all(
color: Colors.red,
width: 7,
),
),
child: const Text('DOG', textAlign: TextAlign.center),
),
);
}
}
SizedBox
本来は width/height を pixel perfect に決めたい要素に対して wrap して利用する widget だが、空間確保にも利用することができる。
正確には margin クラスなどではないが、概念的には margin に近いのでこちらも紹介したい。
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.red,
width: 15,
),
),
),
SizedBox(
height: 70.0,
),
Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.indigoAccent,
width: 15,
),
),
),
SizedBox(
height: 190.0,
),
Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.greenAccent,
width: 15,
),
),
),
],
);
}
}
Spacer
連続した要素(web で言えば ul,ol
など)に対し、それらの余白を均等割りした空間をそれぞれ係数付きで付与できる便利なクラスだ。flex における justify-content: space-between
の上位互換みたいなものに近い。
正確には margin クラスなどではないが、概念的には margin に近いのでこちらも紹介したい。
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.red,
width: 15,
),
),
),
Spacer(flex: 1),
Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.indigoAccent,
width: 15,
),
),
),
Spacer(), // default flex = 1
],
);
}
}
比率を変更したい場合、引数の flex
の値を変更してやる。
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.red,
width: 15,
),
),
),
Spacer(flex: 1),
Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.indigoAccent,
width: 15,
),
),
),
Spacer(
flex: 2,
),
],
);
}
}
background-color がほしい
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Colors.cyanAccent,
),
child: const Text('DOG', textAlign: TextAlign.center),
);
}
}
border かましたい
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Colors.cyanAccent,
),
child: const Text('DOG', textAlign: TextAlign.center),
);
}
}
z-index
かましたい
重ね合わせを表現するときは Stack
widget を使う。
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Stack(children: buildBoxs());
}
List<Container> buildBoxs() {
return [
Container(
width: 300.0,
height: 300.0,
decoration: BoxDecoration(
color: Colors.blueAccent,
),
child: Align(
child: const Text('DOG'),
alignment: Alignment.bottomCenter,
),
),
Container(
width: 200.0,
height: 200.0,
decoration: BoxDecoration(
color: Colors.greenAccent,
),
child: Align(
child: const Text('DOG'),
alignment: Alignment.bottomCenter,
),
),
Container(
width: 100.0,
height: 100.0,
decoration: BoxDecoration(
color: Colors.redAccent,
),
child: Align(
child: const Text('DOG'),
alignment: Alignment.bottomCenter,
),
),
];
}
}
Stack の alignment
も便利なので、使いこなせると良い。
return Stack(
children: buildBoxs(),
alignment: AlignmentDirectional.center,
);
block 要素を inline 要素にしたい
画面幅いっぱいの要素が欲しい
こんな感じのやつ。主に 2 通りある。
Container + w/h MediaQuery
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
color: Colors.blueAccent,
),
child: const Text('DOG'));
}
}
SizedBox.expand
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SizedBox.expand(
child: Container(
decoration: BoxDecoration(
color: Colors.blueAccent,
),
child: const Text('DOG')
),
);
}
}
上下/左右/中央の幅寄せしたい
色々ある。
Row/Column を使っている場合
mainAxisAlignment: MainAxisAlignment.center
& crossAxisAlignment: CrossAxisAlignment.center
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
var column = Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: buildBoxs(),
);
return fullScreenContainer(context, column);
}
List<Container> buildBoxs() {
return [
Container(
decoration: BoxDecoration(
color: Colors.blueAccent,
),
padding: const EdgeInsets.all(30.0),
child: const Text('DOG', textAlign: TextAlign.center),
),
Container(
padding: const EdgeInsets.all(30.0),
decoration: BoxDecoration(
color: Colors.greenAccent,
),
child: const Text('DOG', textAlign: TextAlign.center),
),
Container(
padding: const EdgeInsets.all(30.0),
decoration: BoxDecoration(
color: Colors.redAccent,
),
child: const Text('DOG', textAlign: TextAlign.center),
),
];
}
Container fullScreenContainer(BuildContext context, Widget column) {
return Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
color: Colors.black,
),
child: column,
);
}
}
Align + alignment: Alignment.center
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
var column = Align(child: Text('DOG'), alignment: Alignment.center);
return fullScreenContainer(context, column);
}
Container fullScreenContainer(BuildContext context, Widget column) {
return Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
color: Colors.blueAccent,
),
child: column,
);
}
}
Align は便利で、他にもいろんな場所に揃えることができる。
Container + alignment: Alignment.center
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
var column = Container(child: Text('DOG'), alignment: Alignment.center);
return fullScreenContainer(context, column);
}
Container fullScreenContainer(BuildContext context, Widget column) {
return Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
color: Colors.blueAccent,
),
child: column,
);
}
}
Center
その名の通りの要素が用意されている。
https://api.flutter.dev/flutter/widgets/Center-class.html
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
var column = Center(child: Text('DOG'));
return fullScreenContainer(context, column);
}
Container fullScreenContainer(BuildContext context, Widget column) {
return Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
color: Colors.blueAccent,
),
child: column,
);
}
}
position:relative, position:absolute が欲しい
Stack と合わせて利用する。
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Stack(children: buildBoxs());
}
List<Widget> buildBoxs() {
return [
Container(
width: 300.0,
height: 300.0,
decoration: BoxDecoration(
color: Colors.blueAccent,
),
child: Align(
child: const Text('DOG'),
alignment: Alignment.bottomCenter,
),
),
Positioned(
bottom: 20,
left: 80,
child: Container(
width: 200.0,
height: 200.0,
decoration: BoxDecoration(
color: Colors.greenAccent,
),
child: const Text('DOG')),
),
Positioned(
top: 120,
right: 150,
child: Container(
width: 100.0,
height: 100.0,
decoration: BoxDecoration(
color: Colors.redAccent,
),
child: Align(
child: const Text('DOG'),
alignment: Alignment.bottomCenter,
),
),
),
];
}
}
Flutter 独自系
レイアウトに関して全体的に知りたい
https://flutter.dev/docs/development/ui/widgets/layout
白紙のページどうやって作るんだっけ
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold();
}
}
画面端で見切れちゃう
例えばこんな感じの widget があったとすると、
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(body: Text('feaowigoeha'));
}
}
こんな感じで左上が時刻の表示と被ってしまう。
SafeArea で囲うことで、OS 毎にもたらされる領域をよしなに padding で埋めてくれるという便利 widget だ。
class Tmp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(body: SafeArea(child: Text('feaowigoeha')));
}
}