從零開始的Flutter之旅: StatelessWidget

午後一小憩發表於2020-03-07

從零開始的Flutter之旅: StatelessWidget

這次要展示的是什麼是Flutter的Widget,即小部件;以及如何在Flutter中使用StatelessWidget,即無狀態小部件。

至於Flutter,通俗的講是開發者可以通一套簡單的程式碼來同時構建Android與IOS應用程式。

特性

小部件是Flutter應用程式的基本構建模組,每一個都是不可變的宣告,也是使用者介面的一部分。例如button,text,color以及佈局所用到的padding等等。

下面我們來看flutter_github中的一個例項。

從零開始的Flutter之旅: StatelessWidget

圈選中的item只有兩個資訊,頭像與名稱。為了避免程式碼的重複使用,將其抽離成一個獨立的widget,具體程式碼如下

class FollowersItemView extends StatelessWidget {
  final GestureTapCallback tapCallback;
  final String avatarUrl;
  final String name;
 
  const FollowersItemView(
      {Key key, this.avatarUrl, this.name, this.tapCallback})
      : super(key: key);
 
  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.symmetric(horizontal: 15.0),
      child: GestureDetector(
        behavior: HitTestBehavior.opaque,
        onTap: tapCallback,
        child: Column(
          children: <Widget>[
            Row(
              children: <Widget>[
                FadeInImage.assetNetwork(
                  placeholder: 'images/app_welcome.png',
                  image: avatarUrl,
                  width: 80.0,
                  height: 80.0,
                ),
                Expanded(
                  child: Padding(
                    padding: EdgeInsets.only(left: 15.0),
                    child: Text(
                      name,
                      overflow: TextOverflow.ellipsis,
                      maxLines: 1,
                      style: TextStyle(
                        color: Colors.grey[600],
                        fontSize: 20.0,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                  ),
                )
              ],
            ),
            Padding(
              padding: EdgeInsets.symmetric(vertical: 15.0),
              child: Divider(
                thickness: 1.0,
                color: colorEAEAEA,
                height: 1.0,
                endIndent: 0.0,
              ),
            ),
          ],
        ),
      ),
    );
  }
}
複製程式碼

它繼承於StatelessWidget,StatelessWidget的特性是無狀態,資料不可變化。這個性質正好符合我們將要抽離的部件。抽離的部件需要做頭像與名稱的展示,沒有任何形式上的互動變化。唯一的一個互動也是點選,但它並沒有涉及資料的改變。所以在程式碼中將這些資料定義成final型別。本質就如Text部件,並沒有如輸入文字或者帶有動畫的部件一樣隨著時間內部屬性會有所變化。

既然沒有任何變化,那麼我們也可以將其建構函式定義為const型別。

有了上面的部件抽離,我們就可以直接在ListView中使用該無狀態部件

  @override
  Widget createContentWidget() {
    return RefreshIndicator(
      onRefresh: vm.handlerRefresh,
      child: Scrollbar(
        child: ListView.builder(
            padding: EdgeInsets.only(top: 15.0),
            itemCount: vm.list?.length ?? 0,
            itemBuilder: (BuildContext context, int index) {
              final item = vm.list[index];
              return FollowersItemView(
                avatarUrl: item.avatar_url,
                name: item.login,
                tapCallback: () {
                  Navigator.push(context, MaterialPageRoute(builder: (_) {
                    return WebViewPage(title: item.login, url: item.html_url);
                  }));
                },
              );
            }),
      ),
    );
  }
複製程式碼

在ListView中引用FollowItemView,並傳入不變的資料即可。

呈現原理

現在StatelessWidget的使用大家都會了,那它是如何呼叫的呢?

下面我們來看下它的呈現原理。

正如開頭所說的將小部件作為Flutter應用構建的基礎,在Flutter中我們將小部件的構建稱作為Widget Tree,即小部件樹。它就像是應用程式的藍圖,我們將藍圖建立好,然後內部會通過藍圖去建立對應顯示在螢幕上的element元素。它包含了藍圖上對應的小部件的配置資訊。所以對應的還有一個Element Tree,即元素樹。

每一個StatelWidget都有一個StatelessElement,內部會通過createElement()方法進行建立其例項

  @override
  StatelessElement createElement() => StatelessElement(this);
複製程式碼

同時在StatelessElement中會通過buid()方法來獲取StalessWidget中所構建的藍圖Widget,並將元素顯示到螢幕上。

Widget Tree與Element Tree之間的互動如下

從零開始的Flutter之旅: StatelessWidget

FollowerItemView中的StatelessElement會呼叫build方法來獲取它是否有子部件,如果有的話對應的子部件也會建立它們自己的Element,並把它安裝到元素樹上。

所以我們的程式有兩顆對應的樹,其中一顆代表螢幕上顯示的內容Element;另一顆樹代表其展示的藍圖Widget,它們由許多的小部件組成。

而我們開發人員所做的就是將這些不同的小部件構建成我們所需要的應用程式。

最後,我們再來了解下最初的安裝入口。

void main() {
  runApp(GithubApp());
}
複製程式碼

在我們的main檔案中,有一個main函式,其中呼叫了runApp方法,傳入的是GithubApp。我們再來看下GithubApp是什麼?

class GithubApp extends StatefulWidget {
  @override
  _GithubAppState createState() {
    return _GithubAppState();
  }
}
 
class _GithubAppState extends State<GithubApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Github',
      theme: ThemeData.light(),
      initialRoute: welcomeRoute.routeName,
      routes: {
        welcomeRoute.routeName: (BuildContext context) => WelcomePage(),
        loginRoute.routeName: (BuildContext context) => LoginPage(),
        homeRoute.routeName: (BuildContext context) => HomePage(),
        repositoryRoute.routeName: (BuildContext context) => RepositoryPage(),
        followersRoute.routeName: (BuildContext context) =>
            FollowersPage(followersRoute.pageType),
        followingRoute.routeName: (BuildContext context) =>
            FollowersPage(followingRoute.pageType),
        webViewRoute.routeName: (BuildContext context) => WebViewPage(title: '',),
      },
    );
  }
}
複製程式碼

發現沒它其實也是一個Widget,正如文章開頭所說的,Flutter是由各個Widget組成。main是程式的入口,而其中的runApp中的Widget是整個程式掛載的起點。它會建立成一個具有與螢幕寬高一致的根元素,並把它裝載到螢幕中。

所以在Flutter中一直都是通過建立Element,然後呼叫build方法來獲取其後續的子Widget,最終構建成我們所看到的程式。

文中的程式碼都是來自於flutter_github,這是一個基於Flutter的Github客戶端同時支援Android與IOS,支援賬戶密碼與認證登陸。使用dart語言進行開發,專案架構是基於Model/State/ViewModel的MSVM;使用Navigator進行頁面的跳轉;網路框架使用了dio。專案正在持續更新中,感興趣的可以關注一下。

從零開始的Flutter之旅: StatelessWidget

當然如果你想了解Android原生,相信flutter_github的純Android版本AwesomeGithub是一個不錯的選擇。

下期預告

從零開始的Flutter之旅: StatefulWidget

如果你喜歡我的文章模式,或者對我接下來的文章感興趣,建議您關注我的微信公眾號:【Android補給站】

或者掃描下方二維碼,與我建立有效的溝通,同時更快更準的收到我的更新推送。

從零開始的Flutter之旅: StatelessWidget

相關文章