Day 7 - 跨元件分享狀態
在前幾天的 Game List 的例子中,我們消除了 onTap 層層傳遞的問題。但是,如果我們仔細觀察的話,會發現 Game 也是同樣的情況,只要底層的哪個 Widget 需要 Game,中間層的 Widget 就需要當中間人,代為傳遞參數。

中間的 Widget 傳遞這個參數,自己卻是用不到,這個問題在上一張討論如何傳遞 callback 時也有討論到。今天就來聊聊如何簡化設計。
InheritedWidget
Flutter 有提供許多基礎類型的 Widget,例如:StatelessWidget, StatefulWidget,而其中有一類 Widget 稱作 InheritedWidget。透過 InheritedWidget,我們能有效地讓子層可以跨過父層,直接存取 InheritedWidget。使用這個特性,我們就能避免層層傳遞 Game 的問題了。


經過修改之後,在上面的程式碼中,我們在頂層使用 GameInfo 這個 InheritedWidget,讓 GameInfoSection 和 GameActionSection 自己去取得 GameInfo,最後 GameItemView 也不需要幫忙傳遞 Game 了。
Provider
當我們每次想要在 Widget Tree 中,縱向的分享資料時,我們都得寫一個 Widget 並讓他繼承 InheritedWidget,但是其實在 Flutter 眾多套件中,有一個能幫助我更簡單的使用 InheritedWidget,那就是 Provider。


可以發現使用 Provider 之後的程式碼,簡潔非常多,不需要使用繼承,也不需要特別定義一個新的 Widget,直接使用 Provider 並放入 game 就好。由於 Provider 裏頭其實就是 InheritedWidget,透過 Provider 我們能用組合的方式去使用 InheritedWidget,而不是繼承,想比喻繼承,我們傾向於使用組合,也是一個有名的規則:以組合取代繼承。
值得一提的是,InheritedWidget 是狀態管理的基礎,所以也使得作為 InheritedWidget 的 Wrapper,Provider 也被大量套件引用,其中最知名的就是 Bloc 了。
用 Provider 傳遞按鈕事件
回想前兩天的主題,按鈕傳遞我們也能把 callback 放在 Provider,當子 Widget 渲染時,從 Provider 身上讀取 callback。但其實這個作法並不太合適,因為當我們從 Provider 讀取 callback 時,也意味著使用這個按鈕必須提供相應的 Provider,即使用他的人不需要按鈕有行為。大多時候,使用 Provider 傳遞狀態會比較合理,因為畫面顯示與資料是一致的,不同的資料會有不同的畫面。相比於資料,按鈕行為就很可能會因為畫面不同,而有不同的功能,所以使用 Provider 來傳遞按鈕事件,就會降低按鈕元件通用性。
結論
InheritedWidget 讓我們可以往子層 Widget 共享資料,避免過多的傳遞參數,畢竟沒有參數的方法或物件最好用。雖然 InheritedWidget 好用,但是在實戰中,我們還是傾向於使用 Provider,畢竟組合還是比繼承要來得有彈性。