[2022.11] O: Flutter中級者になる / KR1: 入門書を1冊読む / KR2: Riverpodを使った状態管理ができる / KR3: テストが書ける
2022.11.19 更新
O: Flutter中級者になる
KR1: 入門書を1冊読む[50%]
先月に引き続き「現場で使える Flutter開発入門 (Compass Booksシリーズ)」を読み進める
KR2: Riverpodを使った状態管理ができる[0%]
BKR2-1: 色々な状態管理手法のメリット・デメリットを整理し、Riverpodの特徴を言語化する [30%]
BKR2-2: Riverpodを使ったアプリを一つ作る [30%]
KR3: テストが書ける[0%]
BKR3-1: Unit Testを書く[0%]
BKR3-2:Widget Testを書く[100%]
BKR3-3: Integration Testを書く[0%]
2022.11.01
Integration Testに使えそうなツール
MediaQueryとは
主な役割は画面サイズを取得すること
MediaQueryはInheritedWidgetのサブクラス
Inherited Widgetとは
「祖先(任意の親)の位置にあるInheritedElementへの参照を、全てのElementが保持している」
ツリーの順序を飛び越えて、祖先のWidgetの持つデータ(アプリ全体の色やフォントなど)を使いたい場合の機能らしい
3つのツリーとは
- Widgetツリー
- Immutableなオブジェクト
- Widget.createElementでペアとなるElementを生成
- Elementツリー
- 親と子への参照を持つ
- BuildContextのimplements
- RenderObjectツリー
- 画面にUIを描画するオブジェクト
インスタンスをなるべく再利用して、レンダリングの高速化を行えるようにしている。
この動画が分かりやすかった
Flutterのアーキテクチャ
2022.11.02
Riverpodの概要
providerを進化させた状態管理ツール(同じ作者)
provider: InheritedWidgetをsimplifyしたもの
riverpod: InheritedWidgetをfrom scratchで作り直したもの
-
providerとriverpodは共通した目的を持っている
- stateの作成・監視・削除を安全に行いたい
- Flutterのdevtoolで見えるオブジェクトにしたい
- Testableにしたい
- InheritedWidgetsをもっと読みやすくしたい
- データフローを一方向にしたい
-
riverpodがproviderより優れているところ
- compile safeなオブジェクトになっているので、runtime exceptionに悩まされない
- より柔軟な実装が可能になった
- 同じ型のproviderを複数使える
- providerのstateが不要になったら自動で削除できる
- computed stateを使える
- providerをprivateにできる
- 非同期なstateを使える?
- Flutterと独立した実装になっている
InheritedWidgetを一から実装し直したことで、上記の利点を得られるようになった。
Providerの概要
Riverpod使ってみる
flutter pub add flutter_riverpod
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(const ProviderScope(child: MyApp()));
}
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final counterProvider = StateProvider((ref) => 0);
class Home extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(title: const Text('Counter example')),
body: Center(
// Consumer is a widget that allows you reading providers.
child: Consumer(
builder: (context, ref, _) {
final count = ref.watch(counterProvider);
return Text('$count');
},
),
),
floatingActionButton: FloatingActionButton(
// The read method is a utility to read a provider without listening to it
onPressed: () => ref.read(counterProvider.notifier).state++,
child: const Icon(Icons.add),
),
);
}
}
変化
- 今までStatelessWidgetやStatefullWidgetを継承していたが、ConsumerWidgetになった
- buildメソッドの引数にWidgetRef型のrefが増えた
- ref.watch(provider)で状態の監視
- ref.read(provider.notifier)で状態の読み込み
Riverpodを使うときのアーキテクチャ
2021.11にriverpod1.0.0がリリースされたから、これ以前の記事・書籍はインターフェースが異なる
古い書籍買っちゃったw
https://www.amazon.co.jp/入門-Riverpod-岡花-智貴-ebook/dp/B09D9Q4R2F?encoding=UTF8&qid=&sr=&linkCode=sl1&tag=flt0c-22&linkId=a062e51c70a48671ed2abb66d850323a&language=ja_JP&ref=as_li_ss_tl
後で読む
2022.11.04
Dartの気になる文法メモ
継承・抽象クラス・インターフェース・Mixinの違い
finalとconstの違いは「値が固定されるタイミング」
- const: コンパイル時に値が代入され、それ以降変更できない
- final: 実行時に値が代入され、それ以降変更できない
Null Safety
変数がnullを許容しない、のがデフォルトになっている
int? x
のようにするとpythonで言うところのOptionalになる
x!
のようにするとnullを許容しないように変更できる
Generic型も使える
List<Object>
public, protected, privateのようなキーワードがなく、アンダースコアでプライベートな変数・関数を表現する
libraryを部分的にimportする
// Import only foo.
import 'package:lib1/lib1.dart' show foo;
// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;
必要になったらimportするlazy loading
import 'package:greetings/hello.dart' deferred as hello;
Future<void> greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
- 用途
- webアプリの初期化処理の時間を削減する
- ABテストでどちらか一方しか実行しないとき
- optionalなスクリーンやダイアログなどたまにしか使わないライブラリ
2022.11.13
「再実装Flutter」という書籍(技術書展で見つけた本)をざっと読んだのでまとめる。
KRと関係ないことばかりやっているな笑
Engine/TaskRunner
- https://github.com/organic-nailer/flume/blob/72f852341f2927b6536580fc085750eac9091ca9/src/main/kotlin/engine/TaskRunner.kt
- https://zenn.dev/fastriver/books/reimpl-flutter/viewer/taskrunner
マルチスレッドでUIフレームワークを動かす。
なぜなら、画面表示を30fps以上で行う必要があるから。
FlutterのEngine内でスレッドはTaskRunnerと呼ばれていて、4つのTaskRunnerが動いている。
- Platform
- メインスレッド
- UI
- Dartコードを実行するスレッド
- Raster
- 画面描画するスレッド
- IO
- 重い処理を行うスレッド
Rasterizeとは
何らかの抽象度の高い形式で記述された画像データを、コンピュータが最終的に出力することのできる画素の集まり(ビットマップ形式/ラスター形式)に変換すること
Layerツリーの実装
- https://zenn.dev/fastriver/books/reimpl-flutter/viewer/layer-tree
- https://github.com/organic-nailer/flume/blob/master/src/main/kotlin/common/LayerTree.kt
- https://developers.cyberagent.co.jp/blog/archives/36869/
RenderTree -> LayerTree -> Engineという流れでデータが送られるらしい
Engine は主に C++で書かれており、Skia という C++のグラフィックライブラリを描画に用いています
LayerTreeはSkiaが理解できるデータ形式になっているということだろうか。
データ形式というよりは描画コマンドを保持しているらしい。
- PictureLayer
- 描画コマンドを保持するLayer
- ContainerLayer
- 複数のLayerを保持するコンテナの役割を持つLayer
- TransformLayer, OffsetLayer, ClipRectLayerなど子に特定のエフェクトを作用されるために使用する
RenderObjectの実装
RenderツリーからLayerツリーを生成する
2022.11.14
skiaのpythonバインディング発見
pythonでflutter作れそう
2022.11.15
Flutter Architecture Overviewを読む
Reactive user interfaces
Flutterは「reactive」で「pseudo-declarative UI」なframeworkである
- 伝統的なUI frameworkの問題点
- 「状態」とUIとの整合性が取れなくなる
- MVC的なframeworkの問題点
- UI elementの作成と更新が同期しずらい
- Flutter
- Widgetはimmutableなclassであり、configのような存在
- WidgetツリーからRenderツリーを生成するので、
UI = f(state)
という構造が明確であり、状態とUIの分離、更新漏れが起きない.
らしい。
Flutter(Widget Tree) -> Render Tree -> Layout Tree
エンジニアは作りたいUIを設定ファイルのような形で記述すれば良いだけなところが「declarative」なのかな。
なんとなくわかったくらいの感触。
Rendering and layout
Widgetツリーから画面のピクセルまで変換される仕組みについて解説する章
2022.11.19
Flutterテストについて調べる
全体像
Widget Test
ステップ
Add the flutter_test dependency.
Create a widget to test.
Create a testWidgets test.
Build the widget using the WidgetTester.
Search for the widget using a Finder.
Verify the widget using a Matcher.
サンプルコード