flutter項目結(jié)構(gòu)

工程結(jié)構(gòu)

flutter 工程目錄結(jié)構(gòu)

除了 flutter 本身的代碼、資源、依賴、配置文件之外,flutter 工程還包含了 Android 和 iOS 的工程目錄。
flutter 雖然是一個跨平臺開發(fā)方案,但需要一個容器最終運行到 Android 和 iOS 平臺上,所以 flutter 工程實際上就是一個同時內(nèi)嵌了 Android 和 iOS 原生子工程的父工程。我們在 lib 目錄下進行 flutter 代碼的開發(fā),而在某些特殊場景下的原生功能,則在對應(yīng)的 Android 和 iOS 目錄下提供相應(yīng)的代碼實現(xiàn),供對應(yīng)的 flutter 代碼引用。
flutter 會將相應(yīng)的依賴和構(gòu)建產(chǎn)物注入這 2 個子工程,最終集成到各自的項目中。而我們開發(fā)的 flutter 代碼,最終會以原生工程的形式運行。

工程代碼

flutter 自帶應(yīng)用模版,一個計數(shù)器示例。

  • 第一部分是應(yīng)用入口、應(yīng)用結(jié)構(gòu)以及頁面結(jié)構(gòu)。
  • 第二部分是頁面布局、頁面邏輯以及狀態(tài)管理。
    首先,第一部分的代碼,也就是應(yīng)用的整體結(jié)構(gòu):
import 'package:flutter/material.dart';
 
void main() => runApp(MyApp());
 
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) => MaterialApp(home: MyHomePage(title: 'Flutter Demo Home Page'));
}
 
class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}
 
class _MyHomePageState extends State<MyHomePage> {
  Widget build(BuildContext context) => {...};
}

MyApp 類繼承自 StatelessWidget 類,這也就意味著應(yīng)用本身也是一個 Widget。在 flutter 中,Widget 是整個視圖描述的基礎(chǔ),在 Flutter 的世界里,包括應(yīng)用、視圖、視圖控制器、布局等在內(nèi)的概念,都建立在 Widget 之上,F(xiàn)lutter 的核心設(shè)計思想便是一切皆 Widget。

Widget 是組件視覺效果的封裝,是 UI 界面的載體,因此我們還需要為它提供一個 build 方法,來告訴 flutter 框架如何構(gòu)建 UI 界面。

在 build 方法中,我們通常通過對基礎(chǔ) Widget 進行相應(yīng)的 UI 配置,或是組合各類基礎(chǔ) Widget 的方式進行 UI 的定制化。

MaterialApp 類是對構(gòu)建 material 設(shè)計風格應(yīng)用的組件封裝框架,里面還有很多可配置的屬性,比如應(yīng)用主題、應(yīng)用名稱、語言標識符、組件路由等。但是,這些配置屬性并不是本次分享的重點,如果你感興趣的話,可以參考 Flutter 官方的API 文檔,來了解 MaterialApp 框架的其他配置能力。

MyHomePage 是應(yīng)用的首頁,繼承自 StatefulWidget 類。這,代表著它是一個有狀態(tài)的 Widget(Stateful Widget),而 _MyHomePageState 就是它的狀態(tài)。

如果你足夠細心的話就會發(fā)現(xiàn),雖然 MyHomePage 類也是 Widget,但與 MyApp 類不同的是,它并沒有一個 build 方法去返回 Widget,而是多了一個 createState 方法返回 _MyHomePageState 對象,而 build 方法則包含在這個 _MyHomePageState 類當中。

那么,StatefulWidget 與 StatelessWidget 的接口設(shè)計,為什么會有這樣的區(qū)別呢?

這是因為 Widget 需要依據(jù)數(shù)據(jù)才能完成構(gòu)建,而對于 StatefulWidget 來說,其依賴的數(shù)據(jù)在 Widget 生命周期中可能會頻繁地發(fā)生變化。由 State 創(chuàng)建 Widget,以數(shù)據(jù)驅(qū)動視圖更新,而不是直接操作 UI 更新視覺屬性,代碼表達可以更精煉,邏輯也可以更清晰。

在了解了計數(shù)器示例程序的整體結(jié)構(gòu)以后,我們再來看看這個示例代碼的第二部分,也就是頁面布局及交互邏輯部分。

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  void _incrementCounter() => setState(() {_counter++;});
 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(Widget.title)),
      body: Text('You have pushed the button this many times:$_counter')),
      floatingActionButton: FloatingActionButton(onPressed: _incrementCounter) 
    );
  }

_MyHomePageState 中創(chuàng)建的 Widget Scaffold,是 Material 庫中提供的頁面布局結(jié)構(gòu),它包含 AppBar、Body,以及 FloatingActionButton。

AppBar 是頁面的導(dǎo)航欄,我們直接將 MyHomePage 中的 title 屬性作為標題使用。
body 則是一個 Text 組件,顯示了一個根據(jù) _counter 屬性可變的文本:‘You have pushed the button this many times:$_counter’。
floatingActionButton,則是頁面右下角的帶“+”的懸浮按鈕。我們將 _incrementCounter 作為其點擊處理函數(shù)。
_incrementCounter 的實現(xiàn)很簡單,使用 setState 方法去自增狀態(tài)屬性 _counter。setState 方法是 Flutter 以數(shù)據(jù)驅(qū)動視圖更新的關(guān)鍵函數(shù),它會通知 Flutter 框架:我這兒有狀態(tài)發(fā)生了改變,趕緊給我刷新界面吧。而 Flutter 框架收到通知后,會執(zhí)行 Widget 的 build 方法,根據(jù)新的狀態(tài)重新構(gòu)建界面。

這里需要注意的是:狀態(tài)的更改一定要配合使用 setState。通過這個方法的調(diào)用,F(xiàn)lutter 會在底層標記 Widget 的狀態(tài),隨后觸發(fā)重建。于我們的示例而言,即使你修改了 _counter,如果不調(diào)用 setState,F(xiàn)lutter 框架也不會感知到狀態(tài)的變化,因此界面上也不會有任何改變(你可以動手驗證一下)。
整個計數(shù)器示例的代碼流程示意圖

代碼流程示意圖

MyApp 為 Flutter 應(yīng)用的運行實例,通過在 main 函數(shù)中調(diào)用 runApp 函數(shù)實現(xiàn)程序的入口。而應(yīng)用的首頁則為 MyHomePage,一個擁有 _MyHomePageState 狀態(tài)的 StatefulWidget。_MyHomePageState 通過調(diào)用 build 方法,以相應(yīng)的數(shù)據(jù)配置完成了包括導(dǎo)航欄、文本及按鈕的頁面視圖的創(chuàng)建。

而當按鈕被點擊之后,其關(guān)聯(lián)的控件函數(shù) _incrementCounter 會觸發(fā)調(diào)用。在這個函數(shù)中,通過調(diào)用 setState 方法,更新 _counter 屬性的同時,也會通知 Flutter 框架其狀態(tài)發(fā)生變化。隨后,F(xiàn)lutter 會重新調(diào)用 build 方法,以新的數(shù)據(jù)配置重新構(gòu)建 _MyHomePageState 的 UI,最終完成頁面的重新渲染。

Widget 只是視圖的“配置信息”,是數(shù)據(jù)的映射,是“只讀”的。對于 StatefulWidget 而言,當數(shù)據(jù)改變的時候,我們需要重新創(chuàng)建 Widget 去更新界面,這也就意味著 Widget 的創(chuàng)建銷毀會非常頻繁。

為此,F(xiàn)lutter 對這個機制做了優(yōu)化,其框架內(nèi)部會通過一個中間層去收斂上層 UI 配置對底層真實渲染的改動,從而最大程度降低對真實渲染視圖的修改,提高渲染效率,而不是上層 UI 配置變了就需要銷毀整個渲染視圖樹重建。

這樣一來,Widget 僅是一個輕量級的數(shù)據(jù)配置存儲結(jié)構(gòu),它的重新創(chuàng)建速度非常快,所以我們可以放心地重新構(gòu)建任何需要更新的視圖,而無需分別修改各個子 Widget 的特定樣式。關(guān)于 Widget 具體的渲染過程細節(jié),我會在后續(xù)的第 9 篇文章“Widget,構(gòu)建 Flutter 界面的基石”中向你詳細介紹,在這里就不再展開了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容