このチャプターでは最初に作成したアプリのコードを参考に、 Flutter アプリがどのように作成されているかを見ていきます。
最初のアプリ作成
まずは最初のアプリを作成しましょう!
flutter アプリの作成はコマンドでおこなう方法と Android Studio でおこなう方法の2つを説明します。どちらの方法で作成していただいてもかまいません。
コマンドで作成する
アプリを作成するコマンドは以下の通りです。
適当な作業ディレクトリに移動してから、コマンドを実行しましょう。
$ flutter create flutter_hands_on
flutter_hands_on
はアプリ名です。自由に変更してもかまいませんが、ハイフン -
が使えないことに注意してください。
作成出来たら、 Android Studio で開いて見ましょう。
Android Studio を開いて、「Open an Existing Project」で先ほど作成したアプリのディレクトリを選択して OK を押しましょう。
Android Studio で作成する
Android Studio を起動して、「Create New Flutter Project」をクリックします。
「Flutter Application」を選択して「Next」をクリックします。
アプリ名などを入力していきましょう。
- Project Name : アプリ名です。自由に変更してもかまいません。
-
Flutter SDK : 環境構築のチャプターで設定した Flutter を設定します。
bin
フォルダではなく、1 つ上のflutter
フォルダまでのパスを入力することに注意してください。 - Project location : プロジェクトを配置するディレクトリを指定してください。
- Description : アプリの説明を書く欄ですが、飛ばしてもかまいません。
入力を確認して「Next」をクリックします。
パッケージ名などを指定しますが、今回はデフォルトのままで大丈夫です。
「finish」をクリックするとアプリの作成が開始されます。
作成が完了すると、作成したプロジェクトが開かれます。
作成ができたらデバイスの準備をして実行してみましょう。
デバイスの準備
まずは実行する端末を USB で接続したり、エミュレータを起動したりしてください。
実行できるデバイスがあるかどうかは、以下のコマンドで確認ができます。
$ flutter devices
また、Android Studio なら以下の部分をみることで実行できる端末があるかを確認したり、エミュレータを起動したりできます。Xcode が入っていれば、ここから IOS のシミュレータも起動できます。
実行する
実行できるデバイスが確認できたら、早速実行しましょう。
コマンドと Android Studio で実行する方法を説明します。
コマンドで実行
以下のコマンドで実行ができます。
$ cd flutter_hands_on # 作成したアプリのディレクトリに移動
$ flutter run # 実行
Android Studioで実行
プロジェクトを開いた状態で右上の「▷」をクリックすると、実行ができます。
実行したアプリの右下の+ボタンを押すとカウントされます。
アプリを停止したい場合は、右上の赤い「□」をクリックして停止できます。
最初のアプリを読む
まずはコードを書かずに、作成したアプリのソースコードをざっくりと読みながら flutter を学習していきましょう。ここで全て理解する必要はないので、「よくわからないなあ」と思ったらとりあえず飛ばして読むのもいいでしょう。
アプリのディレクトリ内にはさまざまなファイルやフォルダがありますが、 Flutter アプリのソースコード自体は lib/
フォルダの中にあります。
そしてその中の main.dart
を開いてください。アプリを作成した段階では、アプリのソースコードは実質この main.dart
だけです。
では、この main.dart
の中身を詳しく見ていきましょう。
一応ここにも載せておきます。
lib/main.dart
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
一番上から読んでいきましょう。
インポート文
import 'package:flutter/material.dart';
- マテリアルデザインの UI コンポーネントを使うためにパッケージをインポートしています。
main関数
void main() {
runApp(const MyApp());
}
- アプリを実行すると、この main 関数が実行されます。
- この main 関数の中の
runApp()
という関数を実行してアプリを実行しています。 - 関数の引数(ここでは
MyApp()
)には、実行するアプリを構成するWidget(ウィジェット)が渡されます。
MyAppクラス
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
-
runApp()
の引数として渡されていた MyApp クラスで、StatelessWidget
を継承しています。
-
MaterialApp
Widget を返していますが、マテリアルデザインを使用する Flutter アプリでは基本的にこのMaterialApp
Widget でアプリ全体を覆います。 -
theme
でアプリのカラーテーマを、home
でアプリを起動したとき最初に表示するページを指定します。
MyHomePageクラス・_MyHomePageStateクラス
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
-
MyHomePage
はStatefulWidget
を継承しています。 -
_MyHomePageState
はState<MyHomePage>
を継承しています。
MyHomePage
-
createState
メゾットで_MyHomePageState
を返しています。 - 引数付きのコンストラクタを用意して、
title
に値受け取っています。 - 今回の場合は MyApp で
Flutter Demo Home Page
という値を渡しているのでtitle
にはその文字が入ります。State 内ではtitle
はwidget.title
というようにして参照できます。
class MyHomePage extends StatefulWidget {
// コンストラクタ
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
State<MyHomePage> createState() => _MyHomePageState();
}
home: const MyHomePage(title: 'Flutter Demo Home Page'),
MyHomePageState
状態の保持と更新
- State 内では
_counter
でカウントする数を保持し、_incrementCounter
メゾットで_counter
の数を増やしています。 -
_counter++;
を覆っているsetState()
というメゾットがあります。これは値が変更されたことを通知して UI を再描画するためのメゾットです。このメゾットで包まないと、値は変わりますが実際の画面に表示される数は変わりません。つまり、状態が変わっただけでその変更が UI に伝わってないということです。
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
Scaffold
-
build
メゾットで返している Widget ツリーの根元はScaffold
になっています。 -
Scaffold
はappbar
やbody
などを指定してページ全体を作成する Widget です。 -
マテリアルデザインのアプリを作る場合はページ全体をこの
Scaffold
で覆うのが基本です。MaterialApp
はアプリ全体、Scaffold
はページ全体を覆うということを覚えておきましょう。 -
Scaffold
で指定しているappbar
は画面上部のバー(Flutter Demo Home Page と書かれている)です。 -
body
はページの中身の Widget ツリーを指定しています。 -
floatingActionButton
は画面右下のボタンです。
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(),
floatingActionButton: FloatingActionButton(),
);
}
appbar
- title で appbar に表示するタイトルの文字を指定しています。
-
widget.title
で引数として受け取った値を参照しています。
appBar: AppBar(
title: Text(widget.title),
),
body
- body の Widget ツリーを構成する各 Widget の説明は以下のようになっており、これらの Widget を
child
やchildren
を使って親子関係を作ることで Widget ツリーを構成しています。-
Center
: は子の要素を中央寄せにする Widget。 -
Column
:children
で Widget の配列を指定し、その Widget を縦に並べる Widget。 -
Text
: 文字を表示する Widget。
-
- Text 内の
$_counter
でカウントする数を表示しています。
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton
- floatingActionButton の中の
onPressed
で_incrementCounter
メゾットを指定されています。 - これにより、タップすると
_incrementCounter
メゾットが呼び出されてカウントする数が増え、画面に表示される数が変わります。
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
これでこのチャプターは以上です。
次のチャプターからはこのアプリに手を加えながら TODO アプリを作っていきましょう!