Flutter
雑多メモ
- プロジェクト名にアンダースコアがあると起動できないかも?
- パフォーマンス計測はリリースモードで行うこと。デバッグモードでは性能が落ちるため。
_
で名前が始まる変数はプライベートになる- Anonymous function を関数に渡す方法
some_func(() {/* ここに処理を記載 */})
- DevTools の使用方法
flutter run -d chrome
で flutter を開始し、ws://
から始まるアプリのアドレスをコピーする- VSCode で
Dart: Open DevTools
を選び、先程のアドレスをコピーしてConnect
ボタンをクリックする
- 文字の埋め込みは以下のようにする。
final myNumber = 123;
final message = "hello$a";
// 日本語圏の場合は区切りが曖昧なので{}を省略しない ほうがいいかも
final message = "hello${a}";
--- User Interface ---
ウィジェットの基本
Hello world
ミニマル構成
void main() {
runApp(
const Center(
child: Text(
'Hello, world!',
textDirection: TextDirection.ltr,
),
),
);
}
- root widget は画面全体を占める仕様になっている。詳細は後述の constraints を参照。
StatelessWidget
又はStatefulWidget
を継承したウィジェットを組み合わせて画面を作っていく。- Widget の主たる役割は
build()
メソッドを実装すること。
基本ウィジェット
Text
- テキスト
Column
|Row
- 縦・横方向に要素を並べる
- web でいう flexbox
Stack
- 複数の要素を出現順に z 方向に重ねて表示する
Positioned
widget で位置を調節できる- web でいう absolute 配置
Container
- パディング、マージン、ボーダー、背景色を使いたい時に利用する
BoxDecoration
で装飾する。- web でいう div
Expanded
- スペースを使い切るまで拡張する。または収まるように縮小する。
child
の中で使うflex
により拡張率を設定できる。
widget を引数として別の Widget に与える(React の children に相当)のは便利で強力なテクニックである。
マテリアルコンポーネントの利用
- Material design を使うには以下の設定を予め行っておくこと
# pubspec.yaml
name: my_app
flutter:
uses-material-design: true
MaterialApp
ウィジェットの役割Navigator
をセットアップする- Navigator は routes を管理するために使う。routes は Widget のスタックとして管理されており、個々の Widget(各画面) は文字列で識別される。
- マテリアルデザインのウィジェットを使うときには必ず先祖に必要。
MaterialApp
を最上位に配置し、各画面をScaffold
ウィジェットで作るのがグッドプラクティス。
void main() {
runApp(MaterialApp(
title: 'Flutter Tutorial',
home: TutorialHome(),
));
}
class TutorialHome extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: ...,
body: ...,
floatingActionButton: ...,
);
}
}
子ウィジェットへ引数を渡す
class MyText extends StatelessWidget {
// メンバ変数 の定義
final String firstName, lastName;
const MyText({
// 引数を取り出してメンバ変数にセット
// 必須のもの
required this.firstName,
// 必須でないもの
this.lastName = "",
// keyについてはよくわからんがsuperを呼ぶときに渡すらしい
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Center(child: Text(firstName + ' ' + lastName));
}
}
// 使い方
MyText(
firstName: 'John',
lastName: 'Doe',
)
ジェスチャー
- ボタンなどの場合は
onPressed()
などが用意されているのでそれを使う。 - それらがない場合は
GestureDetector
で要素をラップする。
GestureDetector(
onTap: ..., // タップ時に行いたい処理
child: ..., // ボタンなど
)
StatefulWidget
- State を生成し保持することのできる特別な widget
StatefulWidget
とState
が別オブジェクトである理由:- StatefulWidget は一時的なもの。再描写のたびに再生成される。
- State は継続的なもの。再描写されても同じものが使い回され(
createState()
は初回のみ呼ばれる)、状態を保持する。
- StatefulWidget の内容を減らし、StatelessWidget に抽出していくことが重要
- Stateful| Stateless は関係ない話だが、より上位の Widget で値を管理するということは、その値の寿命を伸ばすことを意味する。極端な話、
runApp()
に渡されるコンポーネントで管理される値は、アプリが起動している間中、ずっと保持される。
class Counter extends StatefulWidget {
final String name;
// このクラスは親から与えられた引数を保持する
// - 引数はstateのbuildメソッドから`widget.***`としてアクセス可能
// - 引数は常にfinalとして扱われる
const Counter({this.name = 'my counter', Key? key}) : super(key: key);
@override
_CounterState createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int _counter = 0;
void _increment() {
setState(() {
// setStateの中で値を変更することで画面が再描写される。
_counter++;
});
}
@override
build(context) {
return Column(
children: <Widget>[
RaisedButton(
onPressed: _increment,
child: Text('Increment'),
),
// `StatefulWidget`のメンバ変数とstateを組み合わせて使用できる
Text(widget.name + _counter.toString()),
],
);
}
};