tag:crieit.net,2005:https://crieit.net/tags/Flutter/feed 「Flutter」の記事 - Crieit Crieitでタグ「Flutter」に投稿された最近の記事 2022-10-28T11:44:15+09:00 https://crieit.net/tags/Flutter/feed tag:crieit.net,2005:PublicArticle/18309 2022-10-28T11:44:15+09:00 2022-10-28T11:44:15+09:00 https://crieit.net/posts/Flutter-card-swiper Flutterのcard_swiperで独自のアイコンを使うにはどうしたらいいか <p>ページ移動のボタンアイコンはSwiperControlというものをSwiperに指定している。これにはIconDataしか設定できない。</p> <p>ただこのSwiperControlは勝手に作れるので元のソースをコピーして自分で作って単なる画像を表示できるようにすればいいだけ。</p> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/TheAnkurPanchani/card_swiper/blob/master/lib/src/swiper_control.dart">https://github.com/TheAnkurPanchani/card_swiper/blob/master/lib/src/swiper_control.dart</a></p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/18236 2022-07-13T09:22:27+09:00 2022-07-13T09:22:27+09:00 https://crieit.net/posts/flutter-launcher-icons-minSdk flutter_launcher_iconsでminSdkが見つからないエラーが出る <p>Flutterでflutter_launcher_iconsを使ってアイコンを生成しようとすると、下記のようなエラーが出た。</p> <pre><code class="plain">Cannot not find minSdk from android/app/build.gradle or android/local.propertiesSpecify minSdk in either android/app/build.gradle or android/local.properties </code></pre> <p>最近のFlutterプロジェクトはbuild.gradleに変数で値が仕込まれているためだと思われる。</p> <pre><code class="plain"> minSdkVersion flutter.minSdkVersion </code></pre> <p>なのでメッセージも書かれている通り android/local.properties に値を書き込んでおく。</p> <pre><code class="plain">flutter.minSdkVersion=21 </code></pre> <p>参考<br /> <a target="_blank" rel="nofollow noopener" href="https://github.com/fluttercommunity/flutter_launcher_icons/issues/371">https://github.com/fluttercommunity/flutter_launcher_icons/issues/371</a></p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/18200 2022-05-29T10:37:53+09:00 2022-05-29T10:37:53+09:00 https://crieit.net/posts/Targeting-S-version-31-and-above-requires-that-one-of-FLAG-IMMUTABLE-or-FLAG-MUTABLE-be-specified-when-creating-a-PendingIntent Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntentエラー <p>FlutterでtargetSdkVersionを31にしたアプリを実機で試してみたらいきなり落ちてしまった。ログを見てみると下記のようなエラー。</p> <pre><code class="plain">Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent </code></pre> <p>調べてみると色々記事が見つかり、どうもFirebaseやAdMobのライブラリが関係していてみんな困っていた。とはいえそれらの情報は2021年の情報だったので、2022/5現在はさすがに改善しているのではないかと思いそのあたりのライブラリを最新にしてみたら起動するようになった。</p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/18197 2022-05-24T09:09:10+09:00 2022-05-24T09:12:51+09:00 https://crieit.net/posts/Flutter-Hive-type-UnspecifiedInvalidResult-is-not-a-subtype-of-type-LibraryElementResult-in-type-cast FlutterのHiveで type 'UnspecifiedInvalidResult' is not a subtype of type 'LibraryElementResult' in type cast エラー <p>FlutterのHiveでbuild_runnerしたところ下記のようなエラーが出た。</p> <pre><code class="plain">type 'UnspecifiedInvalidResult' is not a subtype of type 'LibraryElementResult' in type cast [SEVERE] hive_generator:hive_generator on lib/models/message.dart: </code></pre> <p>調べてみたところいくつかの箇所で一旦pubspec.lockを削除したらいける、とのことで試してみたらなおった。依存関係が更新されてしまうのでちょっと怖いが。</p> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/google/json_serializable.dart/issues/924">https://github.com/google/json_serializable.dart/issues/924</a></p> <p>追記)やっぱ仕様変更したライブラリがあって困ったがそれはまた別の話…</p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/18166 2022-04-11T23:43:19+09:00 2022-04-15T16:49:25+09:00 https://crieit.net/posts/Flutter-62543e877f391 Flutter用問い合わせフォームウィジェット&サービスを作った <p>Flutter用の問い合わせフォームのウィジェットを簡単に設置できるパッケージ及び連携サービス Contact Nite を作りました。</p> <p><a href="https://crieit.now.sh/upload_images/8c23e18a7a344fca6bd592674e642b446252e63476ec0.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/8c23e18a7a344fca6bd592674e642b446252e63476ec0.png?mw=700" alt="card.png" /></a></p> <p>上記画像のように簡単なコードを設置するだけで、サービス上で設定した項目通りの問い合わせフォームウィジェットを表示することができます。また、<br /> 送信された問い合わせはサービス上で確認することができるようになっています。</p> <p><a href="https://crieit.now.sh/upload_images/6e2af66c610d88bc766649f72032893a6252ee397f453.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/6e2af66c610d88bc766649f72032893a6252ee397f453.png?mw=700" alt="image.png" /></a></p> <p>サービス<br /> <a target="_blank" rel="nofollow noopener" href="https://contact-nite.com/ja">https://contact-nite.com/ja</a><br /> パッケージ<br /> <a target="_blank" rel="nofollow noopener" href="https://pub.dev/packages/contact_form">https://pub.dev/packages/contact_form</a></p> <p>無料プランもありますので気になる方は是非試してみてください。僕も自分のアプリに入れて試してみています。</p> <p>問い合わせが来るとメール及びSlack通知を行うことができます。そのうちサービス内で直接問い合わせ下ユーザーとやり取りができるようにしてみようと思っています。</p> <h2 id="技術的な話"><a href="#%E6%8A%80%E8%A1%93%E7%9A%84%E3%81%AA%E8%A9%B1">技術的な話</a></h2> <p>パッケージ自体は純粋なFlutterパッケージです。サービス側は今回 Next.js, PlanetScale, Cloud Run を利用しました。</p> <h3 id="PlanetScale"><a href="#PlanetScale">PlanetScale</a></h3> <p>丁度開発している途中で知った、MySQLサービスです。最近記事も書いたので良ければ見てみてください。</p> <p><a href="https://crieit.net/posts/MySQL-PlanetScale-Next-js-Prisma">サーバーレスMySQLのPlanetScaleをNext.js+Prismaで使ってみた</a><br /> <a href="https://crieit.net/posts/Prisma-PlanetScale">PrismaでPlanetScaleを使う時のエラーあれこれ</a></p> <p>ここ最近はずっとFirestoreを使ってサービスを作っていました。安くて容量が大きいとなるとこれくらいしかなかったためです。ところがPlanetScaleを知りそちらに乗り換えてみることにしました。とにかく容量が大きいというのが決め手です。本当はMySQLの方が好きなので僕にとっては嬉しいサービスです。</p> <p>まだリリースしたサービスで利用した経験が無いのでどうなるかわかりませんが、これから見ていこうと思っています。問題なければこれからの僕の定番になりそうです。</p> <h3 id="Next.js"><a href="#Next.js">Next.js</a></h3> <p>Next.jsで作っています。サーバー側もNext.jsのAPI Routesです。もうとにかく楽ちんですね、サーバーサイドとフロント側の連携とか、何も考えなくて良いというのは。仕事だと色々問題が出てくるのかもしれませんがとにかく個人で開発するものだと今はこれが楽すぎて他を考えられません。</p> <p>特に日本専用サービスとする必要もないためInternationalized Routingを使って日本語と英語の対応を行っています。</p> <h3 id="デプロイ"><a href="#%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4">デプロイ</a></h3> <p>Cloud Runを利用しています。最近こればかり使っているので元々持っている資産的に楽になってきたため他を考えられません。といいつつPlanetScaleを使っているのでDBまわりで悩まなくてもいいしHerokuとかで良かったかもしれません。</p> <p>push時に連動もできるのですが、自動テストはGitHub Actionsでやったほうが超簡単なため、push→GitHub Actionsでテスト→Cloud Buildでビルド&Runにデプロイという流れをとっています。DBのマイグレーションもCloud Buildでビルドしたイメージを利用して自動化しています。</p> <p>テストはJestによるシンプルなAPIのテストと、Cypressを使ったE2Eテストを行っています。Cypressはあまり使ってないですがCypress Dashboardと連携して動画も見れたりするの面白いですね。</p> <h3 id="メール"><a href="#%E3%83%A1%E3%83%BC%E3%83%AB">メール</a></h3> <p>SendGridです。Dynamic Templatesむっちゃ楽ちんですね。SendGrid上でメールの本文を調整して簡単に送信できます。ごちゃごちゃプログラムやDB上にテンプレートを定義しなくていいので良いです。</p> <p>あとはメール受信のhookを利用して、メールも見ずにサービス上だけでやり取りできるようにもしたいなと思っています。なんかできるっぽいので。</p> <h2 id="Flutter側"><a href="#Flutter%E5%81%B4">Flutter側</a></h2> <h3 id="多言語化"><a href="#%E5%A4%9A%E8%A8%80%E8%AA%9E%E5%8C%96">多言語化</a></h3> <p>ハマりどころとして、多言語化が結構複雑でした。パッケージを作成する場合一緒に作成されたサンプルプロジェクトと連携して動作させるのですが、そのプロジェクト内だとうまくいくのに、別途他のアプリに組み込んでみたらちゃんと言語が反映されないという問題が発生したりして手こずりました。</p> <p>ちなみにFlutterはVS Code拡張Flutter Intlを使うことで簡単にローカライズできるのですが、それも使えたようです。</p> <h3 id="Freezed"><a href="#Freezed">Freezed</a></h3> <p>Freezed普通に使えたので使っています。</p> <h3 id="テスト"><a href="#%E3%83%86%E3%82%B9%E3%83%88">テスト</a></h3> <p>パッケージは公開ということもありGitHub Actionsでのテストが無料で放題なので、せっかくなので自動テストをいれてあります。</p> <h2 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h2> <p>小ネタですがサービスサイトの問い合わせフォームもWebではありますがContact Niteに送信して実現しています。</p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/17924 2022-01-07T16:19:06+09:00 2022-01-11T17:18:59+09:00 https://crieit.net/posts/Flutter-61d7e96a4daa2 Flutterベースのモバイル向けタイムラインの作成 <h1><a target="_blank" rel="nofollow noopener" href="https://quire.io">Quire</a>タイムライン(モバイル向け)の構造を初公開</h1> <p>2018年に初めてFlutterベースのアプリを作ったときは、とても楽しく興奮しました。それから3年経ち、<a target="_blank" rel="nofollow noopener" href="https://quire.io">Quireアプリ</a>もかなり充実して、従来のモバイル向けプロジェクト管理アプリの域を超えるまでになりました。Quireモバイルアプリの現行バージョンは、階層表示、ボード表示だけでなく、タイムライン表示にも対応しています。</p> <p>モバイルアプリ向けのタイムライン表示の作成を決めたときは、簡単にできるとは思いませんでした。当時は類似の既成コンポーネントもなかったためですが、驚いたのは、インターネットでタイムライン表示の構造についての情報も見当たらなかったことです。そこで、いちかばちか、自分たちで作ってみることにしました。</p> <h5><a target="_blank" rel="nofollow noopener" href="https://quire.io">Quire</a>モバイルアプリ用のタイムラインでは、以下を計画していました。</h5> <ol> <li>横方向への無限日付スクロール</li> <li>レンダリングオンデマンド(ROD)。ビューポートにあるときのみ実行されるWidgetのState</li> <li>任意の位置に素早く配置</li> <li>操作がかんたんで使いやすいインターフェースと、スムーズなユーザーエクスペリエンス</li> </ol> <p>数週間で初期開発が完了し、以下のような構造になりました。</p> <p><img src="https://storage.googleapis.com/zenn-user-upload/52c3e5779da7-20220105.png" alt="" /></p> <ol> <li>タイムラインペインのコアベース(週、週末のセクションなど)</li> <li>タスクリスト(階層構造のタスクリスト)</li> <li>タイムラインペインのビューポートベース双方向リスト</li> <li>1ペインのみのとき、2ペインにまたがるときの両方に対応した期間の横棒</li> <li>期間の横棒上の固定ラベル</li> </ol> <p>上図のように、タスクごとにタイムラインペインが割り当てられ、すべてのタイムラインのスクロール位置は互いに同期されます。</p> <h2 id="インデックスベースのスクロールビュー"><a href="#%E3%82%A4%E3%83%B3%E3%83%87%E3%83%83%E3%82%AF%E3%82%B9%E3%83%99%E3%83%BC%E3%82%B9%E3%81%AE%E3%82%B9%E3%82%AF%E3%83%AD%E3%83%BC%E3%83%AB%E3%83%93%E3%83%A5%E3%83%BC">インデックスベースのスクロールビュー</a></h2> <p>Google Flutter Widgetに似たインデックスベースのスクロールビューを作るために、Centerに引数のあるカスタムスクロールビューを使用します。実装すると、任意の位置まで素早くスクロールできるようになります。スクロール中のどの時点でも、各位置とインデックスを表示できます。</p> <p>イメージ的には、少しスクロールした時点で、新しいCenterの引数でタイムラインをリロードしてビューポートの外に移し、またスクロールするとビューポート内に配置される、という感じです。</p> <h2 id="タイムラインペイン"><a href="#%E3%82%BF%E3%82%A4%E3%83%A0%E3%83%A9%E3%82%A4%E3%83%B3%E3%83%9A%E3%82%A4%E3%83%B3">タイムラインペイン</a></h2> <p>タイムラインをスムーズに使えるように、インデックスベースのスクロール表示と似た発想で、横方向にスクロールできるカスタム「無限双方向スクロールビュー」を実現しました。実装すると、タイムラインをなめらかにスクロールできます。</p> <p>無限双方向スクロールビューには、Flutterの強力なViewportの考え方を活用しました。そして、Backwardリストのインデックスを-1から始まる負の数に変更しました。Index 0に当たる日付が分かるようにフラグも設定して、任意の日付まで素早くスクロールできるようにしました。</p> <pre><code class="js">Widget forwardList = SliverList( delegate: SliverChildBuilderDelegate((BuildContext context, int index) { return cellBuilder(context, _getIndex(forward: true, index: index)); }) ); Widget backwardList = SliverList( delegate: SliverChildBuilderDelegate((BuildContext context, int index) { return cellBuilder(context, _getIndex(forward: false, index: index)); }), ); Scrollable( viewportBuilder: (BuildContext context, ViewportOffset offset) { return Viewport( offset: offset, center: forwardListKey, slivers: [ backwardList, forwardList, ] ); }, ) </code></pre> <h2 id="2ペインにまたがるときの問題と解決方法"><a href="#2%E3%83%9A%E3%82%A4%E3%83%B3%E3%81%AB%E3%81%BE%E3%81%9F%E3%81%8C%E3%82%8B%E3%81%A8%E3%81%8D%E3%81%AE%E5%95%8F%E9%A1%8C%E3%81%A8%E8%A7%A3%E6%B1%BA%E6%96%B9%E6%B3%95">2ペインにまたがるときの問題と解決方法</a></h2> <p>ビューポートには無限のリスト2つがスクロールされるため、期間の横棒が2つのリストにまたがることもあります。そこで、どちらのリストにも完全に同じ期間の横棒を作成し、ぴったり重ねて、リストがビューポートの外に移動してもリスト内のアンカーが壊れないようにしました。</p> <p><img src="https://storage.googleapis.com/zenn-user-upload/a56ed0583195-20220105.gif" alt="" /></p> <h2 id="固定ラベルで解決"><a href="#%E5%9B%BA%E5%AE%9A%E3%83%A9%E3%83%99%E3%83%AB%E3%81%A7%E8%A7%A3%E6%B1%BA">固定ラベルで解決</a></h2> <p>モバイル機器の小さい画面ではプロジェクトのどこを見ているかが分かりにくく、使っていると混乱してきます。この問題は、できるだけ多くの情報を提供することで軽減できます。そこで便利なのが固定ラベルです。</p> <p>最初はとにかくシンプルにするため、スクロールビューのスクロール通知に従って、位置を取得してから配置されたラベルに設定していました。固定ラベルを各タイムラインペインの開始位置に表示するには、期間の横棒の現在位置の計算がベースとなります。</p> <p>しかし、新しく配置されたラベルは次のフレームまでしか更新されず、スクロールビューと同じ時間枠で同期されないため、ずれて見えてしまいました。</p> <p>幸いFlutterコミュニティーが、レンダリングレイヤー固定ヘッダーというすばらしい解決方法を教えてくれました。つまりレイアウトのタイミングによる方法です。レンダリングレイヤーにすべてのWidgetをサイズとともに入れるだけでなく、そのピクセルすべてを計算する必要があります。最後にlocalToGlobal関数を、スクロール位置、および2ペインにまたがるときのペイン切り替えに基づいた演算操作と置き換えて、パフォーマンスを向上させました。</p> <h2 id="始まりはこれから"><a href="#%E5%A7%8B%E3%81%BE%E3%82%8A%E3%81%AF%E3%81%93%E3%82%8C%E3%81%8B%E3%82%89">始まりはこれから</a></h2> <p>今は大変な時代ですが、だからこそテクノロジーの分野で貢献したいと考えています。タイムライン表示の作成でまず考えたのは、どうやってFlutterの強力なフレームワークを活用して、ビューコンポーネントを一から作り直すことなく、軽く安定したゴージャスなUIを実現するか、ということでした。</p> <p>各日付単位はインデックスとして、FlutterのSliverに組み込まれています。ほとんどのものはWidgetレイヤーの高レベルの開発概念に留まり、固定ビューのときのみレンダリングレベルに移動します。</p> <p><a target="_blank" rel="nofollow noopener" href="https://quire.io">Quireアプリ</a>をインストールして、Flutterベースのモバイルアプリを使ってみませんか。Quireタイムラインについて気になることは、コメントを投稿するか、<a target="_blank" rel="nofollow noopener" href="https://twitter.com/quire_io">@quire_io</a>でツイートしてお知らせください!</p> <p>※転載許可済み: https://zenn.dev/quireteam/articles/2b2c44c3e49fac</p> uniyeh tag:crieit.net,2005:PublicArticle/17894 2021-12-27T16:01:13+09:00 2021-12-27T16:01:13+09:00 https://crieit.net/posts/Flutter-61c964b9876a1 Flutterでラジオボタンのバリデーションを行う <p>Flutterのラジオボタンにはvalidatorがない。そもそもラジオボタングループのようなウィジェットもない。</p> <p>ではラジオボタンもFormの <code>_formKey.currentState!.validate()</code> でバリデーションさせたい時にはどうしたらよいか。</p> <p>Flutter FormBuilderというパッケージにそういうのがあるのでそれを使うと簡単。</p> <p><a target="_blank" rel="nofollow noopener" href="https://pub.dev/packages/flutter_form_builder">flutter_form_builder | Flutter Package</a></p> <pre><code class="dart">FormBuilderRadioGroup<String>( name: 'gender', decoration: const InputDecoration( contentPadding: EdgeInsets.zero, border: InputBorder.none, ), onChanged: onGenderChanged, validator: (value) { if (value == null || value.isEmpty) { return '性別を選択してください。'; } return null; }, options: const [ FormBuilderFieldOption(value: 'man', child: Text('男性')), FormBuilderFieldOption(value: 'woman', child: Text('女性')), ], ) </code></pre> <p>デフォルトだとラッパーの枠がつくのでdecorationのborderで消している。</p> <p>あとはデフォルト選択で良ければいちいちバリデーションを入れずにそれでも良いような気もする。</p> <p>ちなみにどうしてもカスタマイズが必要な場合は下記のように自作も可能。</p> <p><a href="https://crieit.net/posts/Flutter-61b4ba4f439a3">Flutterでバリデーションに対応したカスタムなフィールドを作成する</a></p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/17845 2021-12-11T23:48:47+09:00 2021-12-11T23:48:47+09:00 https://crieit.net/posts/Flutter-61b4ba4f439a3 Flutterでバリデーションに対応したカスタムなフィールドを作成する <p>Flutterにはフォームのサブミットボタンを押した時に各入力項目のバリデーションを一括で行ってくれる機能があります。公式でも説明のページがあります。</p> <p><a target="_blank" rel="nofollow noopener" href="https://docs.flutter.dev/cookbook/forms/validation">Build a form with validation | Flutter</a></p> <h2 id="基本的なやり方"><a href="#%E5%9F%BA%E6%9C%AC%E7%9A%84%E3%81%AA%E3%82%84%E3%82%8A%E6%96%B9">基本的なやり方</a></h2> <p>具体的には例えば下記のようにTextFormFieldにvalidatorプロパティを設定します。</p> <pre><code class="dart">TextFormField( validator: (value) { if (value == null || value.isEmpty) { return 'Please enter some text'; } return null; }, ), </code></pre> <p>そしてフォームには下記のようなキーを割り当てます。</p> <pre><code class="dart"> final _formKey = GlobalKey<FormState>(); @override Widget build(BuildContext context) { return Form( key: _formKey, </code></pre> <p>あとは決定ボタンが押されたときなどに下記のようにバリデーションメソッドを呼び出します。</p> <pre><code class="dart"> if (_formKey.currentState!.validate()) { </code></pre> <h2 id="対応していない場合がある"><a href="#%E5%AF%BE%E5%BF%9C%E3%81%97%E3%81%A6%E3%81%84%E3%81%AA%E3%81%84%E5%A0%B4%E5%90%88%E3%81%8C%E3%81%82%E3%82%8B">対応していない場合がある</a></h2> <p>しかしチェックボックスなど、validatorに対応していない場合があります。その時だけ独自にvalidateメソッド以外でチェックをするのはちょっと面倒です。</p> <p>このような場合に、どのようなものでもバリデーションに対応させる方法があります。もちろんTextFormFieldのvalidatorのようにチェック方法は自分で実装する必要がありますが、たとえばチェックボックスのうち一つは必ずチェックが必要とか、お絵かきをさせてある程度の面積は描かないといけないとか、なんでも自由にバリデーションさせるフィールドをカスタムで作成することができます。</p> <h2 id="具体的には?"><a href="#%E5%85%B7%E4%BD%93%E7%9A%84%E3%81%AB%E3%81%AF%EF%BC%9F">具体的には?</a></h2> <p>具体的にはFormFieldを継承したウィジェットを作ることでバリデーションを導入することができます。このFormFieldというのは例で上げたTextFormFieldのようにvalidatorプロパティを持っているためバリデーションさせることができます。</p> <p>ただ、FormField自体がステートを持っており、ちょっと扱い方が特殊になりますので最初はちょっとハマりどころがあったりします。そのあたりも解決する形の解説をしていきます。具体的には下記のようなことをします。</p> <ul> <li>基本的な継承の方法</li> <li>独自ウィジェットの描画方法(現在の値の取得方法)</li> <li>入力した時にバリデーションが働いてしまうのでボタンを押したときだけにしたい場合</li> <li>描画時のbuilderメソッドの引数にBuildContextがないためそれでもcontextを利用したい場合</li> <li>呼び出し元の親ウィジェットに値を渡す方法</li> <li>エラーの際のエラーメッセージ表示方法</li> </ul> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/17791 2021-11-27T23:35:23+09:00 2021-11-28T11:09:31+09:00 https://crieit.net/posts/Flutter-PackageInfo-test FlutterのPackageInfoの処理を含むtestを実行する <p>FlutterのtestでPackageInfoの実行を含むコードのtestを実行するとエラーになる。</p> <pre><code> Null check operator used on a null value package:flutter/src/services/platform_channel.dart 121:86 MethodChannel.binaryMessenger package:flutter/src/services/platform_channel.dart 146:36 MethodChannel._invokeMethod package:flutter/src/services/platform_channel.dart 329:12 MethodChannel.invokeMethod package:flutter/src/services/platform_channel.dart 356:49 MethodChannel.invokeMapMethod package:package_info/package_info.dart 42:26 PackageInfo.fromPlatform </code></pre> <p>恐らくテストなのでネイティブ処理が無いためだろう。MethodChannelでネイティブの処理を呼ぼうとして失敗している。</p> <p>ということで新規パッケージプロジェクトを作った際に元々入っていたMethodChannelのテストを参考にして、PackageInfoの実行をMockで実行する。具体的にはsetMockMethodCallHandlerを実行する。</p> <p>下記を参考にしてる。もしくはPackageInfoのソースコードを見ても分かると思う。(ただこれだけだと他のテストに影響がでるかもとのことのため対処は後述)</p> <p><a target="_blank" rel="nofollow noopener" href="https://stackoverflow.com/questions/54162467/how-to-test-a-method-that-uses-package-info-in-flutter">https://stackoverflow.com/questions/54162467/how-to-test-a-method-that-uses-package-info-in-flutter</a></p> <p>ensureInitializedを入れないと下記のようなエラーも出るのでちゃんと入れる。</p> <pre><code> Null check operator used on a null value package:flutter_test/src/deprecated.dart 95:47 TestMethodChannelExtension.setMockMethodCallHandler </code></pre> <p>ちなみにsetMockMethodCallHandlerを指定しているせいで影響が出て別のテストでエラーが出る可能性があるのかもしれない。新規パッケージプロジェクトではそちらの対処っぽいものも入っていた。上記のソースコードや他の解説記事などでも対処しているものが見つからなかったのでそれも下記に書いておく。</p> <p>全体としては下記のコードのような感じ。</p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/17747 2021-11-11T08:12:52+09:00 2021-11-11T08:13:25+09:00 https://crieit.net/posts/flutter-secure-storage-Cipher-functions-OPENSSL-internal-BAD-DECRYPT flutter_secure_storageでCipher functions:OPENSSL_internal:BAD_DECRYPTエラー <p>Flutterで実機デバッグしていたらいきなりflutter_secure_storageのエラーが出るようになっていた。下記のようなエラー。(2021/11現在)</p> <p>PlatformException(Exception encountered, readAll, javax.crypto.BadPaddingException: error:1e000065:Cipher functions:OPENSSL_internal:BAD_DECRYPT</p> <p>なんだかよくわからないが、エラーが出はじめたタイミングでtargetSdkVersionを30にしていたのでもしかしたら高いバージョンのAndroid環境(Android11以降?)だと発生するのかもしれない。(とはいってもbuild.gradleを戻してもなおらない。なぜかコミットをもとに戻さないと発生しないので謎)</p> <p>issueを調べてみたら現在報告されているところで改善もしていないっぽい。なのでこれからの修正を待つしかなさそう。</p> <h2 id="一時的な対処方法"><a href="#%E4%B8%80%E6%99%82%E7%9A%84%E3%81%AA%E5%AF%BE%E5%87%A6%E6%96%B9%E6%B3%95">一時的な対処方法</a></h2> <p>まずreadAllは使わない。これを使うと絶対エラーになる。でもそれ以外はなんとなく動いているっぽい気がする。</p> <p>ということでreadを使う。しかし、多分データがなにもないとかキーが無いとかでもエラーになる。そのため、readもtry catchで囲んで、エラーが出たらデフォルト値を返すようにして利用する必要がある。</p> <p>とりあえずそれで動いた。</p> <p>issueはなんか同じようなのがいっぱいあるがとりあえずこれ<br /> <a target="_blank" rel="nofollow noopener" href="https://github.com/mogol/flutter_secure_storage/issues/248">https://github.com/mogol/flutter_secure_storage/issues/248</a></p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/17744 2021-11-06T23:12:54+09:00 2021-11-11T08:32:14+09:00 https://crieit.net/posts/Riverpod-v1-Flutter-Hooks Riverpod v1とFlutter Hooksの使い方 <p>どうも昨日か今日(2021/11/6)あたりにRiverpodのバージョン1が正式リリースされていたようです。めでたい!</p> <p><a href="https://crieit.now.sh/upload_images/6e2af66c610d88bc766649f72032893a61868b6915deb.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/6e2af66c610d88bc766649f72032893a61868b6915deb.png?mw=700" alt="image.png" /></a></p> <p>hooks_riverpodも同時に1.0.0になっています。ということで使い方のメモ。といってもExampleに載っています。</p> <h2 id="Widgetの変更"><a href="#Widget%E3%81%AE%E5%A4%89%E6%9B%B4">Widgetの変更</a></h2> <p>元々HookWidgetを継承してウィジェットを作っていましたが、<code>HookConsumerWidget</code> に変更となりました。(Hookを使わない場合はConsumerWidget)</p> <pre><code class="dart">class MyHomePage extends HookConsumerWidget { const MyHomePage({Key? key}) : super(key: key); </code></pre> <h2 id="読み込みの変更"><a href="#%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%81%BF%E3%81%AE%E5%A4%89%E6%9B%B4">読み込みの変更</a></h2> <p>値の読み込み方法も変更となります。</p> <pre><code class="dart"> @override Widget build(BuildContext context, WidgetRef ref) { final records = ref.watch(recordsProvider); </code></pre> <p>buildメソッドにはWidgetRefの引数が付きます。useProviderは無くなったので代わりにこのrefを使うことで値の読み取りを行うことができます。</p> <h2 id="更新の変更"><a href="#%E6%9B%B4%E6%96%B0%E3%81%AE%E5%A4%89%E6%9B%B4">更新の変更</a></h2> <p>値の更新方法も同様に変更となります。今までは <code>context.read</code> を使っていたと思いますが、refを使う <code>ref.read</code> に変更となります。</p> <pre><code class="dart"> ref.read(recordsProvider.notifier).setRecords(RecordRepository.all()); </code></pre> <h2 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h2> <p>あわせて公式サイトもようやくリニューアルされたようなので見直してみると良いでしょう。</p> <p><a target="_blank" rel="nofollow noopener" href="https://riverpod.dev/">https://riverpod.dev/</a></p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/17691 2021-09-27T22:24:22+09:00 2021-09-27T22:24:22+09:00 https://crieit.net/posts/The-method-setMockMessageHandler-isn-t-defined-for-the-class-BasicMessageChannel-dynamic The method 'setMockMessageHandler' isn't defined for the class 'BasicMessageChannel<dynamic>'エラー <p>Flutter2.5.1にしたら下記のようなエラーが出た。</p> <pre><code>/C:/Users/alpha/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/wakelock_platform_interface-0.2.0/lib/messages.dart:100:17: Error: The method 'setMockMessageHandler' isn't defined for the class 'BasicMessageChannel<dynamic>'. </code></pre> <p>よくわからないけどもこの場合はwakelockに依存しているなにかのパッケージを更新すればなおるかもらしい。僕の場合はchewieをアップグレードしたら治った。</p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/17683 2021-09-20T21:21:10+09:00 2021-09-20T21:22:22+09:00 https://crieit.net/posts/fvm-Flutter2-5 fvmでFlutter2.5がインストールの候補に出てこない <p><code>fvm releases</code> しても全然Flutter2.5.1が出てこないためインストールできない…。</p> <p>fmvのバージョンが低すぎると出てこないようです。僕はFlutter1時代のfvm v1を使っていたためだったっぽいです。アップグレードしたら2.5.1が出てくるようになりました。</p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/17630 2021-09-02T23:38:57+09:00 2021-09-02T23:38:57+09:00 https://crieit.net/posts/Flutter-audioplayers-expected-version-is-1-1-15 Flutterのaudioplayersを入れるとexpected version is 1.1.15というエラーがでる <p>Flutterでaudioplayersを入れると <code>expected version is 1.1.15</code> というエラーがたくさん出る。</p> <p>よくわからないが <code>build.gradle</code> の <code>kotlin_version</code> を下記のように上げると良いらしい。</p> <pre><code>ext.kotlin_version = '1.4.32' </code></pre> <p>参考</p> <p><a target="_blank" rel="nofollow noopener" href="https://stackoverflow.com/questions/67832028/execution-failed-for-task-audioplayerscompiledebugkotlin">flutter - Execution failed for task ':audioplayers:compileDebugKotlin' - Stack Overflow</a></p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/17613 2021-08-24T10:10:08+09:00 2021-08-24T10:10:08+09:00 https://crieit.net/posts/Flutter-is-not-a-readable-directory Flutterで「~~ is not a readable directory」エラー <p>Flutterアプリで「~~ is not a readable directory」みたいなエラーが出て全く起動できなくなってしまった。</p> <p>なんか起動中についついpubspec.yamlを編集してVSCodeが自動でpub getしてしまったのでそのせいかもしれない。</p> <p>gradlew cleanしてもFlutterプロジェクトをcleanしてもPCを再起動しても該当のパスを削除しても全然ダメ。</p> <p>結局、<code>android/.gradle</code> というフォルダがあるのだがそれを削除したら動くようになった。</p> <p>参考<br /> <a target="_blank" rel="nofollow noopener" href="https://stackoverflow.com/questions/60483656/gradle-error-path-is-not-a-readable-directory-for-an-android-project-saved-in-o">Gradle Error: Path is not a readable directory for an Android Project saved in OneDrive - Stack Overflow</a></p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/17612 2021-08-23T08:55:22+09:00 2021-08-23T08:55:22+09:00 https://crieit.net/posts/Flutter-6122e3ea55105 Flutterでアプリを作る時に使っている僕の定番パッケージ <p>この2年でFlutterアプリを5つリリースしました。最初はよく分からない感じで適当に作っていましたが、やっているうちにだんだんわかってきたのと、あとは諸々の便利なライブラリなども出てきてそれらをキャッチアップして開発するようにしていて、最近なんとなくだんだん自分のアプリの作り方が固まって来ました。せっかくなので一度まとめて紹介したいと思います。</p> <h2 id="おすすめ"><a href="#%E3%81%8A%E3%81%99%E3%81%99%E3%82%81">おすすめ</a></h2> <h3 id="Flutter Hooks"><a href="#Flutter+Hooks">Flutter Hooks</a></h3> <p><a target="_blank" rel="nofollow noopener" href="https://pub.dev/packages/flutter_hooks">https://pub.dev/packages/flutter_hooks</a></p> <p>FlutterでReactのようなHooksを使って書けるパッケージです。StatefulWidgetを使わず、代わりにHookWidgetというものを使います。setStateを使わず、Hooks独自の値の見方、更新の仕方で処理を書いていきます。</p> <p>僕もWebメインなので同様にWebでReactとかNext.jsを使っている人には直感的で使いやすいのではないかと思います。Reactとほぼ同様のものが使えます。useState, useEffect, useCallback, useMemoizedなど。</p> <p>他にもFlutterで使うのに必要な~~Controller系もだいたいHooksが用意されています。例えばuseTextEditingControllerなど。</p> <p>例えばsetStateの代わりに使うuseStateは下記のような感じで使えます。</p> <pre><code class="dart"> @override Widget build(BuildContext context) { final isImageDeleted = useState(false); void toggle() { if (isImageDeleted.value) { isImageDeleted.value = false; } } </code></pre> <p>ここまでなら好みで使うかくらいのレベルですが、後述するRiverpodというパッケージでこのflutter_hooksを使って書くことができるため、両方合わせて使うと便利になっています。</p> <h3 id="Riverpod"><a href="#Riverpod">Riverpod</a></h3> <p><a target="_blank" rel="nofollow noopener" href="https://pub.dev/packages/riverpod">https://pub.dev/packages/riverpod</a></p> <p>異なるウィジェット間にまたがった共通のstateを扱いたい時に便利なパッケージです。これまたWebのReactでいうとReduxとかRecoilとかのイメージです(使い方は全然違います)</p> <p>いろいろな書き方があり、先程のFlutter Hooksと組み合わせて使うこともできます。</p> <p>たとえば下記のようにデータを定義し</p> <pre><code class="dart">class ChatEditState { late Chat chat; int editingMessageKey = -1; int insertingMessageKey = -1; int insertingPersonId = -1; int personId = -1; } </code></pre> <p>こんな感じで値を更新処理を定義したりして</p> <pre><code class="dart">class ChatEdit extends StateNotifier<ChatEditState> { ChatEdit() : super(ChatEditState()); void setChat(Chat chat) { state = state..chat = chat; } </code></pre> <p>こんな感じで更新して</p> <pre><code class="dart">chatEdit.setChat(chat); </code></pre> <p>こんな感じで参照したりします。(バージョンによって書き方が違います)</p> <pre><code class="dart"> Widget build(BuildContext context) { final state = useProvider(chatEditProvider); return Text(state.chat.name); </code></pre> <p>便利なのですが、バージョンアップも頻繁で、そろそろv1になるところで同時に破壊的変更もちょっと続いてるので、直近で利用するにはキャッチアップも必要になっています。あと調べて出てくるサンプルがcounterのようなシンプルすぎるものが多かったりするので情報を見つけるのにちょっと苦労する時もあります。</p> <h3 id="Hive"><a href="#Hive">Hive</a></h3> <p><a target="_blank" rel="nofollow noopener" href="https://pub.dev/packages/hive">https://pub.dev/packages/hive</a></p> <p>アプリ内のデータベースです。端末内で完結するため、ネットワークと接続する必要がなくオフラインで利用できるアプリの開発に役立ちます。僕はせっかくアプリということであまり外部のデータベースを使いたくないためいつもこれを使ってアプリを作っています。</p> <p>データベースと言うと他にもSQLiteがありますが、それに比べて下記のような点が優れています。</p> <ul> <li>定義したクラスからわざわざMap型に変換して保存したりデータを取り出してparseしたりする必要がない</li> <li>型も基本的なものは一通り使える</li> <li>リレーションもできる</li> <li>速い(らしい(多分))</li> </ul> <p>とにかく一番上のですね、開発がとても楽です。</p> <p>ただ独自のデータ処理のため、今のところ問題は出ていないのですが、何か問題があった時にどういう対処が必要なのか、そもそも対処が可能なのか、というのがよくわからない部分もありますので、個人アプリでしか使っていません。仕事ではSQLiteを使っています。この辺は自己判断になっていきそうです。</p> <p>boxという箱からデータを取ってきます。</p> <pre><code class="dart"> static Box<Person> getBox() { return Hive.box<Person>('people'); } static Person get(dynamic key) { return getBox().get(key)!; } static List<Person> getAll() { return getBox().values.toList(); } </code></pre> <p>検索はSQLのwhereみたいなのとか特別なものは特になく、十分速いから普通にListのwhereでデータを取る、みたいな感じみたいです。</p> <p>仕組みとしては、build_runnerというパッケージを用いて、コマンドを使ってデータ管理部分はコードを自動生成してくれるようになっています。そのため面倒なコードを書かなくても開発ができるという感じです。このbuild_runnerは他のパッケージでもちょいちょい使われるので慣れておくと良いと思います。</p> <p>その他詳細は以前記事を書いているのでそちらをどうぞ。<br /> <a href="https://crieit.net/posts/Flutter-Hive">https://crieit.net/posts/Flutter-Hive</a></p> <h3 id="Freezed"><a href="#Freezed">Freezed</a></h3> <p><a target="_blank" rel="nofollow noopener" href="https://pub.dev/packages/freezed">https://pub.dev/packages/freezed</a></p> <p>これはイミュータブルなオブジェクトを簡単に扱うことができるようにしてくれるパッケージです。</p> <p>例えば下記のように値を更新して新たなオブジェクトとして生成します。</p> <pre><code class="dart">final cloned = original.copyWith(name: 'aiueo'); </code></pre> <p>まあこれはこれで良いのですが、個人的にはやはりJSONやMap型のオブジェクトをエンティティの形に相互変換できる機能がすごく便利です。json_serializableというパッケージと組み合わせて使います。</p> <p>これにより、SQLiteやAPIで取得したデータなどオブジェクトに変換する処理を書かなくてもデータを扱うことができるようになり、とても楽です。</p> <p>例えば下記のようなコードを書いてbuild_runnerを実行すればコードが生成されてあとはもう使うだけです。</p> <pre><code class="dart">import 'package:anyone_composer/models/cloud/cloud_record_stroke.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'cloud_record.freezed.dart'; part 'cloud_record.g.dart'; @freezed abstract class CloudRecord implements _$CloudRecord { factory CloudRecord({ required String id, required String uid, @JsonKey(name: 'user_id') required String userId, required List<CloudRecordStroke> recordStrokes, }) = _CloudRecord; const CloudRecord._(); factory CloudRecord.fromJson(Map<String, dynamic> json) => _$CloudRecordFromJson(json); } </code></pre> <p>下記のような感じで変換してくれます。</p> <pre><code class="dart">final entity = CloudRecord.fromJson(json); </code></pre> <p>僕はHiveを使うのであまり使いませんが、仕事でAPIを使う場合や、あとはFirestoreからデータを取るときとかは便利です。</p> <h3 id="Flutter EasyLoading"><a href="#Flutter+EasyLoading">Flutter EasyLoading</a></h3> <p><a target="_blank" rel="nofollow noopener" href="https://pub.dev/packages/flutter_easyloading">https://pub.dev/packages/flutter_easyloading</a></p> <p>ローディング表示を行ってくれるパッケージです。なんかもうこんな感じで簡単です。</p> <pre><code class="dart">EasyLoading.show(); EasyLoading.dismiss(); </code></pre> <p>他にもメッセージ出したり、デザインも設定できたりします。デモサイトで実際に動作を見てみることもできます。通信などで時間がかかる処理がある場合に使いましょう。いちいちボタン用にstateを設定して表示切り替えたりするよりもだいぶ楽です。</p> <h2 id="基本的なやつ"><a href="#%E5%9F%BA%E6%9C%AC%E7%9A%84%E3%81%AA%E3%82%84%E3%81%A4">基本的なやつ</a></h2> <h3 id="google_mobile_ads"><a href="#google_mobile_ads">google_mobile_ads</a></h3> <p>これは公式のAdMobパッケージです。サードパーティのものや、古い公式の別ライブラリがあったりして紛らわしいかもですが、これが最新です。ちゃんとウィジェットとして画面内に埋め込むことができます。</p> <p>とはいえまだ時々よくわからない問題が出たりすることもあるのでシンプルな使い方をするほうが良さそうです。僕はだいたい下の方に表示するだけにしています。まだまだ改善してほしいなと思う部分は多いです。</p> <p>あとはapp_tracking_transparencyもあわせつつ</p> <h2 id="Flutter Launcher Icons"><a href="#Flutter+Launcher+Icons">Flutter Launcher Icons</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://pub.dev/packages/flutter_launcher_icons">https://pub.dev/packages/flutter_launcher_icons</a></p> <p>大きい画像を一つ作っておけばあとはAndroid、iOS用のアイコンサイズをぶわっと作ってくれるやつです。便利です。</p> <h3 id="app_review"><a href="#app_review">app_review</a></h3> <p>レビューリクエストをしてくれるライブラリです。とても簡単に使えるのでリリース時から入れておきましょう。</p> <h2 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h2> <p>あくまでも僕の定番です。みなさんも自分のお気に入りを探しましょう。</p> <p>実際に作ったやつは下記のポートフォリオにの「アプリ」というグループでまとめてあります。(Flutterじゃないゲームも混じってますが)<br /> <a target="_blank" rel="nofollow noopener" href="https://www.alphabrend.co.jp/portfolio">https://www.alphabrend.co.jp/portfolio</a></p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/17536 2021-07-24T22:54:58+09:00 2021-07-25T00:49:06+09:00 https://crieit.net/posts/Flutter-pushNamed FlutterでpushNamedを使うメリット <p>Flutterでページ遷移をする場合、pushを使う方法とpushNamedを使う方法があります。pushは直接ページ用のウィジェットを指定して呼び出す方法で、pushNamedは予め定義したページパスの文字列を指定して呼び出す方法です。</p> <p>push</p> <pre><code class="dart">Navigator.of(context).push( MaterialPageRoute( builder: (context) { return SomePage(); }, ), ); </code></pre> <p>pushNamed</p> <pre><code class="dart">Navigator.of(context).pushNamed('/some/page'); </code></pre> <h2 id="どっちが良いのか?"><a href="#%E3%81%A9%E3%81%A3%E3%81%A1%E3%81%8C%E8%89%AF%E3%81%84%E3%81%AE%E3%81%8B%EF%BC%9F">どっちが良いのか?</a></h2> <p>どっちが良いのか僕もよくわかりませんが、僕はpushNamedを知ってからはそちらだけ使っています。後々気づいたのですが、namedパターンの場合にはどうもFirebase Analyticsでアクセスされたパスを自動的に記録してくれるようで、解析が楽になります。下記のような値として保存されています。</p> <p><a href="https://crieit.now.sh/upload_images/6e2af66c610d88bc766649f72032893a60fc18773d87d.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/6e2af66c610d88bc766649f72032893a60fc18773d87d.png?mw=700" alt="" /></a></p> <p>そのため独自にどのページに行ったかというイベントを送る必要がなくなります。ファネルの設定などもこれを使えば簡単です。</p> <h2 id="使いづらい点"><a href="#%E4%BD%BF%E3%81%84%E3%81%A5%E3%82%89%E3%81%84%E7%82%B9">使いづらい点</a></h2> <p>いくつか使いづらい点はあると思いますので下記のように解消はできます。</p> <h3 id="ページパスの誤り"><a href="#%E3%83%9A%E3%83%BC%E3%82%B8%E3%83%91%E3%82%B9%E3%81%AE%E8%AA%A4%E3%82%8A">ページパスの誤り</a></h3> <p>ページパスは単なる文字列のため、間違える可能性もあります。そのため定数にしておけば実行しなくても誤りに気づけます。</p> <pre><code class="dart">const topPagePath = '/'; const contactPagePath = '/contact'; final routes = { topPagePath: (BuildContext context) => SplashPage(), contactPagePath: (BuildContext context) => ContactPage(), </code></pre> <pre><code class="dart">Navigator.of(context).pushNamed(contactPagePath); </code></pre> <h3 id="arguments の型がない"><a href="#arguments+%E3%81%AE%E5%9E%8B%E3%81%8C%E3%81%AA%E3%81%84">arguments の型がない</a></h3> <p>pushNamedを使う時にargumentsとしてパラメータを渡すことが出来ますが、dynamicなのでここも間違える可能性があります。この場合は各ページにpushNamedをラップしたstaticメソッドを作成しておくと間違えることなく書くことが出来ます。</p> <pre><code class="dart">class ContactPageArguments { ContactPageArguments({ required this.contactCategoryId, required this.title, }); final int contactCategoryId; final String title; } class ContactPage extends StatelessWidget { static Future push(BuildContext context, ContactPageArguments arguments) { return Navigator.of(context).pushNamed(contactPagePath, arguments: arguments); } @override Widget build(BuildContext context) { final arguments = ModalRoute.of(context)?.settings.arguments as ContactPageArguments; </code></pre> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/16998 2021-04-24T20:28:10+09:00 2021-04-24T20:28:10+09:00 https://crieit.net/posts/Flutter-audioplayers-assets Flutterのaudioplayersでなぜかassetsを再生できない時 <p>Flutterにはaudioplayersという音声ファイルを再生できるライブラリがある。ファイルも再生できるし、AudioCacheというやつでassetsの音声ファイルも再生できる。</p> <p>…がなぜか再生できない。ファイルが見つからないっぽくてエラーになる。</p> <p>そもそもサンプルのコードが</p> <pre><code class="dart">audioCache.play('audio.mp3') </code></pre> <p>となっており、そもそもパスってどういう仕組で解釈されてるんだ!? と謎がある。</p> <p>で、調べてみたところ、どうも <code>assets</code> というフォルダに入れなければならず、そこからの相対パスを書く必要があるらしい。つまり sounds/aaa.mp3 とかだとそもそも再生できない。 assets/sounds/aaa.mp3 として配置し、 <code>audioCache.play('sounds/aaa.mp3')</code> とすれば辻褄があるという感じらしい。ドキュメントに書いてよ!</p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/16841 2021-04-13T16:45:08+09:00 2021-04-13T16:45:08+09:00 https://crieit.net/posts/38e8f748a2d53951f9604bea786870bf モバイルアプリ開発フレームワーク <p>現在、40億人を超えるモバイルユーザーがいるため、モバイルでオーディエンスとつながることができなければ、あなたは存在しません。人々はスマートフォンの使用をとても楽しんでいるので、技術のない日を過ごすために自分自身に挑戦する必要があります。スマートフォンは、電話、テキストメッセージ、メールのチェック、さらには娯楽まで、必要なものをすべて提供できます。そのため、モバイルアプリフレームワークが実現します。<br /> このような陰謀的なアプリケーションの舞台裏には、何千ものモバイルアプリ開発フレームワークがあります。ただし、モバイルアプリをうまく作るには、適切なモバイルアプリ開発フレームワーク、関連技術、プラットフォーム、データベースが不可欠です。とはいうものの、あまりにも多くの選択肢があり、どれかひとつに絞るのはこれまで以上に難しい状況です。そこでこの記事では、iOSとAndroid両方のプラットフォームで最も人気の高いモバイルアプリ開発フレームワークについて説明します。その前に、効率的な モバイルアプリ開発フレームワーク とはどのようなものかを簡単に見ていきましょう。</p> <p><strong>1. React Native</strong><br /> React Nativeは、AndroidアプリとiOSアプリの両方向けに構築された非常に人気のあるフレームワークです。特に、モバイルアプリの開発者は、より短いビルドサイクルでより高速なデプロイ時間で高性能アプリをビルドでき、予算にやさしいオプションです。さらに、React Nativeは、ビュー、テキスト、画像など、プラットフォームに依存しないネイティブコンポーネントのコアセットを提供し、すべてプラットフォームのネイティブUIビルディングブロックにマップされます。また、フルスタックに必須のJavaScriptもサポートしています。</p> <p><strong>2. Flutter</strong><br /> FlutterはGoogle が作ったオープンソースの<a target="_blank" rel="nofollow noopener" href="https://kaopiz.com/ja-news-cross-platform-framework-flutter-vs-react-native/">ネイティブアプリケーション開発フレームワーク</a>です。Dart という共通言語で開発を行います。詳しいデバッグができるので React Native で感じる「隔靴掻痒」感は少ないです。<br /> Flutterを使うことで同一のコードベースからAndroidやiOSアプリを構築でき、その見た目もネイティブアプリです。FlutterはGoogleによる導入が2015年でしたが、2018年12月の公式リリースまではベータ段階にとどまっていました。<br /> Flutterのユーザーは、携帯電話、テレビ、タブレット、ウェアラブルデバイス、スマートディスプレイ用のアプリを作成できます。<br /> ちなみに Flutter Studio は Flutter の UI 作成のために使えるサイトです。ビルドのためのものではありません。Flutter を採用するなら使うことになるのではないかと思われます。Flutter ではそれぞれの Native パートをプロジェクト含めることができる。</p> <p><strong>3. Ionic</strong><br /> Ionicは、クロスプラットフォームアプリケーションとともにインタラクティブハイブリッドおよびPWAを構築するのに役立ちます。このオープンソースフレームワークは、アプリケーションを作成するためのプレミアムサービスを提供します。さらに、Ionicは、Web、Android、およびiOS用のアプリケーションの構築をカバーしています。さらに、Ionicで作業している間は、常にアプリケーションを作成して、展開可能な場所に出荷できます。すぐに使用できる機能を備えているため、アプリケーション開発に最適です。</p> <p><strong>まとめ</strong><br /> 以上挙げた3つのフレームワーク以外、他にも<a target="_blank" rel="nofollow noopener" href="https://kaopiz.com/ja-news-mobile-application-development-frameworks-20202021/">モバイルアプリ開発フレームワーク</a>があります。どのフレームワークを選択するかは、要件、予算、技術的要件、そして短期的または長期的な成功の可能性に応じて、最も適したものを選ぶと良いでしょう。</p> hanhnh tag:crieit.net,2005:PublicArticle/16821 2021-04-06T23:16:07+09:00 2021-04-06T23:18:28+09:00 https://crieit.net/posts/Flutter-url-launcher-App-Store-URL Flutterのurl_launcherでApp StoreのURLを開くとフリーズする問題 <p>Flutterで作ったアプリ内で別のアプリに送客をしようと思い、url_launcherでApp StoreのアプリのURLを開くことにしました。</p> <p><a target="_blank" rel="nofollow noopener" href="https://pub.dev/packages/url_launcher">https://pub.dev/packages/url_launcher</a></p> <p>テストしてみると特に問題ない……と思いきや、どうもストアの画面からアプリに戻ってみるとアプリがフリーズしていて動かない…!!! Android側は問題ないのですが、iOS側だけ。</p> <p>調べてみると完全にフリーズしているわけではなく、画面の描画だけがフリーズしており、ボタンは押した時のアニメーションが途中で止まったまま。しかも操作をするとログが流れていくので描画は止まっているけどアプリ自体は動いていて画面遷移などボタンのタップなどは動いている状態のようです。</p> <p>なぜか全然分からず、とにかくタップ時に遷移するのがまずいのかと思い、 <code>Future.delayed</code> をしてみたり、launchを呼び出す時にどうせURLを開くだけだからとawaitを省略していたのを追記してみたりしましたがそれでも全然うまくいきません。</p> <p>多分普通のURLだと大丈夫だと思うのですが、App StoreのURLはダメっぽいようです。Schemeを使って開いたら良いのかな、とも思いましたが、どうもSchemeはちょいちょい変わったりURLが古いっぽかったりして全然安心できなかったので、ひとまず使わない方向で考えていくことにしました。</p> <h2 id="解決方法"><a href="#%E8%A7%A3%E6%B1%BA%E6%96%B9%E6%B3%95">解決方法</a></h2> <p>そしてついに解決法を見つけました。大変でした……。</p> だら@Crieit開発者