Life is Really Short, Have Your Life!!

ござ先輩の主に技術的なメモ

RiverpodのProviderが管理するStateに引数を与えて初期化したい

画面遷移とかでよくあるよね。一覧から行タップで詳細に遷移する系のやつ。商品一覧→商品詳細みたいなやつです。

Riverpodが提供するProviderにはfamilyという関数があり、Providerを初期化する際に任意の型の引数を与えることができる。今回は、StateNotifierProviderでありながら、autoDisposefamilyも使うというこんもりセットだった。

// 第1引数:  利用するStateNotifierの型
// 第2引数: 第1引数に入れたStateNotifierで管理するStateの型
// 第3引数: 管理するStateの初期化に必要なパラメータの型
final calcProvider = AutoDisposeStateNotifierProviderFamily<CartPageStateNotifier, Cart, Cart>(
        (ref, param) => CartPageStateNotifier.init(cart: param));

//Widget内部ではこうなる。ConsumerWidgetを使っています。
//このitemは画面遷移でもらったデータで
Widget hogeWidget(BuildContext context, ScopedReader watch) {
    final state = watch(calcProvider(item));
    final vm = watch(calcProvider(item).notifier);
}
class CartPageStateNotifier extends StateNotifier<Cart> {
  CartPageStateNotifier() : super(const Cart());
  CartPageStateNotifier.init({required Cart cart}) : super(cart);
}

こんな感じで、CartPageStateNotifierに名前付きのコンストラクタを定義しておく。initは渡したいデータでstateを初期化する際に呼び出す。

RiverPodの設計思想は勉強になるなぁ.. というか、型がある言語はナイスだなぁ... PythonもTypeHintあるけどね。おほほンゴ。

RiverpodでinitStateを使わず、autoDisposeでUIを再構築する

FlutterのRiverpodの話。

10月に入って、作りかけのアプリのProvider -> Riverpodへのリプレイスを行っています。zennで公開されているRiverpod解説には大変お世話になりました。

ひとつわからなかったのが、「タブを切り替えた時に自動でそのページの中身をリロードして欲しい」という処理の書き方。

StatefulWidgetであれば、initStateが使える。ただ、Riverpodで提供されているHookWidgetやConsumerWidgetには、StatefulWidgetが提供するライフサイクルが存在しない。少なくとも、Riverpodの1.4系だと。

お買い物かごにアイテムを何点か追加して、お買い物かごタブをタップした時、タップに応じて画面を最新化する必要がある。カートはローカルにあるデータで、FutureProvierを使って画面を構築していますが、タップでタブを切り替えてもUIが再構築されない。providerをキャッシュしているから当たり前だけど、ユーザーの画面操作でproviderを更新できないので、どうしようか悩んだ。

結論は単純だった。autoDisposeで自動廃棄してもらうだけ。

final cartController = FutureProvider.autoDispose<CartState>((ref) {
  return loadCart();
});

autoDisposeはスクリーンが消えると読み出してくれるようで、タブで画面を切り替えると、providerで保持した値も全部消える。裏返せば、タブを切り替えるタイミングで毎回データが初期化されるので、providerの値が常に更新され、UIが再構築されました。

最新情報を常に取ってこないといけない類の画面の実装は、FutureProvier.autoDisposeで行けそうだね。stateも任意のタイミングで更新できますので。ちゃんちゃん。

Android StudioでUnable to find bundled Java version

flutter doctorでこの警告が出る場合。一定の手順に沿って行うだけ。JetBrain Toolboxを使っている人の対応策も書いてあった。

Macのユーザー名とAndroidStudioのバージョンは、その時の最新に差し替えれば良さそう。

stackoverflow.com

cd /Users/<USER_NAME>/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/<APP_VERSION>/Android Studio.app/Contents/jre
ln -s ../jre jdk
ln -s "/Library/Internet Plug-Ins/JavaAppletPlugin.plugin" jdk
flutter doctor -v

FileSystemObjectがネットワークドライブ上のファイル探索でクソ遅い件

ネットワークドライブ上の35個のExcelファイルのOPEN→転記に30分近く要していた。

おせえええええええ、死ねええええええ。

理由はわからないけど、FileSystemObjectを使ってネットワークドライブ上のファイル走査すると、クソ遅い。なので、Dir関数を使う方法に変更したら、6分に短縮された。

理由はわからない。

Prism8のContainerLocatorへの対応

日本でWPFのPrismやってるの100人いるかどうかじゃないかな。なんでWindowsのデスクトップアプリなんか作ったんだろう。

Prism8が2020年の10月頃にリリースされ、破壊的変更が入った。ServiceLocatorがなくなってContainerLocatorに変わりました。それだけ言われてもどこを変えていいかわかんねーよって感じですけど、この2点を変えたら動いたので、共有。

RegisterTypes

Locatorなるものが管理するViewが一括で登録されるのがRegisterTypes。そういうものらしい。 Prism8になってIContainerRegistryからGetContainerメソッドがなくなったので、削除するだけでビルドが通った。

        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
            this.GetType().GetTypeInfo().Assembly
             .DefinedTypes
             .Where(t => t.Namespace?.EndsWith(".Views", System.StringComparison.Ordinal) ?? false)
             .ToList().ForEach(t => {
                //containerRegistry.GetContainer().RegisterTypeForNavigation(t.AsType(), t.Name);
                 containerRegistry.RegisterForNavigation(t.AsType(), t.Name);
             });
       }

RegionManager

ビルドは通ったけど、実行時例外(ぬるぽ)は別問題だよね。

Prismで画面遷移する時、GetRegionManager().RequestNavigate("ContentPage", nameof(HogePage));って書いている。ContentPageMainWindow.xamlに書いたこのコードに呼応してる。RegionNameだね。こうすることで、ヘッダー・フッターは固定でコンテンツだけ差し替えることが簡単にできる。便利まーん。

        <ContentControl prism:RegionManager.RegionName="ContentPage"  />

GetRegionManager()ではIRegionManagerインスタンスServiceLocatorから取得していたが、Prism8になってこのクラスのインスタンスはDIされなくなった。なので、こういうコードを書いたら動いた。

 protected IRegionManager GetRegionManager()
        {
            return ContainerLocator.Container.Resolve<IRegionManager>();
            //return ServiceLocator.Current.GetInstance<IRegionManager>();
        }

VIewModelの初期化時にRegionManagerを渡すことも出来るっぽい。IDialogServiceを使う時に、ViewModelのコンストラクタにインスタンスをDIするのと同じイメージ。以下のリンクは、IDialogServiceの使い方。Prism8になると、InteractionRequestは使えない。気をつけて。

prismlibrary.com

クライアントサイドのプログラム、なんとな〜く文脈をつかんでノリで書いたら動くことが多い。あはは。

Pycharmで"Couldn't refresh skeletons for remote interpreter"が出る

MacのDockerの設定の問題だった。

f:id:gothedistance:20210723230018p:plain

ここにUser Docker Compose v2とある。このチェックを外したらエラーが出なくなった。

$  docker --version
Docker version 20.10.7, build f0df350
$ docker-compose --version
Docker Compose version v2.0.0-beta.6 # suck
$ docker-compose --version
docker-compose version 1.29.2, build 5becea4c # good.

やっておいたほうが良さそうなこと

  • PycharmのInvalidate Cache and Restart
  • Pycharmのヘルパーコンテナ及びイメージの削除
  • 以前作ったPython Interpreterの設定の削除

こんだけやれば動きました。4連休最大の懸念が解消されてちょーうれしい。

HerokuでRoute53で独自ドメイン(サブドメイン)運用

秒で終わったのでメモ。

  • ドメインを追加する。hoge.goza.comとする。
  • 追加後、goza53.herokudns.comみたいなドメインが 発行される。
  • hoge.goza.com のCNAMEに上記のドメインをあてるだけ。
  • あとはHeroku側のACMで色々やってくれて、SSLも作ってくれる。

1分で終わりました。