Flutter 無疑的是一個高效率的跨平台解決方案。桌面、行動裝置的應用程式都可以使用它建置創新的軟體服務。在這個時間點,如果您剛要學習應用程式開發或者有跨平台的需求,Flutter 確實是值得推薦的方案之一。
隨著我們逐漸熟悉 Flutter ,學習最佳實踐有助於後續我們專案的實作。
清晰整潔的程式碼結構可以讓我們的專案維持一致性、增加可維護性,也可以避免冗余的重複甚至增加程式的複雜度。
命名是第一件常常被小看的問題,一些常見的建議:
變數和函數名應該描述性強,能清楚表達其用途。避免過短的名稱(如單個字母),除非在非常簡短的作用域內。使用工具或 IDE 的格式化功能來保持一致性。註解應該用來解釋為什麼以某種方式撰寫該程式碼,而不是重述程式碼執行的內容。移除任何未使用的程式碼。
這個部分跟大多數語言或框架並沒有太多的不同。
為您的專案設計一個合適的目錄結構也是一個重要步驟。它非常直觀的影響到可維護性和協作。雖然 Flutter 並沒有強制性的規範,但我們借鑒社群流行的結構不止可以學習大家共識的好方法,在協作時也比較容易了解。社群主流的方式概略有 2 種:
依照類型分類:這是最常見的方式。目錄和檔案根據功能或類型進行分類,例如所有「頁面畫面」都放在 screens
目錄下,資料模型在 models
等。
lib/
├── screens/
├── widgets/
├── services/
├── view_models/
└── ...
依照功能分類:這種方式適合大型項目,目錄根據功能進行組織。每個功能都有自己的目錄
lib/
├── feature_1/
│ ├── screens/
│ ├── widgets/
│ ├── models/
│ ├── services/
│ └── view_models/
├── feature_2/
│ ├── screens/
│ ├── widgets/
│ ├── models/
│ ├── services/
│ └── view_models/
└── ...
對於那些希望採用「依照功能」組織的開發者,社群甚至還有指令工具 - feature_folder_cli
另外,對於那些可能要跨專案重複使用的程式碼,可以擷取函式庫並發佈到 pub.dev。若您覺得多一層這樣的處理開發不太方便也可以:
lib/
└── packages/
├── my_pkg/
│ ├── lib/
│ └── pubspec.yaml
Flutter 應用程式開發的重要原則之一,就是要懂得靈活運用 Flutter 組件。這些組件是用來建構使用者介面、功能和外觀的關鍵。要開發出一個效能優異的應用,熟悉如何運用這些組件是非常重要的。「有效運用」指的是:
另外還有一些實作上的建議:
ListView.builder
。Key(UniqueKey().toString())
,對於可能重建的組件使用ValueKey(items[index].id),
。狀態管理的選擇和使用也能協助我們減少 Bug 、提升效能。我們前面已經介紹過了目前主流的套件。
一般來說,簡單的應用程式選擇 Provider 這樣簡單的技術;而比較大型複雜的應用則應該選擇 Redux、BLoC、Riverpod 等等當然這完全取決於個人。
Riverpod 的發展迅速,對於新的專案,特別是中大型專案,Riverpod 確實是一個好的選擇,但相比於一些更成熟的解決方案,其生態系和社群支持可能還不夠廣泛。
除了套件的選擇,通用的狀態管理最佳實踐如將「狀態」儲存在最低需要使用的階層,避免效能和傳遞問題。
優化應用程式效能是開發過程中重要的環節,一些建議如:
cached_network_image
除了網路讀取的圖片。如果可以,儘早開始測試。Flutter 提供了完整的測試工具包含我們之前介紹的單元測試等等,其他還有 Mockito、flame_test、或 Flutter Deive 都可以協助進行測試。
對於端對端測試或許您可以選擇 Appium - 跨平台 E2E 自動化測試工具除了平台支援 iOS、Android、Windows、macOS、網頁、React Native、tvOS 等等,撰寫語言支援 Java、Python、JavaScript、C#、Ruby
Flutter 3.24 引入了新的 Flutter GPU 若您有進階 3D 圖像處理可以嘗試。
儘量使用 Widget 而不是方法 Method。使用 Widget 可以獲得完整生命週期的好處,避免不必要的重新構建。
盡可能使用 const
關鍵字
使用 if 替代三元表示式
Column(
children: [
Text("平台"),
if (Platform.isAndroid) Text("安卓"),
if (Platform.isIOS) Text("蘋果"),
]
);
使用展開運算子,當資料已經儲存在另一個集合時:
// 建議
var x = [1, 2, 3];
var y = [4, 5, ...x];
// 避免
var x = [1, 2, 3];
var y = [4, 5, 6];
x.addAll(y);
使用級聯運算子 ..
,若要對一個物件進行一系列操作:
var path = Path()
..lineTo(0, size.height)
..lineTo(size.width, size.height)
..lineTo(size.width, 0)
..close();
優先使用 is
,而不是 as
。如果無法強制轉換,則 as
會發生例外。
// 建議
if (item is User) {}
// 避免
(item as User).name = 'Andy';
考慮優先使用 SizedBox 替代 Container 組件
使用 Null 安全運算子:??
和 ?.
,避免過長的 null 檢查。
// 建議
z = x ?? y;
// 避免
z = x == null ? y : x;
這裡單純儘量羅列一些建議,當然其中有些描述可能還需要進一步深入學習其知識。無論如何,這篇文章希望能讓我們在開發的時儘量優化我們的應用程式。