Flutter未経験者の方から「実際どんなコードなん?」と質問があったので、説明したみた内容を投稿します。
以下はお馴染みのあのコード。
Flutterの新しいプロジェクトを作成すると、すでにプログラムが入っています。
そのまま実行するとボタンに反応してカウントアップするアプリが実行されます。
ある程度土台ができているので、それを使って説明します。
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
)
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
import文
import 'package:flutter/material.dart';
一番最初の行にimport
文があります。
Googleのアプリっぽいデザインやそれに合ったツールが使えるようになります。
main()関数と MyApp
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
main()
関数があって、MyApp
をスクリーンに表示するように書かれています。
MyApp
はクラスで名前は何でもいいです。
MyApp
は StatelessWidget
を継承しています。
ウィジェットは画面の一要素、それのステートレスなものです。
つまり 「変更が起きない要素」 のようなものだと思ってください。
その中でウィジェットのビルドが行われています。
引数に context
が呼ばれています。
日本語に訳すと「状況」や「文脈」などと言いますが、この context
の中にどこでどういう風に作成されたかの情報を含んでいます。
build
文で MaterialApp
が返されています。
一番最初の行にimport文で指定した material.dart
が早速使われています。GoogleのUIっぽいデザインを返すようにしています。
引数にタイトル、全体の色などのテーマ情報、初期表示のホームとなるクラスを指定します。
ここでは MyHomePage
が指定されてます。
実際の MyHomePage の中を見てみます。
MyHomePage
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
StatefulWidget
を継承しています。先ほど説明した Stateless とは違って、Stateful なウィジェット、つまり見た目の変化や持っている値の変化などが起きるウィジェットです。
コンストラクタ に key
と title
が渡されています。
MyApp
の stateless ウィジェットで title
を渡していますが、key
はデフォルトでユニークなものが渡されます。
そのあとに : super(key: key)
と書かれています。
Redirecting constructors と呼ばれる手法で、別のコンストラクタの処理を追加で行なっています。親クラスにもキーを渡しているんですね。
そして、createState()
という関数でステートを作成しています。
Statefulウィジェットなので、ステート部分の作成は必須です。
ここでは _MyHomePageState
というステートを作成しています。
では実際にステートの中身を見てみます。
_MyHomePageState
_MyHomePageState
クラスは MyHomePage
の型を持ったステートを継承しています。
「これはMyHomePage
内のある一つの状態を管理するクラスですよ」と宣言していると思ってください。
では何の状態を管理しているのかを説明していきます。
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
//〜〜〜〜 以下、あとで説明 〜〜〜〜
}
まず、変数定義の部分です。
_counter
という int
型の変数と、文字列型を格納する配列の _data
があります。
次に _incrementCounter()
関数があります。
void
型なので値は返しません。setState
という関数があって、その中で _counter
をインクリメントしています。
この setState
で画面の再描画を行います。
_counter
の値が変更されたものを再描画したいので、 setState
の中に_counter
を入れています。
では、_incrementCounter()
関数はどこで呼ばれるのでしょうか?
ボタンを押した時に呼びたいので、これからデザイン部分を作っていきます。
_MyHomePageState の build 部分
@override
Widget build(BuildContext context) {
return Scaffold(
//個々の内容はあとで説明
appBar: xxxxxxxx,
body: xxxxxxxx,
floatingActionButton: xxxxxxxx,
);
}
ウィジェットのビルドを行っています。
この中でボタンの配置や変化する数字など、内容を作っていきます。
build
メソッドはStatelessWidget
の中にもありました。
今回のbuild
メソッドは、StatefulWidget
の中に定義するのではなく、State
の中に定義します。
(Stateの中で定義する理由は色んな記事があるので、今は割愛します。)
return
で Scaffold
を返しています。
Scaffold
は「足場」という意味で、デザインする際に便利な機能が沢山入ったものだと思ってください。
その中で appBar
、body
、floatingActionButton
という要素が入っています。
それぞれ順番に説明していきます。
appBar 部分
appBar: AppBar(
title: Text(widget.title),
),
appBar はアプリケーション上部に表示するバー部分です。属性である title
の内容を表示します。
今回は文字列要素の Text
があり、widget.title
が指定されています。
この widget.title
は StatefulWidget
である MyHomePage
に渡された title
を持ってきています。
State
の中にいても、それを呼び出したウィジェットの情報をこうやって取得することができます。
body 部分
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
)
],
),
),
body
について説明します。内容の中心となるものを配置していきます。
Center
で中央に配置する要素を作っています。コンテンツを中央寄せにしたいんですね。
その中に child
という属性があります。Center
はただの入れ物の要素なので、子要素が必要な要素は、child
や children
が必須です。
今回の場合でいうと、Column
が子要素で、それを中央寄せにしています。
Column
はリストや表のような枠組みを作っていくのに便利な要素です。
mainAxisAlignment
がありますが、属性を変更することで、縦に子要素を並べていったり、横に並べていったりを設定できます。
これらはHTMLのタグのようなもので、種類が沢山あるので、その都度調べて取り込んでいく必要があります。
Column
も子要素が必要です。
並べる子要素は複数なので、child
ではなく、複数形の children
になります。
プログラムでは Text
で文字を設定しています。
style について
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
Text
には style
が設定されています。文字の大きさや色を設定する箇所です。
しかしここでは、「blue」とか「red」とか「12px」のような表現がありません。
Theme.of(context)
で親のテーマも持ってきています。context
は「文脈」なので、親のテーマ情報も持っています。それの「普通のテキスト」に関するテーマである textTheme
の display1
タイプを取得しています。
もちろん display2
も display3
もあり、数字が上がる毎に文字が大きくなっていきます。
floatingActionButton 部分
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
最後にfloatingActionButton
があります。画面の一番下に浮動しているボタンなので、body の外に定義します。onPressed
にボタンが押された時の動作を記述しますが、ここでずっと説明を放置されてきた_incrementCounter()
を設定します。
最後にtooltip
や アイコン を設定して終了です。