tag:crieit.net,2005:https://crieit.net/tags/Dart/feed 「Dart」の記事 - Crieit Crieitでタグ「Dart」に投稿された最近の記事 2022-11-29T17:08:40+09:00 https://crieit.net/tags/Dart/feed tag:crieit.net,2005:PublicArticle/18319 2022-11-23T17:55:13+09:00 2022-11-29T17:08:40+09:00 https://crieit.net/posts/acea21ee905aaf3997a8f1856b504303 事故物件的なポモドーロ タイマー <p>たのまれたので、なんとかする予定のプログラム。</p> <p>ポモドーロタイマー。</p> <p>ポモドーロタイマーとは、ポモドーロがイタリア語でトマト、でタイマーはキッチンタイマーのタイマー。</p> <p>トマト キッチンタイマーなんだけど、そっちが主ではなくて、そのタイマーではかる時間のことを指している。<br /> なにかクリエーティブな作業をするときの時間配分で、2時間かけたい場合、25分間作業して、5分間休憩して、25分間作業して、5分間休憩して、<br /> 25分間作業して、5分間休憩して、25分間作業して、15分間休憩してというようにすると、いい感じで集中してられる、というライフハックのことをポモドーロタイマーというらしい。</p> <pre><code>end time 17:28:35 1 pomodoro = 25min, rest = 5min long rest = 15min 1 pomodoro 25min -> rest 5min -> 1 pomodoro 25min -> rest 5min 1 pomodoro 25min -> rest 5min -> 1 pomodoro 25min -> long rest 15min total 130min pomodoro: 1 total time: 16 min remain: 9 min now 15:35:08 </code></pre> <p>さっと作ってみてポモドーロタイマーを検索したらいっぱいありそう。</p> <p>たのまれたのは、たぶんそういうのじゃないのでしばらく時間がかかりそう。</p> <p>パピヨン本田のマンガを読んでリラックスしよう。</p> <p><a target="_blank" rel="nofollow noopener" href="https://twitter.com/papiyonhonda/status/1475425058684551176?s=20&t=LHttfuow8JjZmaKTEYmpQQ">https://twitter.com/papiyonhonda/status/1475425058684551176?s=20&t=LHttfuow8JjZmaKTEYmpQQ</a></p> <p><a target="_blank" rel="nofollow noopener" href="https://mobile.twitter.com/papiyonhonda/status/1589904538404007936/photo/1">papiyonhonda</a></p> <p>基本的には、このようなかんじか。<br /> Ruby<br /> <a target="_blank" rel="nofollow noopener" href="https://rentry.co/uf7d5">https://rentry.co/uf7d5</a></p> <p>問題なければ、機能を付けていく。<br /> まだロジックを考えてないが、ひとまず間違いがないかチェック。</p> <p>うん、なんかまちがっているね。</p> <p>どこがおかしいかなということでチェックしていこう。</p> <pre><code class="ruby">1 require 'time' 2 3 pomo = ARGV[0].to_i # 1 pomodoro durations 4 interval = ARGV[1].to_i # rest durations 5 long_rest = ARGV[2].to_i # long rest durations 6 7 one_set = pomo*4 + interval*3 + long_rest # min 8 puts one_set # min 9 10 tempotime_0 = Time.new 11 tempotime = Time.new 12 mission_finish_time = tempotime_0 + one_set*60 # sec 13 puts tempotime_0 14 puts mission_finish_time 15 minutes = (mission_finish_time - tempotime_0).to_i.div(60) 16 puts "#{minutes/60}:#{minutes%60}" 17 exit </code></pre> <pre><code class="ruby">60 while seconds < 60 do 61 t = Time.new 62 63 if (t - tempotime > 1) </code></pre> <p>のとこは</p> <pre><code class="ruby">60 while seconds < 60 do 61 t = Time.new 62 63 if ((t - tempotime).to_i > 0) </code></pre> <p>こうしといて。</p> <p>ここもおかしくなるので、</p> <pre><code class="ruby">92 puts "remain: #{(((tempotime_0 + pomo*60)-t).to_i).div(60)} min #{(((tempotime_0 + pomo*60)-t).to_i).modulo(60)} sec" </code></pre> <p>これでいいんじゃない。</p> <pre><code class="ruby">92 puts "remain: #{((tempotime_0 + pomo*60*pomodoro + resttimes*60*(pomodoro - 1)) - t).to_i.div(60)} min #{(((tempotime_0 + pomo*60*pomodoro + resttimes*60*(pomodoro - 1)) - t).to_i).modulo(60)} sec" </code></pre> <p>いちおう、ベースはできた。基本的にはこれで良さそう。<br /> Ruby<br /> <a target="_blank" rel="nofollow noopener" href="https://rentry.co/pvmdk">https://rentry.co/pvmdk</a></p> <p>間違うところもよくわかったので、初めて Dart をどうやって書くのか読みながらヨチヨチと Ruby で書いたものと同じような感じにしてみる。<br /> Dart<br /> <a target="_blank" rel="nofollow noopener" href="https://rentry.co/369sd">https://rentry.co/369sd</a></p> tomato tag:crieit.net,2005:PublicArticle/17948 2022-01-20T19:00:13+09:00 2022-01-20T19:00:13+09:00 https://crieit.net/posts/Dart-61e932ada423e Dartを使った大規模なアプリ開発 <p>こんにちは、Quireです。</p> <p><a target="_blank" rel="nofollow noopener" href="https://quire.io">Quire</a>は、Dartで開発された初めてのWebアプリケーションではありませんが、クライアントとサーバーの両方でDartをふんだんに使ったものとしては、初めてかもしれません。</p> <p><img src="https://storage.googleapis.com/zenn-user-upload/1a086b990119-20220120.png" alt="" /></p> <p>動作の軽い、徹底して階層構造のタスク管理ツールです。このプロジェクトはDartコードで合計53992行、1620 KB。コミュニティーからのオープンソース ライブラリも使っています。</p> <h1 id="私たちについて"><a href="#%E7%A7%81%E3%81%9F%E3%81%A1%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6">私たちについて</a></h1> <p>このプロジェクトを開始するまで、私たちはRikuloというDartファンの集まりでした。Rikuloはこれまで、UIフレームワークやUIライブラリ、Webサーバー、メッセージサーバー、DBクライアントなどのDartライブラリをリリースしています。<br /> Dartが2011年に初めてリリースされたとき、すぐに将来性を感じ、さっそく小規模のプロジェクトに着手。その後、ほとんどDartで書かれた大規模アプリの開発へと進みました。今回はそのときの経験をお話しします。Dartによる開発に興味を持っていただければ幸いです。</p> <h1 id="なぜDartか"><a href="#%E3%81%AA%E3%81%9CDart%E3%81%8B">なぜDartか</a></h1> <p>私たちがDartを選んだ理由はたくさんありますが、以下に大きなものを挙げてみます。<br /> - 強く型付けされていて、無数の小さなミスを防ぐことができる。公式IDEのDart Editorは、タイプミスへの即時フィードバックやオートコンプリート機能を備えており、トラッキングも容易。<br /> - 継承がクラスベースで、プロトタイプベースよりも直感的に使える。<br /> - クライアントとサーバーの両方で同じ言語が使えて、データモデルやコードベースを共有できる。<br /> - JavaScriptの問題点をほぼ解消。完全ではないが、以前にあった問題の99%には対応している。<br /> - JavaScript Harmonyで人気の特徴をいくつか備えている。例えばFuture(Promise)パターン、矢印関数など。<br /> - 強力なチームの後ろ盾があり、ハイクオリティーな公式ライブラリや使いやすいAPIを利用できる。まだ開発途上ながら、仕様はすでにかなり安定している。<br /> - サーバーサイドではマルチスレッドではなくイベントループ方式で、好みだった。<br /> - JavaScriptへのコンパイル時にTree Shakingできる(下記参照)。</p> <h5 id="もちろんデメリットもあります。以下に挙げてみます。"><a href="#%E3%82%82%E3%81%A1%E3%82%8D%E3%82%93%E3%83%87%E3%83%A1%E3%83%AA%E3%83%83%E3%83%88%E3%82%82%E3%81%82%E3%82%8A%E3%81%BE%E3%81%99%E3%80%82%E4%BB%A5%E4%B8%8B%E3%81%AB%E6%8C%99%E3%81%92%E3%81%A6%E3%81%BF%E3%81%BE%E3%81%99%E3%80%82">もちろんデメリットもあります。以下に挙げてみます。</a></h5> <ul> <li>コミュニティーがJavaScriptよりも圧倒的に小さい。</li> <li>DartとJavaScript間の通信が自明でない。</li> <li>強い型付けなので、APIのPolyfillが面倒。</li> <li>Dart Editorの性能が(今のところ)大規模プロジェクトに適さない。</li> </ul> <p>強い型のサポートにより、書くプロセスがJavaScriptよりもずっとロバストです。また、Javaよりもシンプルで、Vanilla JavaScriptと比べてもシンプルなことがあります(関数の書き方など)。全体的にDartは扱いやすい言語ですが、以下のような例外はあります。</p> <ul> <li>コンストラクタのボディよりも先にイニシャライザでFinalフィールドを初期化する必要がある。</li> <li>ミックスインの仕様が使いにくく、2.0まで改善されそうにない。</li> <li>ジェネリック型パラメータがクラスにしかなく、関数宣言にない。(そのためコンパイラの負荷が増す)。</li> </ul> <h2 id="クライアントサイド"><a href="#%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3%E3%83%88%E3%82%B5%E3%82%A4%E3%83%89">クライアントサイド</a></h2> <p>Dart VMがChromeに統合される日まで、JavaScriptにコンパイルして作成することになります。JavaScriptにコンパイルする言語はたくさんありますが、Dartには以下のような独自のメリットがあります。</p> <ul> <li>私たちは開発時、Dart VMを内蔵したChromiumベースのブラウザー、DartiumでDartをネイティブに実行している。このイテレーションではコンパイルが不要で、スムーズに進めることができる。</li> <li>テストとプロダクションの段階でJavaScriptにコンパイルし、すべてのメジャーなブラウザーで実行する。このとき使用するコンパイラがTree Shakingを行い、不要なコードを除去するため、JavaScriptのコードサイズを小さくできる。</li> </ul> <h3 id="サーバーサイド"><a href="#%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%82%B5%E3%82%A4%E3%83%89">サーバーサイド</a></h3> <p>サーバーサイドについてはあまりコミュニティーで話題になりませんが、私たちは以下の理由から、Dartはサーバーサイドのプログラミング言語に入ると考えています。</p> <ul> <li>Webサービスは本質的に非同期なため、イベントループ方式と相性がいい(マルチスレッドと比べて)。</li> <li>クライアントサイドよりもサーバーサイドのほうが、よりロバストで安全なコードが必要。強い型付けの言語はこの点において有利。</li> </ul> <h1 id="サポートライブラリ"><a href="#%E3%82%B5%E3%83%9D%E3%83%BC%E3%83%88%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA">サポートライブラリ</a></h1> <p>Quireは約30のライブラリをインポートしています。そのうち10ライブラリはコミュニティーからで、残りはDartからリリースされたものです。Dartに詳しい方ならAngularDARTとPolymer.dartがあると思うかもしれませんが、実はどちらも使っていません。</p> <h5><a target="_blank" rel="nofollow noopener" href="https://angulardart.org/">AngularDART</a>を使わない理由は、以下のとおりです。</h5> <ul> <li>DOMを細かく制御したい。</li> <li>私たちはクライアント構造の構築に独自のアーキテクチャガイドラインを使用しており、そのパラダイムがAngularのロジックと異なる。</li> <li>AngularDARTについて調べたとき、コンパイルされたJavaScriptのコードサイズにかなりのオーバーヘッドが生じた。ただし、これはその後かなり改善された。</li> </ul> <h5 id="私たちは以下の理由から、Polymer.dartも使っていません。"><a href="#%E7%A7%81%E3%81%9F%E3%81%A1%E3%81%AF%E4%BB%A5%E4%B8%8B%E3%81%AE%E7%90%86%E7%94%B1%E3%81%8B%E3%82%89%E3%80%81Polymer.dart%E3%82%82%E4%BD%BF%E3%81%A3%E3%81%A6%E3%81%84%E3%81%BE%E3%81%9B%E3%82%93%E3%80%82">私たちは以下の理由から、Polymer.dartも使っていません。</a></h5> <ul> <li>カプセル化とイベント リターゲティングのため、ShadowDOMはBootstrapのようなセレクター指向のフレームワークと併用できない。</li> <li>ShadowDOMの外部からスタイルを適用できない。サードパーティーのコンポーネントセットがPolymerで作成されている場合、ユーザーが見た目や感じを変えることは不可能に近い。(更新:2013年12月時点で、内部のスタイルを外部から変更可能。<a target="_blank" rel="nofollow noopener" href="http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom-201/#toc-style-cat-hat">Shadow DOM 201</a>を参照。)</li> </ul> <h3 id="DQuery&Bootjack"><a href="#DQuery%EF%BC%86Bootjack">DQuery&Bootjack</a></h3> <p>私たちのクライアントサイド スタックの基盤は、Rikuloリリースのオープンソース プロジェクト、DQueryとBootjackです。<br /> - <a target="_blank" rel="nofollow noopener" href="https://github.com/rikulo/dquery">DQuery</a>はjQueryの一部、特にイベントデリゲートの仕組みの部分をDartに移植したものです。<br /> - <a target="_blank" rel="nofollow noopener" href="https://github.com/rikulo/bootjack">Bootjack</a>はBootstrap 3の完全な移植で、CSS およびAPIとほぼ同じです。</p> <p>このようにアプリケーション スタックを構築して、JavaScriptについて知っていること、できることを活用しています。</p> <h3 id="Stream"><a href="#Stream">Stream</a></h3> <p>私たちのWebサーバーは、Dartのみで書かれ、ルーティング、フィルター、サーバーサイドMVCなどを備えたStreamです。イベントループ方式とシームレスに連携して動作します。リクエストハンドラーも、ノンブロッキングのルーチンをつなげるだけで書くことができ、従来のマルチスレッド方式よりも生産的かつ快適に仕事ができます。私たちは、Webサービスをスケールし、nginxでHTTPSを処理し、Streamにリクエストをデリゲートしたりもしています。このアーキテクチャによりDart VMを個別にSpawn/Despawnでき、サーバーのアップグレードもユーザーの使用を妨げずに実施できます。</p> <h1 id="終わりに"><a href="#%E7%B5%82%E3%82%8F%E3%82%8A%E3%81%AB">終わりに</a></h1> <p>Dartは使い勝手の良い言語です。この記事がDartコミュニティー成長の一助となることを願っています。最後に、Dartで何ができるか興味のある方は、ぜひ<a target="_blank" rel="nofollow noopener" href="https://quire.io">Quire</a>を試してみてください。</p> uniyeh 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/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/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/16762 2021-03-22T05:03:43+09:00 2021-03-22T08:06:00+09:00 https://crieit.net/posts/Dart 素朴な自作言語のコンパイラをDartに移植した <hr /> <p><a target="_blank" rel="nofollow noopener" href="https://memo88.hatenablog.com/entry/2020/08/30/132314">移植一覧に戻る</a></p> <hr /> <p>Dart で書いてみました。やっつけなので汚いです。Dart よく知らないけどライフゲームが動いたのでヨシ、というレベルのものです。</p> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/sonota88/vm2gol-v2-dart">https://github.com/sonota88/vm2gol-v2-dart</a></p> <hr /> <h1 id="移植元"><a href="#%E7%A7%BB%E6%A4%8D%E5%85%83">移植元</a></h1> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://memo88.hatenablog.com/entry/2020/05/04/155425">Rubyで素朴な自作言語のコンパイラを作った</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/sonota88/vm2gol-v2">https://github.com/sonota88/vm2gol-v2</a></li> </ul> <p>ベースになっているバージョン: <a target="_blank" rel="nofollow noopener" href="https://github.com/sonota88/vm2gol-v2/tree/46">tag:46</a> のあたり</p> <ul> <li>追記 2021-03-21: Dart のバージョンアップ(2.9.1 => 2.12.0)に合わせて修正 <ul> <li>null safety まわりを適当に修正</li> </ul></li> <li>追記 2021-03-21: <a target="_blank" rel="nofollow noopener" href="https://memo88.hatenablog.com/entry/20210317_vm2gol_v2_step56_builtin_vram_function">ステップ56</a> の修正まで適用しました</li> </ul> <h1 id="メモ"><a href="#%E3%83%A1%E3%83%A2">メモ</a></h1> <ul> <li>アセンブラ・VM は移植対象から外しました。Ruby 版のものを使います。</li> <li>トークナイザとパーサを分けてみた</li> <li>テストのステップを細かくした</li> <li>ファイルを直接まじめに読まなくても、 標準入力が読めればあとはシェルスクリプトでラップしてあげたりすればいいので、 もうそれでいいかなという気持ちになった。</li> </ul> <hr /> <p>Dart でプログラムを書くのは今回初めてでしたが、JavaScript と Java あたりを知っていればかなりスムーズに書き始められるなーという感想でした。引っかかるところがほとんどなかったです(今回書いた範囲では)。適当にググりながら 1日で書けてしまいました。</p> <hr /> <p>今回の実験的な要素としては、パーサをいじって <code>set</code> を不要にしてみました。</p> <p>(追記 2021-03-21: この修正はいったん revert しましたが、一応 <a target="_blank" rel="nofollow noopener" href="https://github.com/sonota88/vm2gol-v2-dart/tree/trial">trial</a> ブランチに残してあります。)</p> <pre><code class="diff"> func main() { var a; - set a = 42; + a = 42; } </code></pre> <p>文の先頭の <code>set</code> で判別するのをやめて、識別子だったら <code>parseSet_v2()</code> を呼ぶ。もうちょっとめんどくさいかなと思ってましたが、これだけだったら意外と簡単ですね。</p> <pre><code class="dart">List parseSet_v2() { // consume("set"); ... これが不要になる final t = peek(); pos++; final varName = t.value; consume("="); final expr = parseExpr(); consume(";"); return ["set", varName, expr]; } List parseStmt() { final t = peek(); if (t.value == "}") { return null; } if (t.value == "func" ) { return parseFunc(); } // ... // else if (t.value == "set" ) { return parseSet(); } // ... else if (t.value == "_cmt" ) { return parseVmComment(); } else { if (t.type == "ident") { return parseSet_v2(); } else { throw notYetImpl([ t ]); } } } </code></pre> sonota486 tag:crieit.net,2005:PublicArticle/15825 2020-04-13T21:00:50+09:00 2020-04-13T21:05:45+09:00 https://crieit.net/posts/Flutter-Dart-DateTime Flutter(Dart)でDateTimeが同じ日かどうかを判定する <p>Dartで2つのDateTimeが同じ「日」かどうかを判定したい。DateTimeにはcompareToというメソッドがあるのだが、そもそもDateTimeは日付でなく時間も扱っているため、compareToだとなんとなく怪しそう……。</p> <p>一応、</p> <pre><code class="dart">if (a.year == b.year && a.month == b.month && a.day == b.day) { </code></pre> <p>という書き方もできるが、これはたまたま変数名が1文字なのでなんとなく良さそうに見えるが実際の変数でやるとめちゃくちゃ長くなるしなんか微妙。</p> <p>ということで同じ日を調べるのにはこんな書き方もある。</p> <pre><code class="dart">if (a.difference(b).inDays == 0 && a.day == b.day) { </code></pre> <p>参考<br /> <a target="_blank" rel="nofollow noopener" href="https://stackoverflow.com/questions/52978195/comparing-only-dates-of-datetimes-in-dart">time - Comparing only dates of DateTimes in Dart - Stack Overflow</a></p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/15703 2020-02-01T21:49:48+09:00 2020-02-01T21:53:19+09:00 https://crieit.net/posts/Dart-mixin-static Dartでmixinのstaticメソッドが直接呼べてしまう <p>どうもDartではmixinに実装しているstaticメソッドがそのまま呼べるっぽい。クラスに割り当てなくても。(Dart2.7現在)</p> <h2 id="そもそもmixinとは"><a href="#%E3%81%9D%E3%82%82%E3%81%9D%E3%82%82mixin%E3%81%A8%E3%81%AF">そもそもmixinとは</a></h2> <p>普通、mixinは下記のような感じでclassと組み合わせてclassを拡張させて使うもの。</p> <pre><code class="dart">mixin Mixi { void test() { debugPrint('aaaa'); } } class Tekito with Mixi { } </code></pre> <h2 id="でもそのまま呼べる"><a href="#%E3%81%A7%E3%82%82%E3%81%9D%E3%81%AE%E3%81%BE%E3%81%BE%E5%91%BC%E3%81%B9%E3%82%8B">でもそのまま呼べる</a></h2> <p>元々の経緯として、普通にmixinを使ってclassを拡張しようと思っていた。ただ、型の関係で色々ややこしかったのでやめにして、staticメソッドをたくさん実装したクラスを作ってそれを呼ぶだけにしよう、と思い、途中からクラスに変えてstaticメソッドを実装していった。</p> <p>それでなんだかんだで実装を進めていった。ところが、ある時ふと見直してみると、mixinのままになっていた。classに変えるのを忘れている。ところが、いままで実装したところは全部問題なく動いている。</p> <p>つまり、mixinのstaticメソッドは直接呼べてしまうっぽい。</p> <h2 id="なぜ?"><a href="#%E3%81%AA%E3%81%9C%EF%BC%9F">なぜ?</a></h2> <p>なぜかはよくわからないが、そもそもDart1の時はmixinを定義する時に<code>mixin MixinName</code>という感じでmixinという構文でmixinを定義する方法が無かった。普通にクラスとして定義することで、mixinとしても使える、という仕様だったっぽい。おそらくその名残でclassっぽい動きも残ってしまっているのではないかと思う。多分。</p> <p>ちなみに、定義したmixinのインスタンスももしかしたら作れるのではないかと思ったがそれは無理だった。</p> だら@Crieit開発者