AndroidStudioで「Flutter Device Selection」が表示されない

現象

f:id:Nageler:20200903001641p:plain

「MAIN.DART」の左側に表示されるであろう、デバッグするデバイスを選択できるメニューが何故か表示されない

これがないとWebでのデバッグが面倒(コマンド叩かないといけない)

解決

StackOverflowの微妙な場所に解決策を持つ神が居りました。

stackoverflow.com

May be you have not setup environment variable for android sdk

ということでAndroid SDKのパスを正しく設定しましょう!

f:id:Nageler:20200903002107p:plain

AndroidStudioに戻ると

f:id:Nageler:20200903002259p:plain

表示されましたね

後記

このあと再現のためにPATH弄ったんですが消えなかったんですよね...

Flutter Webでホームページを作ってみた所感

Flutter Webでホームページを公開しました

こんにちは、Fastriver(@fastriver_org)です。先日私の所属しているKCS(Computer Society)の新歓特設サイトを制作、公開させていただきました。

kcs1959.jp

Flutterはモバイル向けのフレームワークなので、「普通の」Webサイトを作ってみた事例は少ないと思われるためここに所感を書いておきます。

環境

>flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel unknown, v1.14.6, on Microsoft Windows [Version 10.0.18363.720], locale ja-JP)

[√] Android toolchain - develop for Android devices (Android SDK version 29.0.1)
[√] Chrome - develop for the web
[√] Android Studio (version 3.6)
[√] IntelliJ IDEA Community Edition (version 2019.2)
[√] VS Code (version 1.43.1)
[√] Connected device (2 available)

• No issues found!

公開時点(2020/03/21)では Beta channelはv1.15.17なのですが、flutter runがうまく作動しないためv1.14.6に戻して作業しています。

レスポンシブ対応

Webサイトのレイアウトを考える上でレスポンシブ対応は非常に重要です。 特にflutterだとモバイル向けのデザインになりがちですが、それをPCで見るとつらいものがあります。

f:id:Nageler:20200324140144p:plain
AppBarやTabBarはそのまま使うときつい

ですがFlutter、よく出来ているのでレスポンシブに対応するためのLayoutが多く揃っています。 私はページのベースとしてLayoutBuilderを使いました。

LayoutBuilder

api.flutter.dev

LayoutBuilderはbuilderの引数にBoxConstraintsを持ち、現在の"そのWidgetの"大きさを教えてくれます。 またサイズが変わると都度リビルドをかけてくれるので、その値によってLayoutを組み直します。

情報としては画面のアスペクト比・PC or Phoneが欲しかったので以下のクラスを定義してツリーに流しました。

その他

その他レスポンシブに便利だったWidgetたちです。

AspectRatio

api.flutter.dev

画面の比率に関わらずWidgetを一定の縦横比にリサイズしてくれます

Align

api.flutter.dev

基本はStack内で使うものですが、Alignで囲むと大きさが(部分的に)親に依存しなくなるので便利です

Wrap

api.flutter.dev

Column, Rowに似たもので、Mainの幅が足りなくなると自動的に折り返してくれます。chipとかによく使われるやつですね

Expanded

api.flutter.dev

ColumnやRowに使います。余り部分を埋めてくれます

AutoSizeText

pub.dev

幅に合わせて文字の大きさを自動的に決定してくれます。最高

ExpandedGrid

pub.dev

私が作ったもので、こんな感じのViewが作れます。

パフォーマンス

Flutterさん、クロスプラットフォームにしてはAndroid, iOSでの実行速度が速く問題ないだろうと思っていたのですが、 Flutter Webは現状かなり重いです。

それもそのはずそれぞれの実装は

であり、またFlutter Webは殆どをCanvasで描画しているということがあります。 Stable版に期待したいところです。

特に気になる点

起動

main.dart.jsの読み込みに時間がかかります。

Google PageSpeed Insightsでみると、以下のように8秒以上かかっていることがわかります。

f:id:Nageler:20200327113706p:plain

プロジェクト作ったときのサンプルでも数秒かかります、削減方法はよくわかってないです。

developers.google.com

スクロール

今回一番躓いたのがスクロールでした。文字だけのスクロールでも60fpsを達成できません。 特に画像を中に数枚置くと、動きがガクガクになりました(画像の大きさより数の方が影響が大きい)。

こちらのトップページを見ていただけるとわかると思います。

またしっかり調査したわけではないのですが、周りの反応からみて泥ChromeよりもアイポンSafariの方がスクロールに関してはスムーズだったようです。 (端末のスペックにもよるので一概には言えない)

一応対策として以下のようなStatefulWidgetを作り、スクロールの現在位置を監視しながら表示されていないViewのOpacityを0にすることで 常時読み込みのWidgetを減らしています。

少し長いのでPageでの実装は

github.com

を見てください。

現状Flutter Webでスクロールさせるのはやめたほうがいいです。許されても単純なListViewだけです(ListView.Builderを使おう!)。

パス(URL)

Webサイトにとって完全な単一ページでない限り、それぞれのページにパスを設定し直接飛べるようにしなければなりません。 Flutter Webでは基本全てが"#/"に飛ばされるのでFlutter側でパラメータの読み取り・URLの変更・画面遷移をする必要があります。

公式ではまだまだ対応が少なく(そもそもモバイルのroutingを使っているので面倒)、ライブラリも決定的なものはないようです。

私が実装したかった機能はこれらです

  • 画面遷移時にURLを変更
  • そのURLに直接アクセスするとそのページに飛ぶ(rootからツリーを辿る)
  • パラメータを渡す
  • Modalな画面遷移

画面遷移時のURL変更

これは標準で対応してくれているので、すぐにできます。やり方はMaterialAppsのRoutesに登録する方法と、MaterialPageRoutesを使う方法があります。

MaterialAppsにはroutesという引数があるので、パスと遷移先のページを指定します。

その後遷移したいときに、Navigator.pushNamde()にパスを渡して呼び出します

実はこの方法を取れば2個目の直接遷移ができます。ただしURLには注意が必要で、#/が入っていなければいけません。

https://kcs1959.github.io/2020new/#/works

routesに登録していない場合でもMaterialPageRoutesでパスを指定することでURLが変更されます。

パラメータを渡す

Flutter WebがURLから呼ばれたときの挙動は、

  1. routes内に "#"より後(hoge.hoge/#[この部分])の一致するものはあるか確認、ある場合は遷移
  2. onGenerateRoute()を呼び出す
  3. 返り値がnullの場合はhomeに指定されたページに遷移

onGenerateRoute、homeは共にMaterialAppsの引数に取ります。onGenerateRouteは引数にRouteSettingsを取り、 その中(settings.name)に"#"より後のパスが入っているのでこれをパースすることでパラメータを得られます。

後はUriクラスなどを使って適当に処理しましょう。

Modalな画面遷移

f:id:Nageler:20200327205827p:plain
こういう画面を作りたいわけだ

かっこいいのでHeroでつながったポップアップ式の画面遷移にしたい+都合上それぞれにURLを振りたい

ということを考えていました。しかし

  • 自由なレイアウトのためDialogではなくNavigatorで遷移する
  • URLを振るためにPushNamed()で遷移したい
  • ページを重ねるために背景を透明にしなければならない
  • しかしPushNamed()だと背景を透明にすることが出来ない

となっており、一筋縄ではいきません。

3つ目についてですが、Navigatorは内部でStackを利用し、Pageを上に重ねて遷移しているらしい(未確認)ので PageRouteBuilderのopaqueをfalseにすると背景を透明にでき、下のページが見える感じになります。

どうしてもパスの文字列で画面遷移したかったので、routesを保持するクラスを作ってそこからPageRouteBuilderを呼ぶようにしました。

ハイパーリンク

FlutterのTextViewはHTMLの文字とは違い<a>タグでリンクを貼ることができません。 そのためなにかWidgetにタッチイベントを付けてそこから飛ばす実装をすることになります。

Flutter Webでも外部リンクはurl_launcherを使えます。

pub.dev

タッチイベントを貼るのには

  • Card+Icon
  • Button類

辺りが分かりやすいと思います。

hover時にカーソルを変えるのはここを参考にしました。

medium.com

埋め込み

他サイト、HTML、MarkDownなどの埋め込みにはeasy_web_viewが使えました。

pub.dev

内部でdart:htmlを使っているようなので動作も軽快です。ただMarkDownについてはCSSの設定がよくわかりませんでした。

PWA

PWAは現在Flutter Webは標準で対応しています。特段の設定は必要ないようです。情報はweb/manifest.jsonに書けば反映されます。

スプラッシュ画面

パフォーマンスの項でも話しましたが、Flutter Webは起動がなかなか遅いです。白い画面が続くとブラウザバックの率が高まります。 少し考えると、main.dart.jsが読み込まれるまではindex.htmlの内容が表示されているわけで、そこに写真を貼り付ければ スプラッシュ画面の代わりとして使えます。

アナリティクス

google analyticsは、ページごとに統計をとってくれるようなので普通のWebページ用のだとHPに訪問したかどうかしかわかりません(実質単一ページのため)。

Firebaseパッケージではなんとanalyticsが使えます。

pub.dev

なのでPageを読み込む時(initState()とか?)に

analytics().logEvent("PAGE_MAIN", null);

のように埋め込んでイベントを記録することで、ページごとの訪問数を記録できます。

まとめ

  • 現状パフォーマンスにおいて実用には向かない
  • パス周りも苦労を強いられる
  • スクロールさせないSPAであればそこそこ行ける気がする
  • 細かい機能はPackageを漁れば結構見つかる
  • HTML,CSS,JSを書かなくていいのは素晴らしい

Flutter Webの将来は明るいと思います。発展させていきましょう!

Flutter on the WebでFirebase導入して詰まった

状況

Flutter WebはFirebaseを導入する際、モバイルとは違いJavaScriptを読み込んで使う。そのためindex.htmlに\<script>を仕込むのだが、異変が起きた。

Launching lib\main.dart on Chrome in debug mode...
Building application for the web...
Attempting to connect to browser instance..

flutter runをしてもここから一切動かないのだ。ググったらimportを消せとか色々策があったが効果はなかった。

解決

A . main.dart.jsを読み込んだ後にFirebaseの設定をしようとしていませんか?

はい。

<body>
<!-- The core Firebase JS SDK is always required and must be listed first -->
<script src="https://www.gstatic.com/firebasejs/7.8.1/firebase-app.js"></script>

<!-- TODO: Add SDKs for Firebase products that you want to use
     https://firebase.google.com/docs/web/setup#available-libraries -->
<script src="https://www.gstatic.com/firebasejs/7.8.1/firebase-analytics.js"></script>

<script>
  // Your web app's Firebase configuration
  var firebaseConfig = {
      //略
  };
  // Initialize Firebase
  firebase.initializeApp(firebaseConfig);
  firebase.analytics();
</script>
<script src="main.dart.js" type="application/javascript"></script>
</body>

とするのが正解です。

真理

f:id:Nageler:20200208204731p:plain

終わりに

公式の文章はちゃんと読もうね!

下からひょこっと出てくるボタンを作る(Android/Kotlin)

やりたいこと

画面を開いたら下からひょこっとボタンが出てくる

ボタンを下にフリックすると引っ込む

できたもの

よい

前提として

これは

nageler.hatenablog.com

の応用例です。実装の際はこちらも一読すると良いと思います

作ってみる

環境はAndroidStudio+Kotlinです。

ボタンを用意する

画面外下から出したいのでConstraintLayoutを使って下に配置します。

Top_ToBottomOfで画面外配置できるのかなり便利

ボタンを出す

これが本題かな?(そうでもない)

アニメーションには前回も使ったSpringAnimationを使います。

developer.android.com

説明は前の分を見てください。

遅延を付けて出す

画面を出してから少し遅延を付けて出したいので、Kotlinの非同期処理としてCoroutineを利用します。

dependencies {
    //略

    //Physical Based Animation
    implementation 'androidx.dynamicanimation:dynamicanimation:1.0.0'

    //Coroutine
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3"
}

GlobalScopeでCoroutineを立て、1秒delayしてからアニメーションを実行するよう設定します。animation.start()はバックグラウンドスレッドで呼び出すと怒られるのでwithContextでスレッドを変えましょう。

出す長さの計算としてviewHeightとmarginを使っています。

val buttonMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16f+16f, resources.displayMetrics)

この一文ですが、SpringAnimationはPixel単位で動かすのでこう書くとDPから変換してくれるらしいです(やらないと端末ごとに移動距離が歪になる)。

引っ込める

利便性のため引っ込める関数も作っておきます。こちらは呼び出されたらすぐ実行するだけなのでCoroutineなしで書きます。

ぐにぐに動かす

前の記事のとおりです。今回は上方向に動いてほしくないので上に指が動いたときのみ制限をかけるようにします。

originAnimationは外部からアクセスしたいのでActivity内に宣言しました。

下へのフリックで引っ込むようにする

フリックの検知にはGestureDetector.SimpleOnGestureListenerを使います。

developer.android.com

class MainActivity : AppCompatActivity() {

    private lateinit var buttonGestureDetector: GestureDetector

    //略
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val buttonGestureListener = object: GestureDetector.SimpleOnGestureListener() {
            override fun onFling(
                e1: MotionEvent?,
                e2: MotionEvent?,
                velocityX: Float,
                velocityY: Float
            ): Boolean {
                println("速度: Y=$velocityY")
                return super.onFling(e1, e2, velocityX, velocityY)
            }
        }

        buttonGestureDetector = GestureDetector(this, buttonGestureListener)

        limitedButton.setOnTouchListener { _, e ->
            //略

            buttonGestureDetector.onTouchEvent(e)
        }

        //略
    }
}

ボタンのフリックした速度が出力されると思います。

適当な速度を超えたら引っ込むようにします。Viewが移動していてもよしなにアニメーションを作ってくれるのがSpringAnimationの素晴らしいところ。

パラメータがいくつかあるのでそれらを調整すれば、それなりの動きになると思います(最初の動画は動きすぎ...)。

最後に

Animationたのしい

出来上がったコードはGitHubにあげてあります(Branch切ってるだけなので見にくい)。

github.com

指で"少し引っ張れる"Animationを付ける(Android/Kotlin)

正式名がよく分からない

Material Designなどではあまり見かけませんが、必要性が出てきたので作ってみました。

何に使うのか

業務にて動きのある/指で動かせるボタンを作る機会があったのですが、動かせる範囲を制限したくこのような動きを考えました。これについては他の記事で話しましょう。

作ってみる

環境はAndroidStudio+Kotlinです。

SpringAnimation

今回、指を離した後に戻る部分のアニメーションにはSpringAnimationを使っています。これはAndroidで使えるAnimationのうちの一つ、「Physical based Animation」の一種で、簡単にバネの動きをViewに対して実装できます。

developer.android.com

app/build.gradleに以下を追加しましょう。

dependencies {
    //略

    implementation 'androidx.dynamicanimation:dynamicanimation:1.0.0'
}

指に追随させる

指の動きに合わせてViewを動かすには、ViewのonTouchListenerを利用します。

developer.android.com

limitedButton.yに座標(pixel)を書き込むことでボタンを移動できるのですが、ここではtouchGapYとstartRawYを使って制御しています。

ボタンのY座標には"親の"Viewの左上からの座標を指定しなければならないのに対しe.rawYで取得できるのは"画面の"左上からの座標です。この分のズレを吸収するためにViewが押された瞬間に2つの差分を取ってtouchGapYに保持します。そして動いたときにはtouchGapYを引いてあげればちょうど良くなるということです。

MotionEvent.ACTION_DOWN -> {
      touchGapY = e.rawY - limitedButton.y
      startRawY = e.rawY
}

MotionEvent.ACTION_MOVE -> {
     limitedButton.y = e.rawY - touchGapY
}

動きの限界を設定する

本題です。限界を設定するにしても動きがスムーズでなければ不自然になってしまいます。そこで私はarctanを使って滑らかさを作りました。なにかの値に収束する関数であればなんでもいいと思います。

2/πに収束するのだと不便なので正規化してこのような関数を用意しましょう。

適当に動かした分(startY-rawY)に応じた量(α)でボタンが動くようにします。

これでlimitを超えない範囲でいい感じに動いてくれるようになります。実際に動かしながらvariationの値などを変えるとよいです。

離したら戻るようにする

ここまでだとただ単に動きの悪いDraggableButtonです。先のSpringAnimationを付けてみましょう。

次の関数を用意します。

まずSpringAnimationの引数で対象と終点を設定します。終点の種類には以下のようなものがあります。

DynamicAnimation. 説明
ALPHA 透明度を設定
TRANSLATION_(X|Y|Z) 現在座標からの移動距離(px)を指定
(X|Y|Z) 画面の左上からの絶対位置を指定
SCALE_(X|Y) 拡大縮小を設定

次にAnimationの物理的な特性である剛性(spring.sfiffness)と減衰率(spring.dampingRatio)を指定します。

  • 剛性を上げる -> 勢いが強くなる
  • 減衰率を下げる -> 終点に達するまでの揺れが多くなる

公式が詳しいので目を通すのがよいです。

指を離したときに関数を呼ぶようにすれば完成です!

MotionEvent.ACTION_UP -> {
    goBack2MyRoots(startRawY - touchGapY)
}

ついでにX座標にも動かせるようにしたのが以下のコードです。冒頭のGIFもこれですね。

参考

android.jlelse.eu

出来上がったコードはGitHubにあげています。

github.com

Flutter Web で最速Modern Web App公開

Flutter Webが本体に帰ってきた

Flutterは今までAndroid, iOSに両対応したMobile Appが作れるフレームワークとしていましたが、Flutter1.9から別に開発されてきていた「Flutter for Web(Hummingbird)」が統合され、晴れてFlutter Webとなりました!

これでAndroid, iOSと同列でWebアプリにも同一コードで対応できるようになったわけです。(まだテクニカルプレビューなので注意)

早速やっていく

まずはflutter 1.9以上が必要なのでアップデートしましょう。

(私の環境が変なのはあしからず)

>flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel master, v1.12.5-pre.21, on Microsoft Windows [Version 10.0.18362.476], locale ja-JP)

[√] Android toolchain - develop for Android devices (Android SDK version 29.0.1)
[√] Chrome - develop for the web
[√] Android Studio (version 3.5)
[√] VS Code (version 1.40.2)
[√] Connected device (2 available)

• No issues found!

そうしたら File > New > New Flutter Project... から新しいプロジェクトを作ります。

f:id:Nageler:20191129172120p:plain
new flutter project

BMIを計算するアプリを作ります

f:id:Nageler:20191129172636p:plain:h500

Projectが作成されると、ディレクトリにWebが追加されているのが分かると思います。

f:id:Nageler:20191129172852p:plain:w300

適当にコードを変更して、Chromeからデバッグしてみましょう。

コマンドラインから、

>flutter run -d chrome

でできます。

f:id:Nageler:20191129175134p:plain:h500

できました。ここまでWeb向けのコードは一切書く/書き換えていません。HotReloadも使えます。

公開してみる

Flutter WebはコードをJSにトランスパイルしてWebページの作成を実現しています。要は静的ページです。と、いうことは、

GitHub Pagesで無料で公開できる!

というわけです。やっていきましょう。

>flutter build web

でトランスパイルします。すると build > web にファイルが生成されます。

f:id:Nageler:20191129175807p:plain:h500

そうしたらGitHubで適当にRepositoryを作り、Webの階層からpushしましょう(rebuildしたら消えるので別に移動した方が良い)。

f:id:Nageler:20191129180241p:plain:h500

Pushが完了したら、GitHubのページに行き、Settings > GitHub Pages でmasterブランチを選びます。

f:id:Nageler:20191129180500p:plain:h500

1分も待てば公開されます。当然独自ドメインも使えますよ。

https://organic-nailer.github.io/calc_bmi_web_app/

今回作ったのがこれです。記事書きながらでも1時間かかりませんでした。

...わかりますかこの速度感!!これはやばいですよ(アプリ開発者の感想)

まとめ

いかがでしたか?

まだテクニカルプレビューですが触る限り大きなバグは見られず、試用程度なら十分に使えると思いました。Stableになるのが非常に楽しみです。

ちなみにFlutter Webでいくつかアプリを作っているので、よければ遊んでみてください

この2つは1日で作成

動物将棋 https://doubutsu.fastriver.dev/#/

ルーレット https://roulette.fastriver.dev/#/

Android版から移植

クラタン https://clatan.fastriver.dev/#/

今回作ったアプリのコードはGitHubに転がしておきます。参考にどうぞ

github.com

Huaweiのスマホのテーマを自作する

Huaweiこそ至高

EMUIの魅力の一つとして、テーマ(見た目)を自由に変えられるということがあります。Huaweiのテーマストアなり、Playストアなりでテーマを手に入れて適用することで、通知バーやロック画面、ランチャーなどを変更することができます。

悩み

割と有料のテーマが多い。

自分にピッタリ合う物が見つからない。

適用すると文字が読めなくなりがち。

ほならね?

ストアで提供されてるものであればSDKが配布されてるはずなので、自分で作れるだろ、と。やっていきましょう。

Toolのインストール

developer.huawei.com

ここから Huawei Theme Tool Suite のzipをダウンロード、インストール。私はWindows Defenderに文句を言われました。

f:id:Nageler:20190818011400j:plain

hwtToolがそれです。

f:id:Nageler:20190818011523p:plain

作成

左の Themes->New で新規作成できます。もしくは.hwtファイルをimportして編集することもできます。

f:id:Nageler:20190818011849p:plain

色々と編集ができる。

ロック画面は結構自由度高めに編集できるよう(触ってない)で、他は色を指定したり画像を指定することでテーマを作れます。手元にHuawei端末があればPCにつないでその場で動かすこともできます。GUIで簡単に作れて素晴らしいですね。

 

...と思いきや

実際には一部分しか作れない

このテーマ、全部一括で変更できそうに思えますが、実はアプリ単位でテーマを設定してます。なので最大でも左に表示されているアプリ(launcher, contacts, messaging)+システムUI(通知バーなど)しか変更できません。私としては設定アプリのテーマを弄りたかったのですができませんでした。

まだ手はある

実はhwtToolで新規作成・importしたテーマは /hwtTool/res 下に配置されています(ex: C:\Program Files (x86)\hwtTool\res)。そこに色を指定したxmlや画像が入っているので、直接弄ることでもテーマを作ることができるのです。というよりもそもそも.hwt自体がただのzip圧縮なのでTool Suiteなんぞ無くともテーマは作れるんですね。

問題

ただ製作するにあたって一つだけ問題があります。ドキュメントが見つからない。

日本語はおろか英語にも多分存在しません(中国語ならある?)。Huawei DeveloperもhwtToolの使い方しか載せていませんでした。

fqdeboer.net

www.xda-developers.com

頑張っている人たちはいるようですが私は解読する気力がないです...

私は諦めました

通知バーのデザイン変えたい!くらいなら好きな色にできるので有用だと思います。

エセダークモード

誰かドキュメントの作成...ないしは発掘お願いします!(他力本願)