Flutter公式チュートリアルをやる
例のごとく何をやったらいいかを調査するところから
とりあえずmonoさんの有名なFlutter紹介記事↓を読んだ。
↑この記事は2018年の記事で、2020年にいくつか追記はされている。
記事執筆の当初と比べると、だいぶ日本語情報が増えている(特に入門は)印象なので、そっちから入ってもいいのよね
たとえばこの辺
公式の表玄関から入って、そこで挫折したら、↑の本を頼る、にしたらいいかな。
Flutter公式はわかりやすそうだけど、英語オンリーではありそう。
公式
- Get Started
- Tutorials
一応iOS開発経験者でFlutter開発入門する人向けの入り口もあるみたい
ここに書いてある通り、
- Introduction to widgets
- Building layouts tutorial
- Add interactivity tutorial
の三つをやろうかな
Dartの言語ツアーはここか。ちょっと長い……
Dartpatというサービスがあるよう
Dartの言語ツアー、長すぎて辛かったので、↓のQiita記事でサクッとつかもう
(そもそもまだそこまで真剣にFlutter使う気がないし)
チュートリアル終わったら、ビンゴゲームでも作ってみようかしらん。
ランダムで1〜99まで数字出して、過去の数字が見られる感じ。
Flutter入門のためのDart入門
↓
Flutter環境構築
↓
チュートリアル開始
という流れで
Dartの基礎をやっていきます
基本的には強い型付け言語だが、dynamic
を使うことで動的っぽく型を使うことができる。
void main() {
dynamic d = "hello";
print(d);
d = 1;
print(d + 1);
}
強い型付けと静的型付けの違いがよくわからなかったので、調べた
強い型付け
強い型付けとは、2つの型に互換性があるかどうかを検出し、互換性がなければ、エラーを投げるか、型の強制変換を行う
という意味だ。表面的には、JavaとRubyはどちらも強く型付けされている。
一方、アセンブリ言語やCのコンパイラは、弱く型付けされている。これらのコンパイラは特定のメモリ位置にあるデータが
整数なのか、文字列なのかあるいは単なるデータなのかを必ずしも意識する必要はない。
コンパイラによる型チェックでエラーを投げるのが強い型付け、ということか。
確かにアセンブラはコンパイルして実行したらよくアライメント合わなくて死んだような記憶がある。
(IBM系アセンブラはなんか型あったような気がするが、そんなに厳密でもなかった気がする)
静的型付け
静的に型付けされた言語では、型の構造体に基づいてポリモーフィズムが行われる。
つまり、遺伝子的な青写真によってアヒルかどうかを判定するのが静的型付け。
鳴き声や歩き方によってアヒルかどうかを判定するのが動的型付けだ。
この定義はちょっとあんまりピンと来ない。
コードをコンパイルしたときに全ての型が決まっているのが静的型付けで、決まらないのが動的型付けかと思っていました(後略)
こっちの方がピンとくるかも。
たとえばSwiftは、静的型付け言語で、強い型付けだと思うけど、
Ruby/Swiftは動的型付け言語だが、強い型付けはしている。
コンパイル時に型エラーは吐くけれど、その型はundefined
の場合があるよ、という理解でいいのかな
final
とconst
の違いがよくわからなかったが、配列の定義で違いが出るみたい。
void main() {
List array = const [1, 2, 3];
print(array);
array.add(4); // ここでダメなはず
print(array);
}
DartPadでやってると↑でエラーメッセージが出ないけど、2回目のprint()は無視されて実行されるという結果になる……
List array = final [1, 2, 3];
この書き方はそもそもできない
void main() {
final array = [1, 2, 3];
print(array);
array.add(4);
print(array);
}
これで出力がこれ
[1, 2, 3]
[1, 2, 3, 4]
セミコロン必須がちょっとうっとうしい
コンストラクタ、private変数がちょっと独特。
特にprivateっていうアクセス修飾子がなくて、_から始まるのが全部private扱いなのは驚き
class Person {
String firstName;
String lastName;
Person(this.firstName, this.lastName);
String _member;
Person.origin() {
this.firstName = '氏';
this.lastName = '名';
}
mixinがそもそも初見だった
mixin とはオブジェクト指向プログラミング言語において、サブクラスによって継承されることにより機能を提供し、単体で動作することを意図しないクラスである。言語によっては、その言語でクラスや継承と呼ぶものとは別のシステムとして mixin がある場合もある(#バリエーションの節で詳述)。
extends
: 継承
implements
: インターフェイスの実装。多重継承が可能
with
: ミックスイン
カスケード記法という名前を知らなかった
特定のインスタンスに対して..で続けることでそのインスタンスに対する操作(メンバ関数呼び出し)を続けることが出来ます。
Cascadeという言葉自体は階段状に落ちる滝・水路を示すワード。
そこから派生して、連続で処理することを示すようになったとのこと。
async
、await
は概念はわかるけど、使ったことはない。
ジェネレーターがよくわからなかった
うーんコード読んでみても結局ピンと来なかった。。。
モダンなJSの非同期処理をちゃんと理解するのが早道かも。一旦いいや
今日は時間的に環境構築したら終わりかな
flutter doctor
の診断結果。
当然Androidの開発環境は入っていないが、Flutterをやるためには整えないといけないのか。
$ flutter doctor
╔════════════════════════════════════════════════════════════════════════════╗
║ Welcome to Flutter! - https://flutter.dev ║
║ ║
║ The Flutter tool uses Google Analytics to anonymously report feature usage ║
║ statistics and basic crash reports. This data is used to help improve ║
║ Flutter tools over time. ║
║ ║
║ Flutter tool analytics are not sent on the very first run. To disable ║
║ reporting, type 'flutter config --no-analytics'. To display the current ║
║ setting, type 'flutter config'. If you opt out of analytics, an opt-out ║
║ event will be sent, and then no further information will be sent by the ║
║ Flutter tool. ║
║ ║
║ By downloading the Flutter SDK, you agree to the Google Terms of Service. ║
║ Note: The Google Privacy Policy describes how data is handled in this ║
║ service. ║
║ ║
║ Moreover, Flutter includes the Dart SDK, which may send usage metrics and ║
║ crash reports to Google. ║
║ ║
║ Read about data we send with crash reports: ║
║ https://flutter.dev/docs/reference/crash-reporting ║
║ ║
║ See Google's privacy policy: ║
║ https://policies.google.com/privacy ║
╚════════════════════════════════════════════════════════════════════════════╝
Running "flutter pub get" in flutter_tools... 1,954ms
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 2.2.0, on macOS 11.2.2 20D80 darwin-x64, locale ja)
[✗] Android toolchain - develop for Android devices
✗ Unable to locate Android SDK.
Install Android Studio from:
https://developer.android.com/studio/index.html
On first launch it will assist you in installing the Android SDK
components.
(or visit https://flutter.dev/docs/get-started/install/macos#android-setup
for detailed instructions).
If the Android SDK has been installed to a custom location, please use
`flutter config --android-sdk` to update to that location.
[✓] Xcode - develop for iOS and macOS
[✓] Chrome - develop for the web
[!] Android Studio (not installed)
[✓] VS Code (version 1.56.2)
[✓] Connected device (1 available)
! Doctor found issues in 2 categories.
そもそもAndroid向けにビルドするかもわからない(Webだけかも)ので、Android向けの環境構築は一旦スルー。
パス通すところがちょっとドキドキしたが、できた。
俺の環境だとbashなので、bash_profile
に下記を一行追記した。
export PATH="$PATH:[PATH_OF_FLUTTER_GIT_DIRECTORY]/bin"
[PATH_OF_FLUTTER_GIT_DIRECTORY]
には、Flutterのzipファイルを解凍したものを置いてあるパスを指定した。
一度ターミナルを閉じて、開き直して、
echo $PATH
↑で確認。
$ echo $PATH
/Users/me/.rbenv/shims:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/share/dotnet:~/.dotnet/tools:/Library/Apple/usr/bin:/Library/Frameworks/Mono.framework/Versions/Current/Commands:/Users/me/Dev/flutter/bin
出力結果はこんな感じ。
(ユーザー名をme
としています)
Flutterだけのパスなら、下記で確認できる
$ which flutter
/Users/me/Dev/flutter/bin/flutter
なお.bashrc と .bash_profileでちょっと迷ったが、こういう場合は.bash_profileで良さそう
起動成功!
VSCodeの設定もした
ここに書かれている通り
open -a simulator
これでiOS Simulatorを開き、VSCodeでF5を押すと、ホットリロードが効くようになった。
これはかなり感動。
明日はここから
VSCodeの勉強をする
VSCodeでファイルが上書きになるか別タブになるかの条件がわからなくてイライラしてたけど、下記が条件みたい
- Make a change to a file.
- Double-click a file's header.
- Double-click a file in the Explorer.
- Open a file that is not part of the current folder.
このページがVSCodeの扱い方のすべてのような気がする
⌘K
→Z
でZenモードに行ける。
抜けるときはEsc二回押し。
前に見たときはこんなん使わないと思っていたが、今は魅力的に見える
Center Layoutじゃない方が好みなので、設定を変えた
別タブになるかの条件はこっちっぽい。
無編集かシングルクリックでタブ上書き。
VSCodeなのかMacBookなのか、ダブルクリックの認識がシビアな気がする
あとこれもいい
あとこれもいい
ここからFlutterに戻ります
Web版の起動はここを参照するとできた
iOS, Webとビルドできたので、せっかくならAndroidも試したくなってきた。環境構築するかあ
Android Studioの初回起動で、「Import studio setting from: 」という選択が出て、一瞬戸惑った。
どうやら前のバージョンから設定を引き継ぎたい人向けのダイアログらしい。
MacOSは力強くBetaと書かれていたので、思わずスルーしてしまったが、気になって戻った。
$ flutter config --enable-macos-desktop
これをやったが、
$ flutter run -d macos
Launching lib/main.dart on macOS in debug mode...
Exception: No macOS desktop project configured. See
https://flutter.dev/desktop#add-desktop-support-to-an-existing-flutter-app to
learn about adding macOS support to a project.
と言われエラー。
プロジェクト作成時にMacOS環境をenableにしてなかったのが原因。
既存PJでも、
flutter create --platforms=windows,macos,linux .
でファイルを追加できる、と書いてあってやってみたのだが、
$ flutter create --platforms=macos
No option specified for the output directory.
Create a new Flutter project.
If run on a project that already exists, this will repair the project,
recreating any files that are missing.
とエラーが出た。
まだサンプルプロジェクトだったので、作り直した。
Flutter x VSCode、デバッグモードにすると、ここでビルド環境が選べるんだ。新鮮
Android Studio入れて、Android Studio Setup Wizard
を完了させて、エミュレーター(AVD)を起動させられるようになった。
が、VSCodeから立ち上げようとするとエラー
FAILURE: Build failed with an exception.
* What went wrong:
Could not determine the dependencies of task ':app:compileDebugJavaWithJavac'
To build this project, accept the SDK license agreements and install the missing components using the Android Studio SDK Manager.
Alternatively, to transfer the license agreements from one workstation to another, see http://d.android.com/r/studio-ui/export-licenses.html
Using Android SDK: /Users/xxx/Library/Android/sdk
エラーの文言でググったら、下記の質問が出てきた。
flutter doctor --android-licenses
で解決しそう
と、思ったらコマンドでまたエラー
$ flutter doctor --android-licenses
Exception in thread "main" java.lang.NoClassDefFoundError: javax/xml/bind/annotation/XmlSchema
at com.android.repository.api.SchemaModule$SchemaModuleVersion.<init>(SchemaModule.java:156)
at com.android.repository.api.SchemaModule.<init>(SchemaModule.java:75)
at com.android.sdklib.repository.AndroidSdkHandler.<clinit>(AndroidSdkHandler.java:81)
at com.android.sdklib.tool.sdkmanager.SdkManagerCli.main(SdkManagerCli.java:73)
at com.android.sdklib.tool.sdkmanager.SdkManagerCli.main(SdkManagerCli.java:48)
Caused by: java.lang.ClassNotFoundException: javax.xml.bind.annotation.XmlSchema
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
... 5 more
どうやらコマンドラインツールがデフォルトだとインストールされない模様。
↓に従ってインストールする
これでようやくライセンスにyesを入れることができた
来ましたねえ!
iOS/Android/MacOS/Webの4プラットフォームでの動作を確認。
感動。。。
結局Web/MacOS/Androidの環境構築で一日溶けてしまった。
明日こそ↓からやる
やっていき
Codelabって何のことかと思ったら、Googleがそういうプラットフォームやってるのね
なんかWidgetって概念に説明がないまま、Step3まで来てしまった
こんな感じでボイラーテンプレートつくってくれる。便利
VSCodeの⌥ + Shift + f
でフォーマットしてくれるの、すごい便利だ
Flutterの構成要素はWidget
という単位で分割される。
(Reactで言うComponent
に相当すると思われる)
Widget
には二種類ある。
StatelessWidget
StatefulWidget
状態なし/あり。
基本的には状態なしの方が実装が楽なので、チュートリアルもその順で説明される。
StatelessWidgetのHello Worldがこちら。
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
, Scaffold
, AppBar
, Center
もWidget
の一種。
FlutterアプリはこのようにWidgetの組み合わせで作成する。
一方、StatefulWidget
は、こんな感じ。
class WidgetName extends StatefulWidget {
_WidgetNameState createState() => _WidgetNameState();
}
class _WidgetNameState extends State<WidgetNameState> {
Widget build(BuildContext context) {
return Container();
}
}
本体部分とState部分が分割される感じ。
無限スクロールするListView
コード読んでみたけど、contextやindexがどこから来ているのかよくわからなくなった
どうやらitemBuilder
がそういう仕様みたい
The ListView class provides a builder property, itemBuilder, that’s a factory builder and callback function specified as an anonymous function. Two parameters are passed to the function—the BuildContext, and the row iterator, i. The iterator begins at 0 and increments each time the function is called. It increments twice for every suggested word pairing: once for the ListTile, and once for the Divider. This model allows the suggested list to continue growing as the user scrolls.
(拙訳)ListView
クラスにはビルダープロパティがあります。itemBuilder
です。itemBuilder
はファクトリービルダーで、匿名関数を受け取るコールバック関数です(※ここ、よくわからない)。2つのパラメーターをその関数に渡します。BuildContext
と行番号のイテレーター(i)です。イテレーターは0はじまりで、関数が呼ばれるたびにインクリメントしていきます。サジェストされたワードペア1組に対して、2回インクリメントされます。まずListTile
に対して、次にDivider
に対して。このモデルはユーザースクロールに対して、サジェストリストを更新し続けます。
ListView
はデフォルトが無限スクロールで、itemCount
で上限をつけるらしい。
デバッグしてみたら、初期のListView
は26までインデックスが来るらしい。
サンプルでは2の倍数でDivider
を入れてる。
これはなんかそうするのがベストプラクティスなのかと思って色々調べてみたけど、そういう訳ではなさそう。
見栄え的にDivider
は欲しいけど、別に入れ方は自由。
渡されるインデックスの仕様はよくわからないが、画面に表示されている要素+αで来ている気がする
Part 1完了。
午後はここから。
setStateを呼ぶと、UIの更新が走るのはReactと同様。
ただ、setState(() { …… } );
ってなんだ?
setState(() {
if (alreadySaved) {
_saved.remove(pair);
} else {
_saved.add(pair);
}
});
Dardの文法をちゃんと理解できてなかったが、引数にクロージャー(というかコールバックと呼ぶ?)を渡すとき、
クロージャーに渡す引数がない場合、↑指定になるみたい。
コールバックがよくわからなくなってきた
コールバックを使った関数って、
func temp(completion: ((String) -> Void)) {
completion("temp")
}
temp { message in // (message) in でもよし
print(message)
}
// 引数を無視することもできる
temp { _ in
print("aaa")
}
頭の中混乱してきたので、図解してみた。
親メソッド、コールバックともに関数ではあるので、それぞれで引数・戻り値を持っている。
Dartに戻ると、
void setState(
VoidCallback fn
)
という定義は、両方とも戻り値がvoidのメソッド。
だが、setState -> VoidCallbackへの値を渡すことは可能なので、それを渡してないですよ、という意味で、
setState(() { … })
と()
が挟まっているみたい。
Swiftでいう_ in
に相当すると考えて良いのかな。
ここから画面遷移
チュートリアル完了!
あとは入門記事何個か見て終わるか
これ先に見るべきだった……
Layout集。これはわかりやすいかも