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

2019年12月8日日曜日

1人 Flutter Advent Calendar 2019:#8 flexboxで縦横くみあわせたり改行とか

FlexBoxを利用した場合の良さを実感できるのは、値としてflexやwrapを指定した場合かなと個人的には思っていたりします。


FlutterにもFlexがあるので、利用してみたいと思います。Flexの配下にFlexibleを指定しているのはパラメーターとしてflexを指定することで、要素の比率を決めるためです。
比率だけが固定されて幅が変われば全体的に変化します。

縦と横の指定を組み合わせれば、このようなことも可能です。

このレイアウト自体は、FlexとFlexibleとAspectで表現しています。

更にwrapについても見てみます。
FlexBoxだとwrapを指定することで、配下に存在する要素が折り返したりするのですが、Flex自体にはwrapに関連するパラメーターがありません。
かわりにWrapというWidgetが存在します。

Flexの配下に要素を並べたようにWrapの配下に要素を並べれば折返しが実現できます。
今回は、heightを固定して、widthをバラバラにした要素を横に並べてみました。




各要素のwidhtは同じなのですが、それが入る全体の幅が変わることで、UIの表示が変わっています。

これだけ見てもメリットが特に無いに感じるとは思うのですが、縦横比がバラバラの画像の高さだけを合わせて、全て並べるような場合がよく例に出されます。

上記2つを一つにまとめてみると以下のようになります。



幅の違うものを縦に並べていますが、レイアウトが崩れずに表示されているのがわかると思います。

Zeplinで表現されたものをFlutter上で表現することができることは以前に示しましたし、同じようなレイアウトをFlexでも実現可能なことは以前示しました。

どっちも良し悪しあるとは思いますが、個人的にはFlexibleやWrapなどを利用して表現するほうが、UI的な完成度は高くなるのではないかと感じます。
横幅に制限されないかつ、ただの縮小でなくレイアウトされるのは視認性の面でもメリットが高いと感じます。

ただ、FlexBoxなどに多少なりとも触れたことがあれば、FlexBoxのあれはどこにあるのだろうか?という発想でいろいろ探せますし、探してみるとFlutterにすでに欲しい機能の実装が存在することが多いと思います。
しかし、FlexBoxのことを何も知らないで、いきなりFlutterのflexなどを利用するのは少々厳しいのではないかとも感じます。

サンプルは
https://github.com/matsuhiro/flex_box_wrap
においてあります。

2019年12月7日土曜日

1人 Flutter Advent Calendar 2019:#7 layout by flex


ZeplinからUIを取り込むというのをやりましたが、Zeplinなどから取り込むのとは違う方法でやってみたいと思います。

基本的にはwidthなどに直値を指定せずに横幅が変わったらいい感じにレイアウトも変化するようにしたいです。

これを実現するにはaspect比を指定したり、親の幅のpercent指定でwidthを決めたりします。画像の重ね合わせはStackを利用して配置します。
https://github.com/matsuhiro/flex_box_percent

コードを見てもらえばわかるところではありますが、親の幅に合わせた変化のところで、FractionalSizedBoxを利用します。
それだけだと画像が縦長になってしまうので、AspectRatioで縦横比が常に一定になるようにしています。

このようなレイアウトを工夫した場合の一つのメリットはテキストサイズかなと思います。
基本的には横幅に合わせてレイアウトが変化するようにしているので、画面幅が小さくなったら、アスペクトを固定している画像などは小さくなります。
ただ、それと同じようにフォントまで小さくなってしまうと文字が読めないくらい小さくなることが懸念されます。
FittedBoxを利用して、Zeplinなどで指定されたデザインが歪まないように画面幅に合わせて大きさを変えた場合にこのようになります。フォントサイズは小さくなるので、視認性が悪くなる可能性があります。

しかし、フォントサイズを変えないようにすれば、以下のように文字の視認性が損なわれることはないです。(この例のデザインが良いかは別にして。。。)

サンプルのアプリだとGroupFlexというWidgetを作って、引数でwidhtを変えられるようにして、わかりやすくなるようにしています。

2019年12月6日金曜日

1人 Flutter Advent Calendar 2019:#6 list

Dartのtourのlistのページをみると
https://dart.dev/guides/language/language-tour#lists
listの中にifやforをまぜたり、...でlistを連結させられたりとか書いてあります。

面白そうなので、試してみるました。
      Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            if (_counter % 2 == 0)
              Text(
                'Count is even!',
              ),
            if (_counter % 2 != 0)
              for (var i = 0; i < _counter; i++) Text('This is $i'),
            ..._widgets(),
          ],
        ),
      ),

適当にこんな感じで入れるとエラーが出ます。2.2.2までサポートしていないよ。と。

Flutterの開発環境にはstable,master,dev.betaとあって、簡単に切り替えられるわけですが、stableで

$ flutter create [application name]

を実行するとpubspec.yamlの中のsdkversionが

sdk: ">=2.1.0 <3.0.0"

といった感じになります。
なので、2.2.2にすれば上記のコードも動作します。

サンプルはこんな感じです。
https://github.com/matsuhiro/flutter_dart_list_test
いつものfloating buttonをクリックするとcount upするやつです。

counterが偶数のときだけ「'Count is even!'」が表示されます。
奇数のときにはforで繰り返された文だけTextが表示されます。
最後に、適当にTextを関数からの戻り値で追加しています。

軽く使うなら便利だなと思いますが、たくさんWidgetを配置するならStatelessWidgetを作ったほうが良いのだろうとは思います。