哈嘍,我是老劉
老劉做Flutter開發六年多了,經手過的項目很多,但已有項目中途接手的情況並不算多。
恰巧近期經手兩個中途接棒的外包項目,有一些感觸分享給大家。
這兩個項目都是老劉組織人手幫外地的公司用Flutter做App開發業務的。其中一個是短期項目屬於工業領域的App和AI還沾點邊。另一個是長期項目,屬於金融領域的。
但是這兩個項目都呈現出典型的開發者為實現快速交付,在項目中堆砌了大量三方庫和臨時拼湊的代碼。
作為一個開發者,我很能理解當初這些開發者的心理。
還記得我剛學Android開發的時候,需要實現一個列表頁,每個列表項現在想想都很簡單。
但我當時一門心思就想着找到一個現成的三方庫,能有一個組件自動包含需求裏面的佈局。
因為對當時的我來説,實現一個自定義的佈局item是挺複雜的事情。
那麼接下來我們先來説説這個現象本身,然後討論一下如何做正確的技術選型,尤其是在一個項目開始的時候。
一、被三方庫綁架的技術架構
在這兩個Flutter項目中,開發者幾乎為每個功能模塊都引入了獨立的三方庫:從路由管理到網絡請求,從狀態管理到UI組件,甚至基礎的工具函數都依賴外部庫實現。這種"拿來主義"導致項目呈現出明顯的拼湊感:
- 路由系統深度綁定第三方框架
- 狀態管理混用Provider/GetX/Riverpod等多套方案
- 基礎工具類直接照搬pub.dev搜索結果前三條的庫
這種開發方式看似快速高效,實則會埋下嚴重的技術隱患。
項目最終變成了三方庫的"縫合怪",開發者的核心工作變成了在不同庫的文檔和API之間疲於奔命。
二、過度依賴的隱形成本
當然這種現象和項目人員不穩定有直接的關係。因為不需要長期維護,開發完可能就走人了,所以很多人選擇自己最熟悉開發速度最快的方案。至於架構設計、公共功能的抽象封裝大概率是沒有的。
但是一旦項目需要長期迭代下去,隨着各種各樣需求的加入,前期圖省事的隱形成本就開始逐漸顯露出來了。
- 維護風險
很多三方庫是個人或者小團隊維護,生命週期比較短。
很可能你的項目正如火如荼呢,人家庫就停更了。 - 定製化困境
在需要擴展功能時(如給路由系統增加埋點能力),發現三方庫的抽象層級過高,難以在不修改庫源碼的情況下實現需求。
老劉的團隊使用TDD開發模式,對定製化方面的要求會更高一些。 - 調試黑洞
很多開發引入三方庫的目的就是為了減少工作量,因此更不會去關係三方庫本身的實現邏輯。
但是一旦碰到bug定位,尤其是需要深入到三方庫內部定位的場景,就會非常被動。 - 技術債利滾利
像狀態管理、路由管理這樣的核心功能,一旦定下來後期更改的成本是非常高的。
如果一開始隨便選擇了一個,後期發現不合適自己的項目,需要付出極大的開發和測試成本才能更換。
瞭解了這些問題,接下來我們看看如何在項目初期進行正確的技術選型。
三、正確的技術選型方法
- 基礎建設自研原則
Flutter SDK的很多功能大家用起來覺得不方便一個很大的原因是這些功能和組件都是針對多種不同的場景設計的。
Flutter需要支持各種不同類型的app開發,所以組件就需要提供大量的可配置參數來滿足不同場景的需求。
所以實際上很多組件並不是讓大家直接使用的,而是讓大家基於這些可配置的參數進行二次封裝。
實際項目中應該使用的主要是這些二次封裝後的組件。
舉個例子,我們App中最常見的對話框。
一個App中的對話框通常會有固定的風格,比如背景色、按鈕顏色、圓角、字體、動畫等等。
我們基於Flutter自帶的對話框組件,將這些通用的風格封裝成項目中公共的對話框組件。
在業務邏輯的開發中只需要傳入具體對話框的標題、內容和確認按鈕的點擊回調即可展示對話框。
對於Flutter SDK已提供良好支持的基礎功能(如路由、Isolate通信、PlatformChannel等),更應該基於原生API進行二次封裝。例如:
// 自定義路由封裝示例
class AppNavigator {
static Future<T?> push<T>(BuildContext context, AppRoute route) {
return Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (_, __, ___) => route.builder(),
transitionsBuilder: (_, a, __, child) =>
_buildTransition(a, child, route.transition),
),
);
}
static TransitionBuilder _buildTransition = (...) {...}
}
作為這個封裝的使用者,用起來是不是和很多三方的路由庫很類似?
但是所有代碼都是你可控的部分。
- 三方庫引入評估矩陣
基礎設施自研不代表不使用三方庫,很多獨立的功能使用封裝好的三方庫的確能大幅提高開發效率。
但是在引入三方庫時需要遵循一定的標準:
- 維護活躍度(最近6個月commit頻率)
- 測試覆蓋率(不低於80%)
- 依賴層級(是否帶來額外依賴)
- 代碼質量(關鍵類的設計合理性)
- 逃生通道(能否方便地替換/移除)
當然,每個項目的情況都不一樣,應該根據自己項目的具體情況進行篩選標準的增減。
- 抽象隔離設計
選擇合適的三方庫後也不建議直接引入項目中使用。
特別是對於關鍵模塊來説,對引入的三方庫建議進行封裝。
下面以比較常見的網絡庫dio為例説明:
// 網絡庫抽象示例
abstract class HttpClient {
Future<Response> get(String url);
}
// Dio實現
class DioClient implements HttpClient {
final Dio _dio = Dio();
@override
Future<Response> get(String url) => _dio.get(url);
}
// 封裝代理
class AppHttpClient extends HttpClient {
static final instance = DioClient();
@override
Future<Response> get(String url) async {
// 統一添加攔截器、日誌等
}
}
我們將底層使用dio的部分封裝在HttpClient的某一個實現類中。
這樣即使後續想要切換網絡庫,也只需要增加一個具體的實現類即可。
甚至可以實現多個網絡請求庫並存、隨意切換。
實現對不同網絡請求庫進行灰度測試和容錯備份等場景。
- 核心能力沉澱
隨着項目的推進,一定會積累很多通用的工具和方法。
把這些內容提取出來,文檔化、獨立化。就可以推廣到團隊的其他項目中。
例如建立項目自身的utils庫,將經過驗證的工具函數沉澱為內部依賴:
# pubspec.yaml
dependencies:
core_utils:
path: ../core_utils
四、架構設計的平衡之道
前面提到的問題更多的是技術選型與封裝。
而在這兩個項目中,關於架構設計也不得不提一下。
試想你們團隊來了一個新人,如何讓他能快速掌握現有項目代碼的脈絡?如何能快速理解自己開發工作該如何進行模塊拆分,該把不同代碼放到哪裏去?
這個一方面要有健全的文檔系統,另一方面就要有一個簡介清晰的架構體系。
老劉自己帶的項目在Flutter上通常會使用bloc做狀態管理,並且基於bloc封裝頁面、業務邏輯等功能模塊的基類。
而這些中途接手的項目就比較麻煩,早期代碼相對混亂和堆砌。
- 有些使用Getx,但是用的不太對。
- 有些自己做了簡單的UI和業務邏輯的拆分。
- 還有些甚至就直接都寫在了一起。
在這樣的項目中再引入bloc無疑會增加混亂度和複雜度。
所以老劉這次的思路是不引入額外的三方庫,基於Flutter自身的ChangeNotifer和已經使用的Provider做一個簡單的架構封裝。
具體的封裝方法老劉單獨寫一篇文章詳細説明,這裏只介紹一下基本原則。
1、狀態管理的核心目標是關注點分離
業務邏輯與UI呈現必須解耦。
通過分層架構將網絡請求、本地存儲等副作用操作封裝在獨立的Service層。
View層僅負責數據展示和事件傳遞。
狀態變更通過ChangeNotifier通知,配合Provider實現精準的局部刷新。
因此基於ChangeNotifier封裝協調View和底層Service的控制器。
2、單向數據流必須嚴格把控
禁止在Widget中直接修改狀態。
事件處理流程遵循"View觸發-> Controller協調 -> Service執行業務 -> 更新Notifier狀態"的閉環,確保狀態變更路徑可追溯。
3、組件間狀態共享分層治理
通過Provider的多層級注入機制,將全局狀態(如用户信息)通過頂層Provider或者單例進行維護。
局部狀態(如頁面級數據)通過頁面及的Provider限定作用域。
這套方案很簡單,甚至可以説是簡陋,只封裝了BasePage和BaseController兩個基類。
但是在中型項目規模下,開發效率與代碼可維護性可以達到較好平衡。
對於需要快速迭代的業務場景,這種輕量化架構可能比過度設計的方案更具生命力。
這裏要説明一下,架構設計不能簡單的等同於狀態管理。
但是狀態管理是日常開發中使用最頻繁,對開發效率和質量影響最大的部分。
因此單獨拿出來做一個快速的封裝,其它部分可以後續逐步補充完整。
五、開發者成長啓示
過度依賴三方庫本質上是技術深度不足的表現。
當我們將每個三方庫的引入都視為一次架構決策,而非簡單的CTRL+C/V時,項目自然會呈現出優雅健壯的特質。
畢竟,真正的技術實力不在於知道多少庫,而在於能否用最簡潔可靠的方案解決問題。
這或許就是Flutter哲學中"Everything is a widget"給我們最深刻的啓示——簡單,但足夠強大。
如果看到這裏的同學對客户端開發或者Flutter開發感興趣,歡迎聯繫老劉,我們互相學習。
點擊免費領老劉整理的《Flutter開發手冊》,覆蓋90%應用開發場景。
可以作為Flutter學習的知識地圖。
覆蓋90%開發場景的《Flutter開發手冊》