torikatsu.dev

Flutterとかプログラミングとかガジェットとか書きます

WidgetやProviderのライフサイクルについて調べてみた

現在Flutterでアプリ開発をしているのですが、ウィジェットやProviderのライフサイクル周りで少しハマりました。

そこで今回の記事ではWidgetやProviderのライフサイクルについて紹介したいと思います。

ハマったケース1

問題

CustomScrollView内にProviderを複数配置した時にBlocA、BlocBは生成されますが、BlocCは生成されない

ウィジェットの構成
ウィジェットの構成

解決方法

この問題の原因はウィジェットのライフサイクルにあります。 スクロール可能な画面において、ウィジェットは画面外に出てしまうと自動で破棄され、再び画面内に入るときに生成されるようです。

今回のケースではBlocA、BlocBを注入するProvider<BlocA>Provider<BlocB>はすでに画面内に存在するため生成済みとなりますが、BlocCを注入するProvider<BlocC>は画面外のため未生成となります。

これより、画面の生成時にBlocA・locB・BlocCを注入するProviderをすべて生成するにはCustomScrollViewの位置でMultiProviderをつかうなどして各Providerを生成すれば良さそうです。

ただし、この方法でProviderを生成してしまうとCustomScrollView以下全てのウィジェットから全てのBlocにアクセスできてしまうので注意が必要です。

私はBlocの肥大化を防ぐためにBlocを分割したかったので、今回のケースではどこからでもBlocへアクセスできることには目を瞑ることにしました。

ハマったケース2

問題

先ほどのケース1を踏まえウィジェットの構成を修正してみました。

CustomScrollViewrより親でMultiProviderを使ってBlocを注入するよう変更しました。

しかし、ここでまた問題が発生しました。

Providerは全て生成済みですが、BlocCは未生成のままです。

修正したウィジェットの構成
修正したウィジェットの構成

解決方法

今回のケースの問題はProvider.of<Bloc>(context)のタイミングにあります。

Providercreateを呼ぶタイミングはProvider.of()が初めて呼ばれるタイミングであって、Providerを生成するタイミングではないようです。

今回のケースではBlocABlocBProvider.of()するウィジェットは画面内に存在していますが、BlocCProvider.of()するウィジェットは画面外にあります。

問題1よりウィジェットは画面内に入ってきて初めて生成されるようなので、BlocCProvider.of()するウィジェットは生成されておらず、そのためProvider.of<BlocC>(context)が呼ばれていないためBlocCが未生成となるようです。

この問題を解決する安直な方法として予めCustomScrollViewの外でProvider.of<BlocC>(context)をしておくことが考えられます。

ですが、Blocを必要としない場所でProvider.of()をすることはあまり良くないように思えます。

幸いProviderにはこの問題を解決してくれるlazyというパラメータが用意してありました。

lazyは注入したいインスタンスを遅延初期化するかどうかについてのパラメータで、何も指定しなければ遅延初期化するようです。

lazeにはboolを渡すことができ、laze = falseとすることで遅延初期化を無効化できます。

これにより全てのBlocを全て生成することができました。

おわりに

今回はウィジェットとProviderのライフサイクルについてまとめました。

今回のハマりを通し、ライフサイクルの理解度がフレームワークを使いこなす鍵になると改めて感じました