Diverse developer blog

株式会社Diverse(ダイバース) 開発者ブログです。

【後編】イベント『Flutter開発者必見!あの有名ライブラリの内部実装を解説』を公開します【Meetup #2】

こんにちは!Diverse広報担当です!

先日、Diverse Meetup #2となる『Flutter開発者必見!あの有名ライブラリの内部実装を解説』を開催いたしました。 本ブログではイベントの様子をお伝えしております。 f:id:diverse-tech:20210511121408j:plain

今回は、前編・中編に続いて、いよいよラストの「コードリーディング」の内容をお届けいたします!

【前編】「事前知識の確認」と【中編】「widgetとElementの関係〜providerの登場人物」をまだご覧になっていない方はまずこちらをご覧ください。

developer.diverse-inc.com

developer.diverse-inc.com



▼目次


当日の資料と動画を公開中!

当日の資料と前編・中編の動画を公開! 資料の36ページから後編の内容となっておりますが、 コードリーティングは画面を共有しながら解説しておりますので、ぜひ動画の方もご覧ください!!

Diverse Meetup #2 前編の動画はこちら
youtu.be Diverse Meetup #2 中編の動画はこちら
youtu.be

続・イベントルポ大公開【後編】

前編・中編に続き、いよいよラストの後編です!

▼一緒にコードリーディングをやってみよう!(00:10

f:id:diverse-tech:20210511121637p:plain

トピックをいくつか用意していたのですが、 今回は「ChangeNotifierProvider のライフサイクルについて」見ていきたいと思います!

f:id:diverse-tech:20210603105744p:plain

ChangeNotifierProvider は lib の src の change_notifer_provider.dart の中にあります。 ChangeNotifierProvider<T> 自体はListenableProvider<T> というのを継承しています。

ChangeNotifierProvider<T> の中身の実態は、 ListenableProvider<T> が ほとんど持っていると思っていただければ良いと思います。

特殊な点としては、_dispose というのを提供していることだけです。 _dispose は、ChangeNotifier の dispose を呼び出すだけのものです。 それを親の ListenableProvider<T> にコンストラクターから渡しています。

f:id:diverse-tech:20210511123140p:plain

ListenableProvider<T> の方を見に行きますと、これもですね ListenableProvider <T> は 18行目から始まって65行目で終わっています。 これもあまり内容がなく、実態は親が持っていそうです。

親は InheritedProvider<T> で ListenableProvider<T> から親に対して渡しているのは _startListening というのがちょっと特殊なくらいです。

f:id:diverse-tech:20210511123335p:plain

_startListeningはここに内容があります。

Value として Listenable を持って addListener をして 戻り値として VoidCallback型で removeListener というのを返しているのでこの戻り値の VoidCallback を呼び出せば Listening が外れるようというそんな関数を返しているようです。

f:id:diverse-tech:20210511123747p:plain

中身でやってることは、 InheritedContext の markNeedNotifyDependents というのを Listener に登録する ということをしています。もう少し中身を追ってみないと詳しいいことはわかりませんが 名前からしておそらく変更が通知されたことを依存しているものに 通知するということを行っているということがわかります。

では、_startListening で渡されている InheritedContext のことは頭の片隅に置きつつ ライフサイクルの大まかなところを 追っていきましょう!

▼ChangeNotifierProviderのライフサイクルを追ってみよう!(02:48

f:id:diverse-tech:20210511123922p:plain

InheritedProvider<T> の中身を見ていきます!
InheritedProvider<T> は長いです。 52行目から始まって168行目まであるので これが本丸だとということがわかりいただけるかと思います。

f:id:diverse-tech:20210511124102p:plain

ライフサイクルについては _Delegate というのが管理しています。

_CreateInheritedProvider というのが _delegate という引数に代入されているので これが _Delegate だということは想像できるかなと思います。

f:id:diverse-tech:20210511125211p:plain

実際にコードジャンプで飛んでみますと _Delegate を継承していますね。

なので _CreateInheritedProvider<T> の中身を見ていけば どんなライフサイクルを辿っているのかということがわかります。ただ、これが Element からライフサイクルが _Delegate されているのものであるというところを見にいきたい場合は InheritedProvider<T> の中身を見にいく必要があります。

f:id:diverse-tech:20210511125755p:plain

今回は ChangeNotifier のライフサイクルを どんな風にして呼び出しているのか この _Delegate の中身を見ていきたいと思います!

f:id:diverse-tech:20210511125854p:plain

まず create というのがあって これは create<T> 型関数というのは、 BuildContext を引数にとってT型を返すような関数になっています。

f:id:diverse-tech:20210511130015p:plain

update に関してはT 型を返すような引数に BuildContxt と前の値を取る関数なので以前の値をどのように変更して返すかということを指定する関数だったと記憶してます。

先ほど ListenabeProvider<T> と ChangeNotifierProvider<T>で startListening とdispose の関数が特徴という話をしました。これらに注目してライフサイクルだけ追いかけていきましょう!

f:id:diverse-tech:20210603110147p:plain

startListening は StartListening<T>型関数を期待しています。

これは void Function() を返す Function、 そして引数に InheritedContext<T>値である Value をとる というものなので、先ほどの ListenableProvider<T>の _startListening と 型が一致しているということがわかります。

それから dispose の方ですが、 dispose はChangeNotifierProvider<T> の中にあります。 dispose とシグネチャーが一致していることはわかると思います。

これらがどこで呼ばれているのかというのを見ていきましょう!

f:id:diverse-tech:20210511130609p:plain

まず startListening の方からいきますと、頻繁に呼ばれている場所を見つけました。

2番目にある CreateInheritedProviderState<T> の中に飛ぶと、State のライフサイクルの中で ChangeNotifier のインスタンスを生成するということをやっているのがわかります。

f:id:diverse-tech:20210511131115p:plain

_CreateInheritedProviderState<T> の中身を見ていきます。 先ほどの startListening が呼ばれているメソッドがどこなのかというのを調べると Value というゲッターに突き当たりました。

DelegateState の Value というのが呼ばれるとその中で値が生成されて管理されるということになるようです。

f:id:diverse-tech:20210511131939p:plain

_removeListener が null である時に実行されるので既に Listen 済かどうかというのは _removeListner が null か null じゃないかで確認しているようです。

この Value がいつ呼ばれているのかを見に行くと InheritedProviderElement の中身になるので、まずは dispose の方に寄り道して見て行きましょう。

f:id:diverse-tech:20210511132215p:plain

dispose の方はどこで呼ばれているのかというと dispose というメソッドの中で呼ばれているというのがあるのでそこを見にいきます。

f:id:diverse-tech:20210511132345p:plain

こちらは CreateInheritedProvider という Delegate の中身です。それが dispose() というメソッドを持っていて、その中で ChangeNotifier の dispose も呼んでいるという形になっていそうです。

では、これらがどんなライフサイクルで呼ばれているのかというのを見に行ってみましょう!

_DelegateState<T> の Value と dispose の方ですね。 それがどこで呼ばれているのかというのは親クラスの _DelegateState<T, D> をまず見に行きまして、その Value がどこで呼ばれているのかをまず見ていきます。

f:id:diverse-tech:20210511133631p:plain

InheritedProviderScopeElement<T>のメンバーにあるゲッターの Value というのは _DelegateState の Value を呼び出していることがわかります。

なので先ほどの ChangeNotifierProvider<T>に関しては、
CreateInheritedProvider が生成した CreateInheritedProviderState というのがここに入ってくるのでその Value を呼び出して ChangeNotifier をこの中で生成するということをしているようです。

f:id:diverse-tech:20210511133748p:plain

その Value を呼び出している箇所はと言うと、見て行きたいのはこの select というところ中身と、notifyDependentの中身、buildの中身、ということになります。

f:id:diverse-tech:20210511150834p:plain

まず一番上は select というメソッドがあって、その中身でも Value を呼び出して何もしなければ ChangeNotifierProvider<T>を作成するということをやっているようです。

f:id:diverse-tech:20210511152048p:plain

これは SelectContext というエクステンションの中身のようです。 使い方をみると buildContext のエクステンションメソッドなので context.select とすると、そのPersonのnameだけ変更された時にここの build メソッドの中身をリビルドすることが出来ます。

f:id:diverse-tech:20210511152119p:plain

2つ目がこちらのnotifyDependent という InheritedProviderScopeElemen<T>のメソッドです。

f:id:diverse-tech:20210511153006p:plain

notifyDependent は名前からして依存しているものに対して状態を通知すというようなものになっていそうです。

f:id:diverse-tech:20210511153208p:plain

元に飛びますと Flutter のフレームワーク中に出てきました。 これはどのクラスかというのを上に辿って見に行きますと、InheritedElement のメソッドが元になっているようです。

f:id:diverse-tech:20210511153250p:plain

InehritedElement というのは InheritedWidget などで使われている Element にあたるということなので、ここまで来るとFlutter の中身になります。

f:id:diverse-tech:20210511153323p:plain

中身としては依存しているものに対してdidChangeDeoendencie、 依存対象のものが変更されたよ!という通知するメソッドを呼び出すだけです。それを InheritedProvider は継承した上で変更しています。

ライフサイクルだけを追っかけたいので変更通知するメソッドが呼ばれた時に ChangeNotifier が生成されたりするということが分かればとりあえず今回は良しとしましょう!

f:id:diverse-tech:20210511153402p:plain

このあたりはassertion だとかデバッグ用の出力が多かったりするので中身が追いづらいです・・・。

f:id:diverse-tech:20210511153437p:plain

もう1箇所 ChangeNotidfire が生成されるような Value が呼ばれる場所があります。

f:id:diverse-tech:20210511153508p:plain

InheritedProviderScopeElement<T>の build() です。なので、StatefulWidget の State の中の build みたいなものだと思っていただければ良いかと思います。

そこの中でもし InheritedProviderScop Element<T>のオーナーが _lazy というプロパティを抱えていて、それが false だった場合は build メソッドが呼ばれたタイミングで初期化を走らせたりここで値の計算を強制的に行わせると書かれています。

その build のタイミングでもChangeNotifier を生成するということをしているようです。

どんなライフサイクルで ChangeNotifier が生成されるのか、dispose されるのかはだいたい追えたかと思いますが、ご理解いただけましたでしょうか?

f:id:diverse-tech:20210511132928p:plain

資料の関係図(p31 ~p34)を見ながらだとコードジャンプした時もどこに何があるのかと把握しやすいかなと思うので、もしお時間あればご自分でも見ていただけると色々と楽しめると思います。

さいごに

これでDiverse Meetup #2となる『Flutter開発者必見!あの有名ライブラリの内部実装を解説』のイベントルポは終了です!

最後までお付き合いいただきありがとうございました!


▼Diverseの情報発信について

Diverseはこのようなイベントの他にも各所で情報発信を行っています。興味のある方はぜひ覗いてください!

www.youtube.com

qiita.com

www.wantedly.com