2019年12月21日土曜日

1人 Flutter Advent Calendar 2019:#21 通信状態監視


通信状態を監視して、状態を変えて、ユーザーに操作を促すとか普通にやりたくなると思います。
Snackbarだしたりとかとか。

それもFlutterだと簡単に出来ます。
Flutterのいいところは、誰かが作ったものがたくさんあるところですね。Androidとかも同じなんですが、導入までのハードルが低すぎて引きます。

https://pub.dev/packages/connectivity
というのを使ってみました。

これ使うと通信状態監視できます。
サンプルは以下です。
https://github.com/matsuhiro/flutter_connectivity_test
airplane modeとかにすれば、snackbar出てくると思います。

ちょっと工夫したところとしては、Snackbarを表示するところでしょうか?
setStateでUIが変わるんですが、そこでSnackbarを表示しようとするとエラーになるため、Delay 0秒で、UIの構築の次の処理に回しています。

2019年12月20日金曜日

1人 Flutter Advent Calendar 2019:#20 delayedで逐次処理


DartはSingle threadで操作しているので、どこかで重たい処理が走ったらCPUを専有してUIの更新が止まります。

ネットワーク通信とかでも止まりそうに思いますが、Socketからデータを読み出すまでは、待ち状態になってCPUが動いていないのでUIがブロックはされないようです。

でも、重たい処理をしたいこともあると思います。
Fileのコピーや動画のEncodeなどは、for 文で繰り返し行ったりすると思います。
そんな時には、Event loopに少しづつ処理をしてもらう感じにしたいこともあると思います。
そうすれば
「少しencode」→「UI更新」→「少しencode」→「UI更新」...
のようになって、UIをblockすることもないです。

AndroidだったらrunOnUiThreadとか便利なmethodがありますが、dartのIsolateにはそういった関数は無さそうでした。

似たようなことは、
Future.delayed(Duration(seconds: 0), doSomething);
のようにすれば、実現できそうです。
https://github.com/matsuhiro/flutter_async_test
サンプルです。
いつものやつですが、floatingボタンを押すと延々とcount upしていきます。

サンプルを動かしてもらえればわかりますが、duration = 0でひたすら動いていますが、timerも動いているので、UIがBlockされていません。

これが何をやっているかというとdoSomethingの処理をひらすらMain loopのqueueに入れていますが、TimerもMain loopのqueueに処理を入れているので、どちらも逐次処理されている感じです。

とはいえ、重い処理の場合はcomputeとかを素直に使うのが良いのかなと思います。
現実には、そこまで重い処理はC++で書くServer側に処理を任せるなど、dartにこだわらない選択をすることになるのかなと思ったりします。

2019年12月19日木曜日

1人 Flutter Advent Calendar 2019:#19 DevTool


FlutterはDevToolが充実していています。
AndroidやiOSならMarketでアプリの売上につながるから開発環境を整えるモチベーションもわかりますが、なぜFlutterでこんなに整っているのか。しかも無料。

Performanceを見るツールもありますが
https://flutter.dev/docs/development/tools/devtools/inspector
のにLayoutを調べる機能もあります。
Android Studioを利用しているならば⇧⌘A(Shift + command + a)で、「flutter inspector」で検索すると出てきます。

大体触れば何かが起こるので、だいたい分かると思いますが「Enable Select widget mode」はちょっと何が起こるのかすぐにはわからないと思います。
リンクをたどっていくと動画にたどりつくので見ればすぐに分かりますが、マウスで選んだ場所のWidgetがすぐに分かります。
https://youtu.be/JIcmJNT9DNI?t=361

AndroidでもLayout Inspectorがありますが、同じような機能です。

動画でやっているようなズルズルカーソルを動かすと選択されるWidgetが変わるようなことも実機でもできました。
指を置きっぱなしにして移動すれば実機上でどこに何があるのかわかります。

こんな機能、どこで使うんだろう。自分で作ったやつなら把握しているし必要ないだろうと思うかも知れないですが、人が作ったものをメンテする時には非常に役に立つと思います。
このUIを表示しているコードどこにあるんだろう。見つからない。。。とか、既存のアプリをメンテしている人ならよくあると思います。

Androidの場合、Layout Inspectorからidを見つけて、そのidから検索してとかを僕はしますが、FlutterならClick一つでcodeまでjumpするので、codeにたどり着くのが非常に早いですし、多少Layoutのcodeのネストが深くてもツールで追いかけられるのはすごいです。

2019年12月18日水曜日

1人 Flutter Advent Calendar 2019:#18 fps check


Flutterでは60fpsを目指しているとありますが、実際に60fpsがkeepされているかどうやったら分かるでしょうか?

Android StudioのでFlutter Performanceのwindowを開くのが最も良さそうです。
https://flutter.dev/docs/development/tools/android-studio#show-performance-data
ただ、release buildの時には当然使えません。

そんな時に大体のfpsを取ろうと思うとendOfFrameというのが使えると思います。
runAppの内部で行っていることをmainの中に書いて、WidgetsBindingにあるendOfFrameがfutureになっているので、thenで呼び出される関数を設定します。
これは、現在のframeの最後に呼び出されるだけで、毎frameごとに何回も呼び出されるものではないです。そのため、常に監視をしたい場合は、callbackの最後にもう一度設定をする必要があります。

https://github.com/matsuhiro/fps_check_test/blob/master/lib/main.dart#L3-L21

こんな感じ。

ただ、Flutterは、描画の必要がなければIdle状態になります。
ゲームのように常にEvent Loopが60pfsで動き続けているわけではないです。

実際に、Android studioで、Flutter Performanceのwindowを開くと描画がされていないときには、処理を行われていないことがはっきり分かると思います。

ここで示した例では、endOfFrameにcallbackを設定しています。そうするとidleになっていたものが起動されます。
そのため、Idleにならずに処理が続くことになります。
当然ですが、普通の状況では行うべきではないです。
release buildでfpsが知りたいときには使えるかもしれない程度で、実際にはこんな事しないでdebug buildでしっかりとperformanceのチューニングを行うのが良いでしょう。

サンプルです。

https://github.com/matsuhiro/fps_check_test

Flutter PerformanceはAndroid Studioの右下にあるのでclickすると見れます。
もしくは、⇧⌘A(Shift + command + a)で、「Flutter Performance」で検索してください。

2019年12月17日火曜日

1人 Flutter Advent Calendar 2019:#17 log


Flutterで適当にlogを出そうと思ったらとても楽で、printを使えばよいだけです。
それだけで、AndroidでもiOSでもログを見ることが出来ます。

ただ、これ、製品を作るときには困ります。
製品版では出したくないですよね。

Androidではproguradの設定で消したりtimber使ったりすると思います。
同じようにFlutterでも最初からLogについてはきちんと考えておいたほうが良いです。

print自体の制御をしたいと思っても簡単にprintの中身についてはわかりませんでした。
https://github.com/flutter/engine/blob/master/lib/ui/dart_runtime_hooks.cc#L200
このあたりで出力しているようなのですが、外部からの入力値によって出す出さないを切り替えるみたいな仕組みが無いような。。。うーん。

Flutterで利用するlogのツールとしては
https://pub.dev/packages/logger
が良さそうです。

特別なことはしておらずv,d,i,w,eと別れていて、使いやすいです。完全にログを出したくないと思えばLogFilterなどを作成して設定すれば良いです。
https://github.com/matsuhiro/font_log_test/blob/master/lib/main.dart#L11-L15
こんな感じで使えます。

といいえ、適当にprintだけ書いてそのままcommitしてしまうような事故もあるとは思うので、print自体をいじりたいもんです。


2019年12月16日月曜日

1人 Flutter Advent Calendar 2019:#16 cookbook


Flutterの記事はたくさんあるし、動画もあるしで、Flutterはドキュメントには事かきません。
日本語のドキュメントもあるし、日本語の本も何冊かあります。

なので、別に英語のドキュメントを頑張って読まなくても良いのかなとか思ったりもします。
ただでさえFlutterって公式のドキュメントが充実しているので、一つ一つ読んでいるとキリがないというのもありますが。

でも、Cookbookは一通り読んでおいたほうが良いのかなーとか思ったりもします。
こういう事言うと、「いや、これも。いや、あれも。」みたいな意見も出るでしょうけど、CookBookがいいなと思ったのは、テーマが具体的でこういうのってプロダクトで必要だよね。といったものがまとめられているという印象だからです。だからCookbookなのでしょうけど。

例えば
https://flutter.dev/docs/cookbook/images/cached-images
これとか、プロダクトで画像データのcacheとか絶対やると思いますし。

データの永続化だったり動画の再生だったりとかとか。いくつかの必要そうなテーマで短めに書かれているので読みやすいです。

FlutterのWidgetを一つ一つ見ていくのも面白いですが、実践的なものがよくまとまっているのはこれかなと。

githubにcodeもあればいいんですがね。自分で作れという話ではありますが、現在はないみたいです。

2019年12月15日日曜日

1人 Flutter Advent Calendar 2019:#15 click event


特定のWidgetをClickしたら処理をするのは当たり前ですが、その周りの余白だったりをclickした時になにか処理をしたい時にはどうすればよいか?

大抵は、Cancelのような挙動を設定したくなると思います。
Containerなどにもtapのイベントを設定することは可能です。GestureDetectorを利用して実装してみます。


https://github.com/matsuhiro/click_event_test/commit/39081b5a452cf23dd76f277c5b6919c1c4f8ca87#diff-fe53fad46868a294b309fc85ed138997R94-R132

これを適当なところにいれて表示させます。

Enable Debug paintをすると上のようになります。

Button以外の部分をtapすると「container tapped」と表示されるのが分かると思います。
Paddingの上をTapしても「container tapped」が表示されるので、だいたい想像通りに動作するようです。

ただ、Containerが何も描画していないとonTapが反応しません。
Event handingをしているのが、描画のObjectであるのが原因ぽかったです。
そのため、以下のようなコードだと
https://github.com/matsuhiro/click_event_test/blob/53596dfd7a6fd8e625ed8c27616be672f0a0b052/lib/main.dart#L94-L141
「inner container tapped」は出力されません。

「inner container tapped」のコードが記述されているのは、GestureDetectorとContainerを2重に入れ子にしたものの内側のGestureDetectorのonTapで表示させようとしています。
内側のContainerは何も描画していないので、onTapが反応しません。

そこで
https://github.com/matsuhiro/click_event_test/commit/8d44ed0b81a21374e5718249a3502ff4ccc0a3ac
このように透明な色を描画すれば「inner container tapped」が出力されて、ただの入れ物のContainerでもonTapを処理できるようになります。

サンプルは以下です。
https://github.com/matsuhiro/click_event_test