深入瞭解 GetX 的 Obx 與 Rx

閱讀時間約 7 分鐘


最近在做畫面時,滿常使用 GetX 的 Obx + Rx 變數,讓畫面可以根據狀態變化即時更新。過程中有時會碰到一些錯誤,在建置 Widget 的過程中,因為 Obx 找不到可以被監聽的目標,導致 Obx 認為使用者錯誤的使用了 Obx,所以透過 Exception 來提醒使用者。

raw-image

Obx 的使用方法是把一個 builder 方法傳給 Obx 這個 Widget,不需要任何 Rx 變數做為參數。像是下面的這個簡單的 count 例子中,Obx 傳入一個 builder 方法,只要 builder 在建置 Widget 的過程中使用 Rx 變數,Obx 就能進行監聽。

raw-image

這就讓我自己十分好奇,Obx 到底是如何找到 builder 方法中的 Rx 變數,然後對這個 Rx 變數進行監聽呢?自己花了一些時間研究和實驗,今天就來分享一下,到底 GetX 中的 Obx 是如何完成他的工作的。

兩位主角

在 Counter 例子中,有兩個重要的物件,一個是 Obx,另一個則是存放 count 的 Rx 變數。其中 Obx 身上有一個型態為 RxNotifier 的 _observer 變數,主要用來監聽 Rx 變數並更新畫面的。而存放 count 的 Rx 變數的爺爺也是 RxNotifier。

raw-image

RxNotifier 本身沒有任何實作,實作都是集中 NotifyManager 這個 mixin 身上。NotifyManger 身上主要有兩個方法

  1. addListener:用來決定監聽什麼事件
  2. listen:用來決定監聽到事件後,要做什麼事

Obx 如何更新畫面

當開始 build GetCountView 的畫面並 build 到 Obx 時,程式會先執行 ObxWidget initState() 方法。在 initState() 中執行 _observer.listen,並傳入 _updateTree,讓 _observer 監聽到事件時,可以呼叫 setState 更新畫面。

raw-image

再來程式會走進 ObxWidget 的 build 方法。可以發現 ObxWidget 的 build 方法也就只是轉頭呼叫 RxInterface 的 notifyChildren 靜態方法,並傳入 ObxWidget 身上的 _observer 和 widget.build

raw-image

此時的 widget.build 也就是我們在 GetCountView 中傳給 Obx 的那段印出 count 的 builder。

raw-image

當我們在深入 RxInterface.notifyChildren 之後,可以發現在程式會把 ObxWidget 身上的 _observer 塞到一個全域變數 RxInterface.proxy 中。然後繼續執行 builder 並 build 出顯示 count 數的 Text Widget。最後把 RxInterface.proxy 還原成原本的值,然後就回傳結果了。

raw-image

看到這邊好像還是不知道 Obx 到底是如何發現 builder 之中的 RxInt 的,我們只有看到 _observer 被賦予了他要觸發 setState 的工作,但是 _observer 是如何監聽 count 事件呢?

在 Getter 中註冊監聽

其實關鍵就在 builder 中。我們回頭看一下使用 Obx 那段程式碼,Text 中使用了 count.value,而關鍵就在這個 value getter 中。

raw-image

讓我們深入看一下 value 這個 getter 是如何被實作的。

raw-image

在使用 value getter 時,程式會向 RxInterface.proxy 這個全域變數註冊一個 subject,還記得先前 RxInterface.notifyChildren 做了什麼嗎,它把 ObxWidget 身上的 _observer 塞給 RxInterface.proxy,此時 value getter 中用的對象就是 ObxWidget 身上的 _observer。最後,當 value 發生變化時, Rx 變數就會透過 subject 通知 _observer。

簡單來說,Obx 與 Rx 變數是透過 RxInterface 這個全域變數作為橋樑,讓兩者可以在 build 的時候建立觀察者模式。當 Rx 變數透過 value setter 賦值時,就能成功通知 ObxWidget 身上的 _observer,_observer 接收到事件之後就觸發 setState(),讓畫面根據新的狀態更新。

整理流程

  1. GetCounterView 開始建置,隨後呼叫 ObxWidget 的 build 方法
  2. ObxWidget 的 build 方法被呼叫後,轉頭直接把工作轉交給 RxInterface.notifyChildren,並且傳入 _observer 與 builder
  3. RxInterface 先是把 _observer 放進 RxInterface.proxy 中
  4. RxInterface.notifyChildren 呼叫 builder,建置 builder 中的 Widget
  5. builder 在建置的過程中會使用 count 這個 Rx 變數取得 count 值
  6. 取得 count 值過程中,Rx 也會呼叫 RxInterface.proxy?.addListener,讓 _observer 可以監聽

raw-image

ps. 實際上並不是由 GetCountView 直接呼叫 ObxWidget.build 的,這邊只是簡化一下呼叫流程。實際上是由 GetCountView 的 Element 去呼叫的。

小結

GetX 作為熱門的 Flutter 狀態管理套件之一,有許多容易使用的 API,其中也大量的使用全域變數 。使用的時候還需要多加考慮一下,用得太多容易導致產品程式碼與套件緊緊相依,不只測試難寫,想抽換狀態管理的套件也會十分麻煩,使用的時候需要多多思考。

分享各種 Flutter 開發技巧
留言0
查看全部
發表第一個留言支持創作者!
本文探討如何在 Flutter 中自訂 Tab Bar 特效,提升使用者介面互動性。從基本的 Row 佈局開始,我們逐步實現選中 Tab 動態變化的需求。最後,使用 CustomMultiChildLayout 與 AnimatedSize 實現一個符合設計需求的 Tab Bar,提升整體使用體驗。
本文探討如何有效解決 Flutter 中 PageView 動畫與複雜畫面造成的卡頓問題。透過使用 Provider 優化效能,減少不必要的 Widget 重建,達成更流暢的使用體驗。本文提供範例程式碼及效能分析,讓開發者能夠理解並應用於實際產品中,從而改善應用的效能。
本文介紹如何解決 Flutter 應用程式中 PageView 的卡頓問題。透過使用 DevTools 的 Profile 模式及啟用 Track Widget Builds 功能,分析了 UI phase 的效能瓶頸,識別出 PlayerInfoGameLogView 重新建構的高成本。
Flutter Widget 能加速開發,但誤用 MediaQuery 可能導致不預期的重建。範例中,頁面因鍵盤觸發高度變動而刷新,隨機數重新生成。使用固定比例設計避免重建,顯示深入理解框架對穩定性的重要性。
本文探討如何在 Flutter 中自訂 Tab Bar 特效,提升使用者介面互動性。從基本的 Row 佈局開始,我們逐步實現選中 Tab 動態變化的需求。最後,使用 CustomMultiChildLayout 與 AnimatedSize 實現一個符合設計需求的 Tab Bar,提升整體使用體驗。
本文探討如何有效解決 Flutter 中 PageView 動畫與複雜畫面造成的卡頓問題。透過使用 Provider 優化效能,減少不必要的 Widget 重建,達成更流暢的使用體驗。本文提供範例程式碼及效能分析,讓開發者能夠理解並應用於實際產品中,從而改善應用的效能。
本文介紹如何解決 Flutter 應用程式中 PageView 的卡頓問題。透過使用 DevTools 的 Profile 模式及啟用 Track Widget Builds 功能,分析了 UI phase 的效能瓶頸,識別出 PlayerInfoGameLogView 重新建構的高成本。
Flutter Widget 能加速開發,但誤用 MediaQuery 可能導致不預期的重建。範例中,頁面因鍵盤觸發高度變動而刷新,隨機數重新生成。使用固定比例設計避免重建,顯示深入理解框架對穩定性的重要性。
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
就是指變數可以被訪問和使用的範圍,來說一下var、let和const的作用域差異。 var :function example() { console.log(x); // 輸出: undefined 因為變量提升造成的 var x = 5; } 函數作用域或全域作用域 可以重複宣告
Thumbnail
本文介紹了在網站開發中如何運用狀態機的原則和設計方法。通過具體案例分析,以及狀態和數據的區分,詳細介紹了狀態機的設計原則和應用。讀者可以通過本文瞭解如何將狀態機應用於實際的網站開發中。
Thumbnail
在網路速度有限的情況下,依序記錄不斷產生的資訊,能統計使用者在頁面上操作了哪些功能。
Thumbnail
瞭解如何在Xcode15及以上使用Logger進行更好的程式debug。Logger可以更好的組織Log,但也有一些缺點需要注意。本文將介紹Logger的基本使用方式,以及一些注意事項。
※ 觀察者模式 定義: 觀察者模式(Observer Pattern)是一種設計模式,涉及兩個主要角色:觀察者(Observers)和被觀察者(Subject)。在這種模式中,一群觀察者訂閱並觀察某個被觀察的對象。當被觀察者的狀態發生改變時,它會通知所有觀察者,讓他們知曉並作出相應的反應。這種模
Thumbnail
需求情境: 在設計畫面時,資料來源是後台的 api,每一次畫面細節的修修改改,都會觸發 Xcode Preview 程序,導致不斷呼叫後台。此時若資料結構和大小都具有一定規模,就會導致效率低落,不斷等待,且消耗伺服器資源甚鉅。 解決方案: 將後台傳回的資料以檔案形式暫存在本地端,每次 pr
setter和getter能把狀態改變時需做的事情包裝起來,讓外部只需簡單修改參數就能達到預想的效果
Thumbnail
當你在開發程式時,難免會遇到各種錯誤和異常情況。這些錯誤可能是因為代碼中的錯誤、外部資源無法訪問或其他不可預期的狀況。為了提高程式的可靠性、穩定性和可維護性,我們使用「例外處理」來處理這些異常情況。
Thumbnail
Request內容 package main import ( "fmt" "log" "net/http" "strings" ) func request(w http.ResponseWriter, r *http.Request) { //這些資訊是輸出到伺服器端的列印訊息
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
就是指變數可以被訪問和使用的範圍,來說一下var、let和const的作用域差異。 var :function example() { console.log(x); // 輸出: undefined 因為變量提升造成的 var x = 5; } 函數作用域或全域作用域 可以重複宣告
Thumbnail
本文介紹了在網站開發中如何運用狀態機的原則和設計方法。通過具體案例分析,以及狀態和數據的區分,詳細介紹了狀態機的設計原則和應用。讀者可以通過本文瞭解如何將狀態機應用於實際的網站開發中。
Thumbnail
在網路速度有限的情況下,依序記錄不斷產生的資訊,能統計使用者在頁面上操作了哪些功能。
Thumbnail
瞭解如何在Xcode15及以上使用Logger進行更好的程式debug。Logger可以更好的組織Log,但也有一些缺點需要注意。本文將介紹Logger的基本使用方式,以及一些注意事項。
※ 觀察者模式 定義: 觀察者模式(Observer Pattern)是一種設計模式,涉及兩個主要角色:觀察者(Observers)和被觀察者(Subject)。在這種模式中,一群觀察者訂閱並觀察某個被觀察的對象。當被觀察者的狀態發生改變時,它會通知所有觀察者,讓他們知曉並作出相應的反應。這種模
Thumbnail
需求情境: 在設計畫面時,資料來源是後台的 api,每一次畫面細節的修修改改,都會觸發 Xcode Preview 程序,導致不斷呼叫後台。此時若資料結構和大小都具有一定規模,就會導致效率低落,不斷等待,且消耗伺服器資源甚鉅。 解決方案: 將後台傳回的資料以檔案形式暫存在本地端,每次 pr
setter和getter能把狀態改變時需做的事情包裝起來,讓外部只需簡單修改參數就能達到預想的效果
Thumbnail
當你在開發程式時,難免會遇到各種錯誤和異常情況。這些錯誤可能是因為代碼中的錯誤、外部資源無法訪問或其他不可預期的狀況。為了提高程式的可靠性、穩定性和可維護性,我們使用「例外處理」來處理這些異常情況。
Thumbnail
Request內容 package main import ( "fmt" "log" "net/http" "strings" ) func request(w http.ResponseWriter, r *http.Request) { //這些資訊是輸出到伺服器端的列印訊息