[Flutter入門(2)] Write your first Flutter app, part 1をやってみる
Flutterの公式サイトに、チュートリアルよりも前にやってみる用のコンテンツとして Write your first Flutter app, part 1 というものが用意されているので、この内容を簡単なコードの解説を交えながら紹介していきます。
Step 1:アプリを作る
まず、Getting Started with your first Flutter app の手順でサンプルアプリを作ります。このとき、アプリ名は my_app
ではなく今回は startup_namer
にします。
$ flutter create startup_namer
サンプルアプリができたら、iOSシミュレータを立ち上げて、アプリを起動しましょう。
$ open -a Simulator
$ cd startup_namer
$ flutter run
これでとりあえずサンプルアプリがそのまま動いている状態ですね。
ここまで来たら、 lib/main.dart
の中身を以下のコードでごっそり置き換えてしまいましょう。
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('Welcome to Flutter'),
),
body: Center(
child: Text('Hello World'),
),
),
);
}
}
コードを変更したら、 flutter run
しているターミナルで r
キーをタイプすることでホットリロード(起動中のアプリにソースコードの変更内容を即時反映させるFlutterのデバッグ用機能)ができます。
$ flutter run
:
:
:
Flutter run key commands.
r Hot reload. 🔥🔥🔥
R Hot restart.
h Repeat this help message.
d Detach (terminate "flutter run" but leave application running).
c Clear the screen
q Quit (terminate the application on the device).
An Observatory debugger and profiler on iPhone SE (2nd generation) is available at: http://127.0.0.1:64038/E4xtLgtlcWU=/
# ここで r キーをタイプ
Performing hot reload...
Reloaded 1 of 503 libraries in 164ms.
# 起動中のアプリに変更が反映される
ちなみに、VS CodeなどのIDEにFlutterプラグインを導入して使っている場合は、ソースコードの変更を保存したら即座にホットリロードが実行されるようにすることも可能です。
例えばVS Codeの場合なら、ターミナルで
flutter run
する代わりに実行 > デバッグの開始
(またはF5
)でアプリを起動すれば、ソースコードを変更して保存したら即座にアプリの状態に反映されます。
VS Code自体のセットアップ方法は こちらの公式ドキュメント をご参照ください。
lib/main.dart
を上記の内容に変更すると、アプリの画面は以下のような内容になります。
コードの内容のうちいくつかポイントとなる点を確認しておきましょう。
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('Welcome to Flutter'),
),
body: Center(
child: Text('Hello World'),
),
),
);
}
}
-
MaterialApp
クラスを使って、Materialデザイン に準拠したアプリを作成している。FlutterはMaterialデザインを実装した様々なウィジェットを提供している -
main()
メソッドはアロー記法(=>
)を使っている(JavaScriptのアロー関数と同様、関数の処理が1行の場合はこの省略記法が使える) -
MyApp
自体がStatelessWidget
クラスを継承していて、アプリそれ自体が「ウィジェット」になっている点に注目。Flutterではほとんどすべてのもの(alignment, padding, layoutなども含む)がウィジェットである -
Scaffold
ウィジェットはFlutter標準のMaterialライブラリにより提供されている。このウィジェットはappBar
title
body
プロパティを持っていて、ホームスクリーンの見た目を簡単に作成できる - ウィジェットのメインの仕事は
build()
メソッドを提供することである。build()
メソッドでは、他のウィジェットを組み合わせるなどしてウィジェット全体の見た目を構築する - この例では
body
に「Text
ウィジェットを子として持つようなCenter
ウィジェット」を渡している。Center
ウィジェットは子要素を中央寄せしてくれるウィジェットである
Step 2:外部パッケージを使ってみる
english_words という外部パッケージを導入してみます。
pubspec.yaml
というファイルに以下のような1行を追記します。
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^0.1.2
+ english_words: ^3.1.5
追記したら、アプリのディレクトリ直下( flutter run
しているのと同じ階層)で以下のコマンドを実行してパッケージをインストールします。
$ flutter pub get
VS CodeにFlutterプラグインを入れて使っている場合は、
pubspec.yaml
の変更を保存したときに自動でflutter pub get
が実行されるので、自分で別途実行する必要はありません✋
次に、 lib/main.dart
でこのパッケージを利用するために、ファイルの先頭に以下の import
文を追記します。
import 'package:flutter/material.dart';
+ import 'package:english_words/english_words.dart';
最後に、このパッケージを使用して画面に出力する文字を Hello World
ではなくランダムな単語の組み合わせにしてみます。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final wordPair = WordPair.random();
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('Welcome to Flutter'),
),
body: Center(
+ child: Text('Hello World'),
+ child: Text(wordPair.asPascalCase),
),
),
);
}
}
下図のように、 Hello World
の代わりにランダムな単語を並べた文字列が表示されるようになりました。
ホットリロードする度に表示される文字列が変わるはずです。試してみてください。
上手くホットリロードされない場合は、一度
flutter run
を終了(ターミナル上でCtrl + C
で終了できます)させて、再度flutter run
してアプリを再起動してみてください。VS Codeの場合なら
実行 > デバッグの再起動
で再起動できます。
Step 3:Statefulウィジェットを作ってみる
Flutterには Stateless ウィジェットと Stateful ウィジェットがあります。
Statelessウィジェットは状態が変化しない(イミュータブルな)ウィジェットです。
反対にStatefulウィジェットは状態が変化しうる(ミュータブルな)ウィジェットです。ウィジェットが生まれてから破棄されるまでの間に、保持している値が変化する可能性があります。
Statefulウィジェットを実装するためには StatefulWidget
と State
という2つのクラスが必要になります。
-
StatefulWidget
クラスがState
クラスのインスタンスを作る -
StatefulWidget
クラスそのものはイミュータブルだが -
State
クラスが対応するウィジェットの状態を保持する
という関係です。
ここでは、 RandomWords
というStatefulウィジェットを作ってみます。 RandomWords
ウィジェットは _RandomWordsState
というStateクラスを作成します。最終的には、 MyApp
Statelessウィジェットの子要素として RandomWords
ウィジェットを持たせる形になります。
まず、 lib/main.dart
の末尾に以下のコードを追記して、2つのクラスを定義します。
class RandomWords extends StatefulWidget {
_RandomWordsState createState() => _RandomWordsState();
}
class _RandomWordsState extends State<RandomWords> {
Widget build(BuildContext context) {
final wordPair = WordPair.random();
return Text(wordPair.asPascalCase);
}
}
_RandomWordsState
クラスのクラス名の先頭の _
は、Dart言語における private
アクセス修飾子です。名前の先頭に _
をつけることでこのクラスは private
になります。
また、 _RandomWordsState
クラスが State<RandomWords>
を継承している点にも注目です。「 RandomWords
クラス用として使われる State
クラスである」ということを明示しています。
最後に、こうして定義した RandomWords
ウィジェットを、 MyApp
クラスで使うようにコードを修正します。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
- final wordPair = WordPair.random();
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('Welcome to Flutter'),
),
body: Center(
- child: Text(wordPair.asPascalCase),
+ child: RandomWords(),
),
),
);
}
}
アプリを再起動するなどしても、先ほどまでと同じような動作(ホットリロードする度に毎回ランダムな文字列が表示される)になっていればOKです👍
Step 4: 無限スクロールできるListViewを作ってみる
_RandomWordsState
を、ランダムな単語ペアの リスト を生成するようなクラスに改造してみましょう。
ListView
ウィジェットを使い、その builder()
ファクトリが遅延的に実行される性質を生かして、無限にスクロールできるものにしていきます。
まず、 _RandomWordsState
クラスに以下の2つの final
(変更不可) なクラス変数を追加します。
class _RandomWordsState extends State<RandomWords> {
+ final _suggestions = <WordPair>[];
+ final _biggerFont = TextStyle(fontSize: 18.0);
+
@override
Widget build(BuildContext context) {
final wordPair = WordPair.random();
return Text(wordPair.asPascalCase);
}
_suggestions
は提示する単語ペアのリスト( ListView
ではなく情報としてのリスト)を保持する変数、 _biggerFont
はあとでテキストのフォントサイズを大きくするときに使うための単なるスタイルの定義です。
次に、同じく _RandomWordsState
クラスに、 ListView
ウィジェットを組み立てるための _buildSuggestions()
メソッドを作ります。
Widget _buildSuggestions() {
return ListView.builder(
padding: EdgeInsets.all(16.0),
itemBuilder: /*1*/ (context, i) {
if (i.isOdd) return Divider(); /*2*/
final index = i ~/ 2; /*3*/
if (index >= _suggestions.length) {
_suggestions.addAll(generateWordPairs().take(10)); /*4*/
}
return _buildRow(_suggestions[index]);
}
);
}
コードの解説は以下のとおりです。
-
/*1*/
:itemBuilder
に渡すコールバックは、各単語ペアごとに呼び出されて、その単語ペアを_buildRow()
メソッド(まだこの時点では定義していない)に渡すことでListTile
ウィジェット(リストの1行を表すウィジェット)を生成する。ただし、偶数行目ではListTile
を返すが、奇数行目では何もせずDivider
ウィジェットを 返す点に注意 -
/*2*/
:Divider
ウィジェットで高さ1ピクセルの仕切り線を作成(各行の間に仕切りを入れて見やすくするためだけ) -
/*3*/
:i ~/ 2
という式は、「2で割った結果を小数点以下切り捨てて整数にする」という演算をする。例えば、1, 2, 3, 4, 5
が、この演算を通すことで0, 1, 1, 2, 2
になる。この計算により、仕切り線の数を無視した実際の要素の数を算出している -
/*4*/
:生成済みの単語ペアをすべて消費した場合に、さらに10個の単語ペアを追加で生成してリスト(ListView
ではなく情報としてのリスト)に追加している
さらに、先ほどの _buildSuggestions()
メソッドから単語ペアを受け取ってリストの1行を ListTile
ウィジェットとして生成する _buildRow()
メソッドを実装します。
Widget _buildRow(WordPair pair) {
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
);
}
これでパーツは整ったので、実際に _RandomWordsState
クラスの build()
メソッドでこれらの処理を実行して ListView
を作れるようにします。
@override
Widget build(BuildContext context) {
- final wordPair = WordPair.random();
- return Text(wordPair.asPascalCase);
+ return Scaffold(
+ appBar: AppBar(
+ title: Text('Startup Name Generator'),
+ ),
+ body: _buildSuggestions(),
+ );
}
最後に、 MyApp
ウィジェット自体の build()
メソッドも更新して、 RandomWords
ウィジェットをそのまま表示するようにすれば完了です。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
- title: 'Welcome to Flutter',
- home: Scaffold(
- appBar: AppBar(
- title: Text('Welcome to Flutter'),
- ),
- body: Center(
- child: RandomWords(),
- ),
- ),
+ title: 'Startup Name Generator',
+ home: RandomWords(),
);
}
}
実行すると下図のような感じで表示されます。
マウスでドラッグすればスクロールできるので、無限にスクロールできることを確認してみてください👍
Discussion