会社のPCでは出るけど、家のPCでは出ないのなんでって思ってたけど、設定してなかったみたい。
02:24 Frameworks Detected Android framework is detected. Configure
Event Logにこういうのが出ているので、Configureを押したら設定できる。
このあたりも確認した。
会社のPCでは出るけど、家のPCでは出ないのなんでって思ってたけど、設定してなかったみたい。
02:24 Frameworks Detected Android framework is detected. Configure
Event Logにこういうのが出ているので、Configureを押したら設定できる。
このあたりも確認した。
get_it | Dart Package を使っていたがやめた。
// 登録さえすば GetIt.instance.registerSingleton<Logger>(Logger()); // どのクラスでもLoggerを参照できる class HogeBloc { final Logger logger = GetIt.instance.get<Logger>(); }
便利なんだけど次の問題があると思った。
今回の場合、providerも使っているので、providerで値を受け取って愚直にDIをすると解決する。Loggerを使う場所で全部自分でinjectしていくのは正直めんどいがそれは別の方法で解決するといいかなと思う。[どうやって?] providerも使っていると外から何に依存しているのかがわからないとは思うのだけど、それ以外のメリットが大きいので採用している。
Kotlinではdata classはこのように書ける。
data class User(val name: String, val age: Int)
これは、 ただのクラスではなくて equals() / hashCode() / toString() / copy() が実装されるクラスになる。 なので次のコードはtrueが返る。
User(name: "hisaichi5518", age: 1) == User(name: "hisaichi5518", age: 1) //=> true
Dartでこういうクラスを作ってみて、上記のような比較するとfalseが返ってしまう。
class Hoge { final String name; final Int age; Hoge({this.name, this.age}) }
これは equals が実装されていないからそうなる。つまりKotlinのようなdata classを作るには自前でequalsなどを実装する必要がある。 Dart のリポジトリにもイシューとしてあがっている Add data classes · Issue #314 · dart-lang/language · GitHub
この問題を解決するライブラリは次のようなのがある。
これらのライブラリは, Dartのextensionを使わない方法で実装されている。 Dart 2.6から入ったextensionを使ったらもっとスマートな実装になるのでは?と思ったので調べてみたが、結論からいうと無理だった。
@immutable class Hoge { final String name; final int age; Hoge({this.name, this.age}); } // このextensionを自動生成してimportしてあげれば動くんじゃないかと考えた extension HogeDataClass on Hoge { bool operator ==(Hoge other) { return other is Hoge && name == other.name && age == other.age; } ... }
@immutable
がついたクラスにextensionで ==
や toString()
を実装すればいいと思ったのだけど、以下のような制限があると知った。
Extensions can’t declare members with the same name as a member declared by ‘Object’. Diagnostic messages | Dart
悲しい!
APIが4.0.0で変わるようで、builder を create/update に変えるだけで今の所良かった。直感的になって良い変更だなとおもう。
RFC Renaming the parameter ‘builder’ of providers · Issue #259 · rrousselGit/provider · GitHub
ついでに4.0.0-devにするかと思ってやってみたけど、MultiProvider で StreamProvider を使おうとするとエラーになるのでやめた。たぶんこれの影響 Simplify the integration of custom widgets with MultiProvider · Issue #237 · rrousselGit/provider · GitHub
Flutterの画面遷移について考えたのでメモ。 現状このようにして遷移している。
Navigator.of(context).push(MaterialPageRoute( builder: (_) => HogeScreen(keyword: "..."), ));
このやり方だと settings.name
が設定されていないので、FirebaseAnalyticsにログを送るときになんの画面なのか判別できないためログが送られない
settings.name
を設定するFirebaseAnalyticsObserverを利用して画面遷移のログを送信したい場合、settings.name
がnullだとログが送られない。
FirebaseAnalyticsObserver class - observer library - Dart API
ログを送るためには以下のようにsettings.name
を設定するか、pushNamed を使う.
Navigator.of(context).push(MaterialPageRoute( settings: RouteSettings(name: "HogeScreen"), builder: (_) => HogeScreen(keyword: "..."), ))
pushNamed は以下のように使い、画面遷移のコードを何度も書く必要がないというメリットもある。 pushNamed method - Navigator class - widgets library - Dart API
Navigator.of(context).pushNamed("HogeScreen", arguments: "...")
しかし、pushNamedには以下のような問題もあると思った。
settings.arguments
経由で値を渡すことになるので、型がObjectになり利用するときにcastする必要がありうっかりミスが起きやすい人間のうっかりミスが起きやすく、コンパイルエラーではなく実行時エラーになるため最悪気づかないことがある。
pushNamedは画面遷移のコードを何度も書く必要がないという点では便利だけど、うっかりミスも起きやすいと感じた。 それを防ぐためにはこのようにRouteを生成するメソッドを書いてあげればよいのではと考えた。
extension HogeScreenRoutes on HogeScreen { static Route<dynamic> route({@required this.keyword})) { return MaterialPageRoute( settings: RouteSettings(name: "HogeScreen"), builder: (_) => HogeScreen(keyword: "..."), ) } }
Navigator.of(context).push(HogeScreen.route());
こうすればうっかりタイポなどをしても実行したタイミングでエラーになるのではなくコンパイルのタイミングでわかるので、最高。 ただし、これにもうっかりミスが起きる場所がある。HogeScreenに渡す値が増えたときだ。
class HogeScreen { final String keyword; final String fuga; // 増えた HogeScreen({Key key, @required this.keyword, @reuqired this.fuga}); } extension HogeScreenRoutes on HogeScreen { static Route<dynamic> route({@required String keyword})) { return MaterialPageRoute( settings: RouteSettings(name: "HogeScreen"), builder: (_) => HogeScreen(keyword: keyword), // fugaが渡されていない ) } }
@required
をしていてもコンパイルエラーにならない。lintを動かせばエラーにすることはできる。
複数箇所変更するのが嫌ならコンストラクタからrouteメソッドを自動生成すればいいかなと思ったけどそこまでやらなくていいかなと思ってここで考えるのをやめた。
もっといい方法があるなら知りたい。
追記: こうやれば一応一箇所の変更ですむねって話を同僚としたけど、やりすぎ感あるのでやめておいた。
class HogeScreenArguments { final String keyword; final String fuga; HogeScreenArguments({Key key, @required this.keyword, @required this.fuga}); } class HogeScreen { final HogeScreenArguments arguments; HogeScreen({Key key, @required this.arguments}); } extension HogeScreenRoutes on HogeScreen { static Route<dynamic> route({@required HogeScreenArguments arguments})) { return MaterialPageRoute( settings: RouteSettings(name: "HogeScreen"), builder: (_) => HogeScreen(arguments: arguments), ) } }
最近FlutterのWebViewを触るようなことをやっている。 WebViewのプラグインにはいくつか実装があるが、大きく分けるとするとPlatformViewを使わないか使うかによって分けれる。
PlatformViewを使わないとネイティブのViewをFlutterWidgetの上に被せて表示する実装になるので、FlutterのWidget(SnackBarなど)をWebViewの上にかぶせるなどができない。また大きさの変更もContainerなどのWidgetを利用して変更できない。launchするときにrectを指定すれば大きさの変更はできる。さらに1画面に1個のWebViewしか利用できない。このように制限はあるが、PlatformViewを経由しないため、それなりに動く。(キーボードが出てきてもViewの大きさが変わらない問題(仕様?)はある)
PlatformViewを使うとFlutterのWidgetとして扱えるので、WebViewの上にSnackBarを出したりWebViewの大きさをContainerに囲んで変更したり複数のWebViewを表示したり制限なく使える。しかし、後述するがAndroidのキーボードまわりでバグにぶちあたる。
今回の要件的には上記のような制限を受けたくないので、PlatformViewを利用する選択をした。 PlatformViewを利用するプラグインは探せばたぶん無限にあるけど、この2択っぽい。
webview_flutterに関しては、公式のプラグインで安心感がある。利用者も多く大体の問題はイシューになっている。ただあまり実装は進んでおらず、onPageStartedなどのコールバックもほとんど実装されていないようだった。(プルリクはあるが… #1389, #1788)
flutter_inappbrowser のほうはCookieManagerやコールバックもある。ただ、リリースはされてないが2.0.0でAPIが大きく変わりそう。また、大きな実装だなという印象で、InAppWebView.javaの実装を見るとJSがめっちゃ書かれていて、それって我々に必要なのか?みたいな気持ちになる。
今回は、公式のwebview_flutterで確認することにする。
webview_flutterを利用する選択をしたはいいが、触っていたところAndroidのキーボードまわりで結構バグっていることに気づいた。全部イシューになっているけど、WebView起因というよりはPlatformViewが原因なのが多い気がしていて、シュッとは直せなさそうだと思った。 自分が遭遇しただけでも以下がある。
TextFieldをタップしてキーボードを表示したあとTextField以外をタップしてもキーボードが出っぱなし。
TextFieldをタップしてもコピーなどの項目が表示されない。 Flutter 1.9以降で動かないようになったらしく、なおしてるところとのこと。 https://github.com/flutter/flutter/issues/24585#issuecomment-513950478
TextFieldだけではなくウェブページのテキストをコピペしようと長押ししても動かない。 そもそもLongTapが無効化されている。 https://github.com/flutter/plugins/blob/f31d16a6ca0c4bd6849cff925a00b6823973696b/packages/webview_flutter/lib/src/webview_android.dart#L31
日本語を打つと強制的に確定されてしまい、サジェストが出ずキーボードが英字に変わってしまう。 Chromiumが原因かも?と言っている。ネイティブアプリでWebViewを使ってみてもならないので違うような気もするけどよくわからず。
サイズ変更が原因らしいので、サイズを変更しなければ起きない。
FlutterがつくるAndroidアプリは、キーボードを出すとViewの大きさを変える設定になっているので、それをやめればよい。AndroidManifest.xmlのandroid:windowSoftInputMode="adjustSize"
をandroid:windowSoftInputMode="adjustPan"
に変える。
ただ、この設定をするとFlutterで作った画面すべてに影響し(つまりWebViewを利用していない画面でキーボードが出てもViewのサイズは変更されない)、Viewの大きさが変わらないとキーボードを出した状態で一番下までスクロールできなくなるため、あまり現実的な手段ではないと思う。
この問題は、flutter_inappbrowserだと実装が違いそうなので、動かして確認したところそもそも文字がなにも入力できなかったので確認できなかった………。
Android Qでキーボードが出なかった。beta channelだとなおっているのを確認した。
#38375をなおした影響でAndroid Qでログが出まくる。 Q未満は出ないので、Q未満でデバッグすることにした。
動かないか確認できていないけど、ログが出てる。
W/cr_AwAutofillManager( 2953): WebView autofill is disabled because WebView isn't created with activity context.
ただPlatformViewは、VirtualDisplayを使うことで実現していてそのためには android.app.Presentation
を使う必要があるっぽくActivityのContextを利用するのは無理なのではと自己完結した。
FlutterでWebViewを使いたいが、Androidのキーボードまわりで困っている。数カ月後にはなおってるかもしれないし、なおってないかもしれない。 せっかくFlutterを利用するのであれば、ネイティブでAndroid/iOSを書きたくないという思いがあるが、ネイティブで書くのがよいという決断をするかもしれない。
ちなみにiOSでは結構まともに動いていて「AndroidとFlutterは同じ会社が作ってるのでは…?」と思うなどした(大変そう)