たまたまFlutterを利用することになったのでこのテーマで投稿します。
今回はVSCodeとiOSシミュレータを使って入門レベルの説明を行います。
環境
macOS
インストールするもの(すでに入っている方は先へ)
Flutter SDK
VSCode
XCode
Flutterとは
Flutter is Google’s UI toolkit for building beautiful, natively compiled applications for mobile, web, and desktop from a single codebase.
Flutterは、単一のコードベースからモバイル、ウェブ、デスクトップ向けの、美しくネイティブにコンパイルされたアプリケーションを構築するためのGoogleのUIツールキットです。
環境構築
インストール
インストールに関しては、公式のやり方を参考にすれば特に困ることはないと思います。
使用するエディタの候補は Android Studio、IntelliJ、VSCode などがあるみたいですが、私は普段VSCodeを利用しているので、そちらで進めていきます。
https://flutter.dev/docs/get-started/install
で自分のOSを選択し、FlutterSDKをインストールします。
解凍したflutterフォルダを ~/Developer 以下に移動させます。
ターミナルでvim ~/.bash_profile
として、
export PATH="$PATH:/Users/[USER NAME]/Developer/flutter/bin"
を書き込み、source ~/.bash_profile
で読み込みます。
ターミナルでflutter --version
コマンドを入力して、バージョンが表示されればOKです。
flutterにはflutter doctor
コマンドがあり、これで、開発環境構築の状況がわかります。
何をしなくちゃいけないかが書いてあるので、指示通りやれば問題ないです。
例↓
[✗] iOS toolchain - develop for iOS devices
✗ Xcode installation is incomplete; a full installation is necessary for iOS development.
Download at: https://developer.apple.com/xcode/download/
Or install Xcode via the App Store.
Once installed, run:
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
Xcodeインストール
XCodeがない方は、flutter doctor
で
Download at: https://developer.apple.com/xcode/download/
Or install Xcode via the App Store.
とある通り、Xcodeをインストールします。
gitが入っていないときはインストールするよう言われるかもしれませんので指示に従ってインストールしましょう。
VSCode設定
VSCodeがない方は https://code.visualstudio.com/ からVSCodeインストールします。
VSCode内の左下のボタンをクリックして Flutter と検索し、一番上に出てくる拡張機能をインストールします。
これで、Flutterが使えるようになりました。
Flutterの拡張機能に加えて、Dartの拡張機能を入れてもいいと思います。
サンプルアプリを動かす
私は ~/flutter_apps というディレクトリを作りました。この中にアプリを作っていきます。
$ flutter create first_app
でアプリの雛形を作成します。
完了したら first_app フォルダをVSCodeで開きます。
lib/main.dartの中にはすでにコードが書かれているので、シミュレータで実行してみます。
デバッグの開始、Start iOS Simulator をクリックします。(もしくはF5)
こんなカウンターアプリが立ち上がりました。プラスボタンをタップするとカウントが+1されます。
Hello World
main.dartを以下の様に書き換えると
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(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('First App'),
),
body: Center(
child: Text('Hello World'),
),
);
}
}
Hello World が表示されました。Flutterには Hot Reload があるので、コードがすぐ画面に反映されていいですね。開発がサクサク進みそうです。
コードの説明を簡単にしてみます。
void main() => runApp(MyApp());
一行目のこれは
void main() {
runApp(new MyApp());
}
と同じ意味です。main関数内で MyAppインスタンスを引数としてrunApp関数を呼んでいます。
インスタンス生成時の「new」は書かなくてもいいみたいです。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
MyAppクラスはStatelessWidgetクラスを継承しています。
StatelessWidgetクラスとはなんでしょう。
https://api.flutter.dev/flutter/widgets/StatelessWidget-class.html
公式ドキュメントには詳しい説明がありますが、ここでは簡単に、
state(状態)がなく、immutableな(再描画が必要ない)Widgetと考えます。
(もし状態を持たせたいなら StatefulWidgetを使うことを検討しましょう。)
ちなみにWidget(ウィジェット)とは、コンピュータの操作画面を構成する、何らかの機能を持った表示・操作要素のことです。
StatelessWidgetはbuildメソッドを持っています。これでWidgetクラス等を返すようです。
上のコードでは、MaterialApp Widgetを返しています。
Material Widgetの引数で、MyHomePageインスタンスが呼ばれています。
続いてMyHomePageクラスを見てみます。
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('First App'),
),
body: Center(
child: Text('Hello World'),
),
);
}
}
こちらもStateless Widgetを継承しているのでbuildメソッドを持っています。
同じように、Scaffold Widgetを返しており、引数で AppBar、Center Widgetが呼ばれています。
Flutterではこんな感じでWidgetの中でWidgetを呼んで、入れ子状にして構築するようです。
Todoアプリ
ここから、追加だけの簡単なTodoアプリを作りたいと思います。
main.dartを書き換えます。
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
TextEditingController _controller = TextEditingController();
final List<TodoItem> _items = List();
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Todo App'),
),
body: Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(
flex: 3,
child: TextField(
controller: _controller,
),
),
Expanded(
flex: 1,
child: RaisedButton(
child: Text("追加"),
color: Colors.orange[600],
textColor: Colors.white,
onPressed: () {
setState(() {
_items.add(TodoItem(content: _controller.text,));
_controller.clear();
});
},
),
)
],
),
Expanded(
child: Column(
children: _items,
),
),
],
),
);
}
}
class TodoItem extends StatelessWidget {
final String content;
TodoItem({@required this.content});
@override
Widget build(BuildContext context) {
return ConstrainedBox(
constraints: BoxConstraints.expand(height: 50.0),
child: Container(
decoration: BoxDecoration(
border: Border(bottom: BorderSide(color: Colors.lightBlue[200]))
),
child: Center(
child: Text(
content,
style: TextStyle(
fontSize: 18.0,
)
),
),
),
);
}
}
編集も削除もできない追加だけのシンプルなTodoアプリができました。
簡単にコードの説明をします。
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
今回は再描画が必要なので MyHomePageクラスは StatefulWidgetを継承しています。
StateクラスがUIの状態を持っているので、ここでは特に処理を書かず、_MyHomePageStateクラスに処理を書いていきます。
class _MyHomePageState extends State<MyHomePage> {
TextEditingController _controller = TextEditingController();
final List<TodoItem> _items = List();
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Todo App'),
),
body: Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(
flex: 3,
child: TextField(
controller: _controller,
),
),
Expanded(
flex: 1,
child: RaisedButton(
child: Text("追加"),
color: Colors.orange[600],
textColor: Colors.white,
// ボタンを押したときの状態の変化をここに書く
onPressed: () {
setState(() {
_items.add(TodoItem(content: _controller.text,),);
_controller.clear();
});
},
),
)
],
),
Expanded(
child: Column(
children: _items,
),
),
],
),
);
}
}
build関数の中は先ほどと同じ様にWidgetを入れ子にしてUIを作っています。
onPressd: のところに状態がどう変わるかを書いています。
_itemsというTodoItemのListを入れる変数をつくり、そこにTodoItemを追加していく感じです。
追加していくTodoItemの内容は、 TextEditingControllerのインスタンスを入れる_controller変数をTextFieldのcontroller: に設定しておけば、_controller.textで取り出すことができます。
終わりに
データの永続化にはFirebaseが相性が良いようです。
今後、FirebaseやBLoCパターン等も学習していけたらと思います。
私も最近触り始めたレベルなので間違っている部分があるかもしれませんが、そのときはコメントお願いします。
最後まで読んでいただき、ありがとうございました。