天天看點

第一個Flutter程式-計數器及項目結構分析

文章目錄

      • 一、目标
      • 二、建立計數器項目
        • 2.1 建立項目,設定項目名及配置SDK
        • 2.2 設定包名
        • 2.3 運作項目
        • 2.4 計數器
      • 三、項目結構分析
        • 3.1 導包
        • 3.2 應用入口
        • 3.3 應用結構
        • 3.4 首頁
        • 3.5 _MyHomePageState詳解
        • 3.6 流程小結
      • 四、解惑build為何放在State中

一、目标

通過一個計數器示例,對

Flutter

應用程式結構有個基本了解,對其概念和技術有一定的認識,也友善後續對

Flutter

其他概念和技術的學習。

二、建立計數器項目

此過程比較簡單,通過

Android Studio

建立新的

Flutter

工程即可。

2.1 建立項目,設定項目名及配置SDK

第一個Flutter程式-計數器及項目結構分析

2.2 設定包名

在此,僅勾選了

Kotlin support

,如果在iOS上需要使用

Swift

的話,在此勾選即可。

第一個Flutter程式-計數器及項目結構分析

2.3 運作項目

至此,工程建立完畢。在

Terminal

視窗,運作

flutter run

即可将應用安裝到目前連接配接的手機上。

第一個Flutter程式-計數器及項目結構分析

2.4 計數器

機智的你,也許發現,此時點選程式右下角的"➕",頁面中的數字會一直增加,簡單吧,不需要手寫一行代碼,一個計數器應用就完成了。

三、項目結構分析

Flutter

是采用

Dart

語言來編寫的,該示例項目中,主要

Dart

代碼在

lib/main.dart

中,接下來就對該檔案中的代碼進行分析。

3.1 導包

import 'package:flutter/material.dart';
           

該行代碼主要導入

Material UI

元件庫。

Material

是一種标準的移動端和

web

端的視覺設計語言,

Flutter

預設提供了一套豐富的

Material

風格的

UI

元件。

3.2 應用入口

void main() => runApp(MyApp());
           

Flutter

應用中,

main

函數是整個應用程式的入口。

main

函數中調用了

runApp

方法,其功能是啟動

Flutter

應用,接收一個

widget

參數,在該示例中,它是

MyApp

類的一個執行個體,該參數代表

Flutter

應用。

main函數使用了

=>

符号,這是

Dart

中單行函數或方法的簡寫。

3.3 應用結構

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}
           
  • MyApp

    類代表

    Flutter

    應用,它繼承了

    StatelessWidget

    類,也意味着應用本身也是一個

    widget

  • Flutter

    中,大多數東西都是

    widget

    ,包括對其(

    alignment

    )、填充(

    padding

    )和布局

    (layout

    )
  • Flutter

    在建構頁面時,會調用組建的

    build

    方法,

    widget

    的主要工作是提供一個

    build()

    方法來描述如何建構

    UI

    界面,通常是通過組合、拼裝其他基礎

    widget

  • MaterialApp

    Material

    庫提供的

    Flutter App

    架構,通過它可以設定應用的名稱、主題、首頁以及路由清單等,

    MaterialApp

    也是一個

    widget

  • home

    Flutter

    應用的首頁,它也是一個

    widget

3.4 首頁

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
 ...
}
           

MyHomePage

是應用的首頁,它繼承自

StatefulWidget

類,表示他是一個有狀态的

widget(Stateful widght)

,目前,可以簡單認為

Stateful Widget

Stateless Widget

有兩點不同:

a.

Stateful widget

可以擁有狀态,這些狀态在widget生命周期中時可以變的,而

Stateless widget

是不可變的

b.

Stateful widget

至少由兩個類組成:

  • 一個

    StatefulWidget

  • 一個

    State

    類,

    StatefulWidget

    類本身是不可變的,但State類中持有的狀态在

    widget

    生命周期中可能會發生變化

_MyHomePageState

類時

MyHomePage

類對應的狀态類。看到這裡,也許機智的你已經發現,和

MyApp

類不同,

MyHomePage

類中沒有

build

方法,取而代之的是,

build

方法被挪到了

_MyHomePageState

方法中,至于為什麼這麼做,先留個疑問,在分析完完整代碼後再來解答。

3.5 _MyHomePageState詳解

接下來,一起看看

_MyHomePageState

中都包含哪些東西。

  1. 狀态
    int _counter = 0;
               

    _counter

    為儲存螢幕右下角"➕"号按鈕點選次數的狀态。
  2. 設定狀态的自增函數
    void _incrementCounter() {
    	setState(() {
     		_counter++;
    	});
    }
               
    當按鈕點選時,會調用此函數,該函數的作用是先自增

    _counter

    ,然後調用

    setState

    方法。

    setState

    方法的作用是通知

    Flutter

    架構,有狀态發生了變化,

    Flutter

    架構收到通知後,會執行

    build

    方法來根據新的狀态重新建構界面,

    Flutter

    對此方法做了優化,使重新執行變得很快,是以你可以重新建構任何需要更新的東西,而無須分别去修改各個

    widget

  3. 建構

    UI

    界面

    建構

    UI

    界面的邏輯在

    build

    方法中,當

    MyHomePage

    第一次建立時,

    _MyHomePageState

    類會被建立,當初始化完成後,

    Flutter

    架構會調用

    Widget

    build

    方法來建構

    widget

    樹,最終将

    widget

    樹渲染到裝置螢幕上,是以,來看一下

    _MyhomePageState

    build

    方法都幹了什麼事。
Widget build(BuildContext context) {
   return new Scaffold(
     appBar: new AppBar(
       title: new Text(widget.title),
     ),
     body: new Center(
       child: new Column(
         mainAxisAlignment: MainAxisAlignment.center,
         children: <Widget>[
           new Text(
             'You have pushed the button this many times:',
           ),
           new Text(
             '$_counter',
             style: Theme.of(context).textTheme.display1,
           ),
         ],
       ),
     ),
     floatingActionButton: new FloatingActionButton(
       onPressed: _incrementCounter,
       tooltip: 'Increment',
       child: new Icon(Icons.add),
     ),
   );
 }
           
  • Scaffold

    Material

    庫中提供的頁面腳手架,提供了預設的導航欄、标題和包含主螢幕

    widget

    樹的

    body

    屬性,

    widget

    樹可以很複雜
  • body

    widget

    樹中包含了一個

    Center widget

    Center

    可以将其子

    widget

    樹對齊到螢幕中心,

    Center

    widget

    是一個

    Column widget

    Column

    的作用是将其所有字

    widget

    沿螢幕垂直方向依次排列,此例中

    Column

    包含兩個

    Text

    widget

    ,第一個

    Text widget

    顯示固定為文本

    You have pushed the button this many times:

    ,第二個

    Text widget

    顯示

    _counter

    狀态的數值

    -

    floatingActionButton

    是頁面右下角的帶"➕"的炫富按鈕,它的

    onPressed

    屬性接受一個毀掉參數,代表它被點選後的處理事件,本例中将

    _incrementCounter

    作為其處理函數

3.6 流程小結

現在,我們将整個流程串起來:當右下角的

floatingActionButton

按鈕被點選後,會調用

_incrementCounter

,在

_incrementCounter

中,首先會自增

_counter

計數器(狀态),然後

setState

會通知

Flutter

架構狀态發生變化, 接着,

Flutter

會調用

build

方法以新的狀态重新建構

UI

,最終顯示在裝置螢幕上。

四、解惑build為何放在State中

現在,我們可以回答之前的問題,為什麼

build

方法在

State

,而不是

StatefulWidget

中?

這個主要是為了靈活性,如果将

build

方法放在

StatefulWidget

中則會有兩個問題:

  • 狀态通路不便

    試想一下,如果我們的

    Stateful widget

    有很多狀态,而每次狀态改變都要調用

    build

    方法,由于狀态是儲存在

    State

    中的,如果将

    build

    方法放在

    StatefulWidget

    中,那麼建構時讀取狀态會很不友善。如果真的将

    build

    方法放在

    StatefulWidget

    中的話,由于建構使用者界面過程需要依賴

    State

    ,是以

    build

    方法必須加一個

    State

    參數,大概是下面這樣:
Widget build(BuildContext context, State state){
      //state.counter
      ...
  }
           

這樣的話,就隻能将

State

的所有狀态聲明為公開的狀态,這樣才能在

State

類外部通路狀态,但将狀态設定為公開後,狀态将不再具有私密性,這樣依賴,對狀态的修改将會變得不可控。但将

build

方法放入

State

中的話,建構過程可以直接通路狀态,會很友善。

  • 繼承

    StatefulWidget

    不便

    例如,

    Flutter

    中有一個動畫

    widget

    的基類

    AnimatedWidget

    ,它繼承自

    StatefulWidget

    AnimatedWidget

    中引入了一個抽象方法

    build(BuildContext context)

    ,繼承自

    AnimatedWidget的動畫

    widget

    都要實作這個

    build

    方法。 現在試想一下,如果

    StatefulWidget

    類中已經有了一個

    build

    方法,正如上面所述,此時build方法需要接受一個state對象,這意味着

    AnimatedWidget

    必須先将自己的

    State

    對象(記為

    _animatedWidgetState

    )提供給其自雷,因為子類需要在其

    build

    方法中調用父類的

    build`方法,代碼可能如下:
class MyAnimationWidget extends AnimatedWidget{
	    @override
	    Widget build(BuildContext context, State state){
	      //由于子類要用到AnimatedWidget的狀态對象_animatedWidgetState,
	      //是以AnimatedWidget必須通過某種方式将其狀态對象_animatedWidgetState
	      //暴露給其子類   
	      super.build(context, _animatedWidgetState)
	    }
}
           

這樣顯然是不合理的,因為:

  1. AnimatedWidget

    的狀态對象是

    AnimatedWidget

    内部實作細節,不應該暴露給外部
  2. 如果要将父類狀态暴露給子類,那麼必須有一種傳遞機制,二做這一套傳遞機制是毫無意義的,因為父子類之間狀态的傳遞和子類本身邏輯是無關的。

綜上所述,對于

StatefulWidget

,将

build

方法放在

State

中,可以給開發帶來很大的靈活性,更加規範。