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

2019年12月14日土曜日

1人 Flutter Advent Calendar 2019:#14 qrcode


QR codeを読んだり表示したりってことがあると思います。

QRだけで検索するとreaderとgeneratorが両方引っかかります。
QR code readerもViewで大きさを変えられるものから、全画面のカメラになるものまでありました。

ただ、全体的に言えることは、ただただ簡単。ということです。
QR code readerって作るのこんなに簡単だったのかよ。。。
って思うくらい簡単です。

QR code 生成:https://pub.dev/packages/qr_flutter
QR code 読取:https://pub.dev/packages/flutter_qr_reader

が良いかなとか思いました。flutter_qr_readerの方は、サンプルアプリとか中国語でちょっと構えてしまいますが、別にコード読めばすぐ分かる感じでした。

当たり前ですが、Transformとかもきちんと動きますし、ほんとに簡単でした。


https://github.com/matsuhiro/qr_code_test
サンプルです。
カメラにアクセスするためのpermissionを取得する部分はQRコードとは関係ないけど必要なので入れました。

2019年12月13日金曜日

1人 Flutter Advent Calendar 2019:#13 storyboard

UI作っているとUIの部品を実機で見せたくもなります。
デザイナーなどに共有するために、いちいち専用アプリを作るのもだるいですし、実機で見せてコミュニケーション取りたいケースはたくさんあると思います。

そんな時にStoryboadというのが便利そうです。
https://proandroiddev.com/storyboarding-widgets-in-flutter-96d79d9a72f0

ただ部品を並べるための枠が用意されるだけなのですが便利です。

デザイナーなどとコミュニケーションするにも良いですし、エンジニアにとっても良いshowcaseになるります。


class TextStory extends Story {
  @override
  List get storyContent {
    return [
      Text('this is story sample'),
    ];
  }
}

のようにStoryを拡張したclassを作成して


class StoryBoardApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Storyboard([
      TextStory(),
    ]);
  }
}

といあった感じでWidget作れば良いので、組み込みも楽ちんです。

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

2019年12月12日木曜日

1人 Flutter Advent Calendar 2019:#12 font

Flutterでfontをカスタマイズするのはほんとに簡単です。
ちょっとpubspec.yamlに


fonts:
        - family: NotoSerifJP-Regular
          fonts:
            - asset: assets/fonts/NotoSerifJP-Regular.otf
        - family: KouzanSoushoOTF
          fonts:
            - asset: assets/fonts/KouzanSoushoOTF.otf


とかいて

           Text(
              msg,
              style: TextStyle(
                fontFamily: 'NotoSerifJP-Regular',
              ),
            ),
            Text(
              msg,
              style: TextStyle(
                fontFamily: 'KouzanSoushoOTF',
              ),
            ),


こんな感じで指定するだけです。

すごい。
文字列の描画自体はどうしているのかと気になりましたが、WebのコンテンツなどのようにPlatformViewで表示するのも良いのでは?とか思ったりもしますが、独自に
https://github.com/flutter/engine/tree/master/third_party/txt
このあたりのライブラリを利用して独自に描画しているようです。

調べといてなんですが、本やニュースなど文字そのものが商品と直結しているようなものでない限りは、フォントに関してカスタマイズするということはそんなにないかなーとは思います。

サンプルは以下においてます
https://github.com/matsuhiro/font_log_test

2019年12月11日水曜日

1人 Flutter Advent Calendar 2019:#11 webview


WebViewにもいくつかpackageがあります。
Flutterを利用しているとWebViewを利用したくなるケースというのはお知らせなどでしょうか。

アプリ開発の経験があれば、だれしもWebViewにいろいろ表示したいという要望が上がって、WebViewとChromeやSafariとの挙動の違いに苦しんだことがあると思います。

WebView自体はChromeとは違うアプリであるというのが理由ですが、WebのFrontendのエンジニアや非エンジニアからは、理解されずにバグ扱いされることもあったりすると思います。
https://flutter.ioをユーザーに提示するにあたって3パターンあると思いますが、
  1. Chromeを開く
  2. Chrome custom tabで開く
  3. WebViewで開く
ユーザーの体験で問題ないならば1がおすすめです。
とはいえ、アプリ遷移させたたくないという要望もあると思うのですが、そのときには2かなと言う感じで、どうしてもヘッダーなどをカスタマイズしたいとかがあれば3を選択するのが良いと思います。

ただ、これはエンジニア的な視点で、大体の場合は3を作ることになるのかなーとかいうのも思う所ではあります。(ログを取りたいとかJSのI/Fがほしいとか。。)
それぞれ、ライブラリがあって、
  1. https://pub.dev/packages/url_launcher
  2. https://pub.dev/packages/flutter_custom_tabs or https://pub.dev/packages/flutter_web_browser
  3. https://pub.dev/packages/webview_flutter
かなと思います。
2は、flutter_custom_tabsの方がoption設定が柔軟ですが、そんなにいじらないと思うので、flutter_web_browserで良いんじゃないかと思ったりします。


とりあえず全部使ったサンプルです

https://github.com/matsuhiro/flutter_webview_test


2019年12月10日火曜日

1人 Flutter Advent Calendar 2019:#10 flexのbaseline


FlexBoxっぽいFlutterですが、Flexのレイアウトなどの実装は何が使われているのでしょうか?
Chromeはblinkで、React nativeはYogaです。

https://flutter.dev/docs/resources/rendering
ここにあるようにFlutterでは、RenderObjectがレイアウトと描画の基本クラスのようです。
FlutterのFlexの内部をみるとcreateRenderObjectでreturnしているのはRenderFlexです。
RenderFlexにperformLayoutがあって、ここでレイアウトを決めています。
他の種類のRenderXXXでも、performLayout内部でレイアウトが決められています。
ここからもわかるようにFlutterではレイアウトの仕組みも1からDartで実装しているようです。
そうしないとWebで実行とか出来ないわけですから、当たり前といえば当たり前かも知れません。

正直、これを読んだところで何が分かるというわけでも無いかなと思います。
全体的に眺めていて、目に止まったのはbaselineの計算部分でした。

例えば、RenderFlexの中にgetDistanceToBaselineという関数呼び出しがあります。alignmentをbaselineに設定した時に位置の計算に利用されます。
Reactだと以下のように出来ます。
https://snack.expo.io/BJfcbjDsH
gの下の部分が画像やViewよりも下に来ているのが分かると思います。

このように文字と他の要素をbaselineで揃えたいという要望は普通にあると思います。

これ、Flutterでどうやるのか?

rowに素直に並べてalignにbaselineを設定すると以下のようになります。
https://github.com/matsuhiro/flex_baseline_test/commit/043dfd7f9cd73cd0e8068fddf45923b3cd94629a

全然違いますね。

Flutter自体にはBaselineクラスというのがあるので、すべての要素を囲ってbaselineを0にすればそれっぽくはなります。
https://github.com/matsuhiro/flex_baseline_test/commit/804261a3453c7a8c2c2153c0535f214c2caee9ab

baselineの値を0ってなんでしょうね。baseline関連はdocumentもすごい充実しているわけではなかったです。

baseline関連はまだ発展途上なのかもななどとも感じます。

2019年12月9日月曜日

1人 Flutter Advent Calendar 2019:#9 flexで画像をcenterに


アプリを作成しているときには、大抵の場合は、画像自体も表示してほしいアスペクト比になっているたりすると思います。
とはいえ、画像を加工したりは普通にしたくなるとは思います。
中心に合わせて拡大したり、画像全体が表示されるようにしたりなど。

その場合にFlutterでどうするのかというとImage.assetのfitにcoverを指定したりすれば簡単に出来ます。
画像の表示をするだけならこれだけで十分ですし、SVGを表示したいと思った場合も、flutter_svgを利用すれば、変数にfitがあるので、それを指定すれば十分です。

その他にやりたくなりそうなのは、Containerの背景に画像を指定しようとした場合とかでしょうか。
その場合は、BoxDecorationとDecorationImageを利用すれば可能です。

この例ではjpegを背景としてchildにsvgを表示させています。
https://github.com/matsuhiro/flutter_image_test/blob/master/lib/main.dart#L121-L136

BoxDecorationはshapeで円に切り抜きなども出来るので、やってみます。
そうするとsvgの画像がはみ出しています。

こういった時にはClipOvalなどのClipXXXを使うのが良いかと思います。
https://github.com/matsuhiro/flutter_image_test/blob/master/lib/main.dart#L154-L170
SVGも含めて全体をclipする感じです。

コードの全体はここにあります。

https://github.com/matsuhiro/flutter_image_test