Flutter 高效自學筆記(二)

方應杭在飢人谷發表於2019-05-26

Flutter 高效自學筆記(一)

這是上篇文章文末提到的「第三個教程」的快速自學筆記。

Widget 部件

讀音 /'wɪdʒɪt/ 危機特,文件說借鑑了 React。我認為 Widget 相當於 React 裡的元件 Component。

下文將 Widget 全部稱為「部件」。

部件的特點

  1. 基於你傳給它配置和 state 生成檢視
  2. 當一個部件的 state 變化了,部件就會更新檢視
  3. Flutter 會對比新舊檢視的區別,對實際檢視做最小化的變動

runApp

這是 Flutter 應用必須呼叫的 API,接受一個部件。個人認為這很像是 React 的 ReactDOM.render 操作。

import 'package:flutter/material.dart';

void main() {
  runApp(
    Center(
      child: Text(
        'Hello, world!',
        textDirection: TextDirection.ltr,
      ),
    ),
  );
}
複製程式碼

以上程式碼的作用:

  1. Center 是個類,Center() 就是例項化出一個例項
    1. 我非常欣賞刪掉 new 的做法,再也不會有人問 Center() 和 new Center() 的區別了,fuck JS。
  2. Center(...) 返回一個部件,作為根部件
  3. Center(...) 部件的子部件是 Text(...),如果只有一個子元件就用 child,如果有多個子元件就用 children
  4. Flutter 會強行讓根部件佔滿螢幕(好評),所以最終效果就是螢幕中央有個 Hello, world! 字串
  5. 程式碼中需要用 textDirection 指明文字方向,你用 MaterialApp 就可以省掉這一步,後面會講
  6. 所有的部件類要麼是 StatelessWidget 的子類,要麼是 StatefulWidget 的子類
  7. 一個部件的主要功能是提供 build 方法(個人認為類似於 React 的 render),build 方法可以呼叫更多其他部件
  8. Flutter 會遍歷這些子部件,然後計算出當前部件的結構

基礎部件

常用的部件有:

  • Text:顧名思義
  • Row 和 Column:佈局用的,類似於 CSS 的 flex 佈局
  • Stack:佈局用的,類似於 CSS 的絕對佈局
  • Container:通用佈局,類似於 div

MaterialApp

如果你想用一些 Material 風格的部件,只需要做這幾件事就可以:

  1. 在 pubspec.yml 裡新增如下程式碼

     flutter:
         uses-material-design: true
    複製程式碼
  2. runApp 的時候使用 MaterialApp,並使用 Material 部件

     runApp(MaterialApp(
         title: 'My app', // used by the OS task switcher
         home: Material(),
     ));
    複製程式碼
  3. import 'package:flutter/material.dart';

常用的 Material 部件

  1. Navigator:類似於 ReactRouter

  2. AppBar:頂部的 bar

     AppBar(
         leading: IconButton(
             icon: Icon(Icons.menu),
             tooltip: 'Navigation menu',
             onPressed: null,
         ),
         title: Text('Example title'),
         actions: <Widget>[
             IconButton(
             icon: Icon(Icons.search),
             tooltip: 'Search',
             onPressed: null,
             ),
         ],
     )
    複製程式碼
  3. Scaffold: 最常用的佈區域性件

     Scaffold(
       appBar: AppBar(...),
       body: Center(...),
       floatingActionButton: ...
     );
    複製程式碼

這些傳參方式很像 Vue 的命名插槽。

用 GestureDetector 處理使用者互動

這是一個自定義 button 部件:

class MyButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GestureDetector(               //  <-- 注意看這裡
      onTap: () {
        print('MyButton was tapped!');
      },
      child: Container(
        height: 36.0,
        padding: ...,
        margin: ...,
        decoration: ...,
        child: Center(
          child: Text('Engage'),
        ),
      ),
    );
  }
}
複製程式碼
  1. GestureDetector 部件並沒有任何表現層,只處理使用者互動。當使用者點選 Container 部件時,會觸發 GestureDetector 部件的 onTap 回撥。
  2. GestureDetector 支援的互動相當多,如輕觸、拖曳和縮放。
  3. 很多其他部件都藉助 GestureDetector 來接收回撥,如 IconButton, RaisedButton 和 FloatingActionButton 都接收 onPressed 回撥。

改變 state

無狀態部件從父級接收資料,並把資料存在自己的 final 成員變數裡(類似於 JS 的 const)。

有狀態的部件則知道如何建立 state 物件,並維持 state 物件。

如何使用呢?

首先宣告一個 StatefulWidget

class Counter extends StatefulWidget {
  @override
  _CounterState createState() => _CounterState();
}
複製程式碼

這個部件需要重寫 createState 方法,而這個方法的返回值的型別,必須繼承自 State 類。

_CounterState 是如何繼承 State 的呢

class _CounterState extends State<Counter> {
  int _counter = 0;

  void _increment() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Row(
      children: <Widget>[
        RaisedButton(
          onPressed: _increment,
          child: Text('Increment'),
        ),
        Text('Count: $_counter'),
      ],
    );
  }
}
複製程式碼
  1. 需要重寫 build 方法提供檢視,而且可以使用 '$_counter' 混入語法
  2. 在按鈕被點選的時候呼叫 _increment,這個 _increment 會呼叫 State 提供的 setState 函式,給 setState 函式傳入一個函式,這個傳入的函式可以對資料進行修改。
  3. 非常像 React 的 Class 元件。區別在於
    1. Flutter 的部件和 state 是分開的類,而 React 只是把 state 用一個 hash 表示。
    2. Flutter 的 State 裡面提供 build 方法,而 React 的 state 只是一個 hash
    3. Flutter 將 StatelessWidget 和 StatefulWidget 分開,能更好地組織程式碼

生命週期

StatefulWidget 有 createState 方法,State 有 initState 方法。

Flutter 呼叫 createState 方法後,得到一個 State 物件,然後呼叫這個 State 物件的 initState 方法。

你的自定義 State 類可以重寫 initState 方法,這個方法只會呼叫一次。比如你可以重寫 initState 來配置動畫或者訂閱事件。

當一個 State 物件快死的時候,Flutter 會呼叫 State 物件的 dispose 方法。你也可以重寫 dispose 方法,比如在裡面取消 timer 或者取消訂閱。

keys

Flutter 預設是通過部件的 runtimeType 和出現順序來確定一個部件的身份的,這樣才能在對比新舊部件樹的過程中確定哪個是哪個。如果你給部件賦予了 key,那麼就不用根據「出現順序」來唯一確定元件了,因為這種方式不太靠譜。

個人認為跟 React 的 key 有相同的作用。

key 分為兄弟間的 key 和全域性 key。目前還不知道應用場景的區別。大部分時候應該只需要用兄弟間的 key 用來區分兄弟就行了。


下一篇文章打算看看 Flutter 如何發起網路請求。

未完待續……

相關文章