8天讓iOS開發者上手Flutter之二

Flutter佈局

Alignment

Container類裏有一個alignment屬性,翻譯過來應該叫對齊方式,這個屬性用來控制Container的子控件相對於它自身的一個位置。在咱們iOS開發中,咱們知道座標系的原點是在左上角。而在flutter中,座標系的原點在父控件的正中心,可使用這個alignment屬性來控制子控件在父控件中的位置,它有兩個參數分別是double類型的x,y。取值是-1到1,當0,0的時候表示子控件在父控件的正中心;當1,0的時候,表示子控件位於x方向上的最右側,y方向上居中;當-1,-1的時候,表示子控件位於父控件的左上角位置。有點相似於CALayer的anchorPoint屬性。如圖代碼以下: image.png數組

Row

Row表示水平佈局,它有一個children屬性,用來存放它的子控件。代碼以下: image.png微信

其中Icon類是flutter提供的一個快速建立一些經常使用圖標的類。若是想給每一個Icon都加一個背景色,直接設置Icon的color是不行的,這樣修改的是圖標的顏色而不是背景色,給Icon包一層Container容器,而後再設置Container顏色這樣就能夠實現了。

最開始咱們嘗試了alignment屬性的做用,當它是0,0的時候,Text的位置默認是在屏幕中央的。爲何這裏換成咱們的Row以後,Row的子控件位置不在屏幕中央呢?markdown

mainAxisAlignment

Row 和 Column 都有一個mainAxisAlignment屬性,叫做主軸對齊方式,默認是MainAxisAlignment.start意思沿着主軸方向開始,Row佈局下就是從左至右,Column佈局下就是從上至下。
MainAxisAlignment.spaceAround:將剩下的空間平均分配
MainAxisAlignment.spaceBetween:將剩下的空間分配到子控件之間
MainAxisAlignment.spaceEvenly:等間距分配子控件框架

crossAxisAlignment

交叉軸對齊方式,start,end,center這幾種方式試一下很好理解,stretch會將子控件拉伸。而baseline用的比較少,單獨使用它會報錯,須要和Text文本結合,還須要配合textBaseline屬性一塊兒使用。以下圖所示,若是不設置CrossAxisAlignment.baselineTextBaseline.alphabetic就會根據控件高度水平對齊,而若是設置了就會根據控件內文本的基線對齊。
Snip20210713_177.png Snip20210713_178.pngless

Column

這個和Row是對應的,Row是水平佈局,這個Column是垂直佈局ide

class LayoutDemo extends StatelessWidget {
  const LayoutDemo({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.green,
      alignment: Alignment(0, 0),
      child: Column(
        children: [
          Container(
            child: Icon(Icons.add, size: 180,),
            color: Colors.red,
          ),
          Container(
            child: Icon(Icons.ac_unit, size: 120,),
            color: Colors.yellow,
          ),
          Container(
            child: Icon(Icons.access_alarm, size: 60,),
            color: Colors.blue,
          ),
        ],
      ),
    );
  }
}
複製代碼

顯示效果如圖:佈局

mainAxisAlignment

這個跟Row相似動畫

crossAxisAlignment

這個跟Row相似ui

Stack

這個是用在Z軸上的佈局的,row是X軸,column是Y軸。children數組第一個放在最底部,最後一個放在上面,離用戶最近的地方。spa

class LayoutDemo extends StatelessWidget {
  const LayoutDemo({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.green,
      alignment: Alignment(0, 0),
      child: Stack(
        children: [
          Container(
            child: Icon(Icons.add, size: 180,),
            color: Colors.red,
          ),
          Container(
            child: Icon(Icons.ac_unit, size: 120,),
            color: Colors.yellow,
          ),
          Container(
            child: Icon(Icons.access_alarm, size: 60,),
            color: Colors.blue,
          ),
        ],
      ),
    );
  }
}
複製代碼

APP顯示效果:

alignment

Stack裏有一個alignment屬性,它用來控制全部子控件相對於最大那個子控件的位置

class StackDemo extends StatelessWidget {
  const StackDemo({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Stack(
      alignment: Alignment.bottomRight,
      children: [
        Container(
          child: Icon(Icons.add, size: 180,),
          color: Colors.red,
        ),
        Container(
          child: Icon(Icons.ac_unit, size: 120,),
          color: Colors.yellow,
        ),
        Container(
          child: Icon(Icons.access_alarm, size: 60,),
          color: Colors.blue,
        ),
      ],
    );
  }
}
複製代碼

Positioned

Stack裏配合Positioned類使用的話,跟咱們iOS的約束有點相似了,能夠設置上,左間距之類的

class StackDemo extends StatelessWidget {
  const StackDemo({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Positioned(
          child: Container(
            child: Icon(
              Icons.add,
              size: 180,
            ),
            color: Colors.red,
          ),
        ),
        Positioned(
          child: Container(
            child: Icon(
              Icons.ac_unit,
              size: 120,
            ),
            color: Colors.yellow,
          ),
        ),
        Positioned(
          top: 20,
          left: 20,
          right: 20,
          child: Container(
            child: Icon(
              Icons.access_alarm,
              size: 60,
            ),
            color: Colors.blue,
          ),
        ),
      ],
    );
  }
}
複製代碼
能夠看到最小的藍色視圖的上左右均設置了20的間距,是否是熟悉的約束味道。。。

Expanded

Expanded是一個相似Container的經常使用的佈局容器,它用來填充佈局,使用了填充佈局在主軸方向上是不會有間隔的,因此Expanded用在Row裏面的時候,子控件的寬度設置就沒有意義了,而在Column裏面使用的使用,子控件的高度設置就沒有意義了。這裏以Column爲例:

class LayoutDemo extends StatelessWidget {
  const LayoutDemo({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.green,
      alignment: Alignment(0, 0),
      child: Column(
        children: [
          Expanded(
              child: Container(
                child: Icon(Icons.add, size: 180,),
                color: Colors.red,
              ),
          ),
          Expanded(
              child: Container(
                child: Icon(Icons.ac_unit, size: 120,),
                color: Colors.yellow,
              ),
          ),
          Expanded(
              child: Container(
                child: Icon(Icons.access_alarm, size: 60,),
                color: Colors.blue,
              ),
          ),
        ],
      ),
    );
  }
}
複製代碼

AspectRatio

AspectRatio是一個容器類,它有一個屬性aspectRatio表示寬高比。若是指定了寬度,根據這個aspectRatio能夠自動算出高度;若是指定了高度,根據aspectRatio能夠自動算出寬度。以下面代碼指定了父視圖高度爲100,aspectRatio寬高比爲2,子視圖寬度就是200,再把父視圖撐起來也是200。

class LayoutDemo extends StatelessWidget {
  const LayoutDemo({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
        color: Colors.green,
        alignment: Alignment(0, 0),
        child: Container(
          color: Colors.blue,
          height: 100,
          child: AspectRatio(
            aspectRatio: 2,
            child: Icon(
              Icons.add,
            ),
          ),
        ));
  }
}
複製代碼

Flutter狀態管理

以前介紹的這麼多類都是無狀態的,意思是顯示以後沒辦法更新UI的,若是想要實時更新UI的話,就不能繼承無狀態的類了。咱們先來看一個例子:明明count變化了,可是界面顯示沒有變化 image.png 記得修改APP的home視圖 image.png 而後點擊屏幕右下角的加號按鈕,能夠發現明明控制檯打印了count的值已經發生了變化,可是界面顯示依然是0 image.png 下面咱們解決這個問題,將StateManagerDemo繼承改成StatefulWidget,實現createState方法返回一個自定義的State對象,自定義的State對象裏面實現build方法。還須要注意在按鈕的點擊方法裏調用一下setState方法。這樣每次點擊加號按鈕就能實時更新UI了。改造完以後以下圖所示: image.png

項目搭建之底部TabBar

到目前爲止,咱們對flutter的一些基礎知識就算是介紹的差很少了。接下來咱們開始作一個簡單的仿微信APP。咱們應該都有經驗,理論的知識學得再多,不動手開始敲代碼,不在項目中運用,是很難真正掌握一門知識的。

新建一個flutter工程,命名wechat_demo: image.png

刪掉多餘的代碼,能夠所有從新本身寫: image.png

建立底部的TabBar和item,默認的type是白色的,顯示效果很難看因此改成fixed,還能夠設置fixedColor: image.png

這樣底部的TabBar就顯示出來了,會發現點擊沒有效果,對比iOS會發現這塊地方仍是iOS提供的UITabBarController封裝的更舒服,每一個平臺都各有優劣吧。

BottomNavigationBar有一個屬性currentIndex即表明了當前選中的下標。咱們能夠經過設置它的值來控制哪一個按鈕被選中。既然須要改變UI了,說明咱們須要將StatelessWidget改成StatefulWidget了。還有一個參數onTap是用來回調點擊事件的。實現點擊事件,切換currentIndex,從新setState就能夠實現,點擊切換了。咱們將bottomNavigationBar相關代碼放到一個新的文件rootPage中。代碼以下: image.png 記得修改main.dart文件中 image.png 這樣就實現了APP的底部TabBar的展現,點擊功能。點擊每一個item的時候,會發現flutter的bottomNavigationBar還自帶了動畫效果...

咱們知道Scaffold還有一個body的屬性,表示展現在屏幕上的內容。咱們每一個item對應的界面都須要一個AppBar,那麼也許意味着,body屬性還須要一個Scaffold來展現咱們的每一個item對應的內容。 image.png 能夠看到微信首頁就已經大概出來了,可是點擊的時候只會顯示這個微信頁面,怎麼實現切換不一樣的頁面呢,確定須要一個數組,來存放對應的每一個頁面了。 image.png
而後body裏,根據咱們的_currentIndex返回對應的body image.png 這樣點擊每一個item都會跳轉到對應的界面了,APP的主框架的搭建好了。

總結

今天主要講了flutter的三大布局類Row,Column,Stack以及他們的一些屬性。而後是有狀態的Widget和無狀態的Widget,最後搭建了一下咱們要作的仿微信APP的底部bottomNavigationBar和切換頁面功能

相關文章
相關標籤/搜索