Flutter入門並開發天氣預報APP(4)——基礎Widget


到這章我們就差不多可以開始寫天氣預報了。首先我們來看一下一些基礎簡單的Widget。

1. 基礎組件

1.1 文本

Text用於顯示簡單的文本,包含一些控制文本顯示的屬性。

Text(
    "1234",
),
Text(
    "1234",
    style: TextStyle(
        color: Colors.purple,
        fontSize: 32.0,
        fontWeight: FontWeight.bold,
    ),
),

Text示例

  • 需要顯示的文本信息直接放到一個雙引號裏面就可以了;
  • textAlign:文本對齊方式;
  • maxLines:文本顯示的最大行數;
  • overflow:指定多餘文本的截斷方式;
  • textScaleFactor:指定文本相對於當前字體大小的縮放因子;
  • TextStyle:設置顯示文本的字體、顏色、粗細等樣式:
    • height:指定行高,但是不是絕對值,而是一個因子,相當於fontsize * height
    • fontFamily:設置字體;
    • fontSize:設置字體大小

1.2 按鈕

不同的組件庫有不同的按鈕,我們現在只拿Material組件庫中的按鈕舉例。

RaisedButton

漂浮按鈕,帶有陰影和灰色背景。按下後陰影會變大。

RaisedButton(
  child: Text("1234"),
  onPressed: () {},
);

RaisedButton未按下
RaisedButton按下

FlatButton

扁平化按鈕,背景透明且不帶陰影,按下後會有背景色。

FlatButton(
  child: Text("1234"),
  onPressed: () {},
)

FlatButton按下
FlatButton按下

IconButton

可點擊的Icon,默認沒有背景,按下後出現陰影

IconButton(
  icon: Icon(Icons.thumb_up),
  onPressed: () {},
)

-w88

1.3 圖片

我們通過Image來顯示圖片,來源可以是asset、網絡等位置。

從asset加載圖片

  1. 現在項目根目錄(也就是和android、ios、lib等目錄同級)新建一個images目錄,並把圖片main.png拷進去;
  2. pubspec.yaml中的flutter部分添加一下內容:
    assets
  3. 加載該圖片
Image(
  image: AssetImage("images/amoled.png"),
);

Image也提供了一個快速構造函數:

Image.asset(
  "images/amoled.png",
)

從網絡加載圖片

Image(
  image: NetworkImage(
      "https://s2.ax1x.com/2019/05/27/VZrQ3V.png"),
)

或者

Image.network(
  "https://s2.ax1x.com/2019/05/27/VZrQ3V.png",
)

參數

Image有一些基本參數

const Image({
  ...
  this.width, //圖片的寬
  this.height, //圖片高度
  this.fit,//縮放模式
  this.alignment = Alignment.center, //對齊方式
  this.repeat = ImageRepeat.noRepeat, //重複方式
  ...
})
  • widthheight:寬和高;
  • fit:縮放模式:
    • fill:拉伸圖片知道填滿;
    • cover:按原圖長寬比放大圖片來填滿,多餘的部分捨去;
    • contain:在保證圖片長寬比不變的情況下儘可能去填滿;
    • fitWidth:寬度會縮放到顯示空間的寬度,高度會按比例縮放,如果有多餘的部分會被捨去;
    • fitHeight:與fitWidth同理;
    • none:沒有適應策略,圖片多大就顯示多大,如果圖片原尺寸小於顯示空間就只顯示原尺寸;如果大於則捨棄多餘部分只顯示中間部分;
  • repeat:當圖片小於顯示空間時,會將圖片重複顯示。

2. 佈局組件

2.1 線性佈局(Row、Column)

線性佈局就相當於Android裏面的LinearLayout,但是不同的是Flutter將豎直和水平佈局單獨拿了出來。

線性佈局分爲豎直佈局Column和水平佈局Row。他兩屬性都是一樣的。
再說屬性之前我們先熟悉兩個概念:主軸和交叉軸。如果是Column,主軸是豎直軸,交叉軸是水平軸;如果是Row,主軸是水平軸,交叉軸是豎直軸。

Row({
  ...  
  MainAxisSize mainAxisSize = MainAxisSize.max,    
  MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
  CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
  List<Widget> children = const <Widget>[],
})
  • mainAxisSize:主軸佔用空間。默認是MainAxisSize.max,是指佔用全部主軸空間。如果設置爲MainAxisSize.min,那就只佔用所有子組件所需要的空間;
  • mainAxisAlignment:子Widget在主軸的對其方向;
  • crossAxisAlignment:子Widget在交叉軸的對其方向;
  • children:所有的子Widget。

2.2 彈性佈局(Flex)

其實這個佈局和線性佈局很有淵源,爲什麼這麼說呢,因爲RowColumn都繼承自它。
因此關於他和線性佈局重複的地方我們現在就不再講了,我們直說他“彈性”的部分。

Expanded

可以按比例“拉伸”RowColumnFlex子組件所佔的空間。

const Expanded({
  int flex = 1, 
  @required Widget child,
})

flex爲彈性係數,如果爲0或者null,則child不會闊伸佔用的控件。如果大於0,則會按照flex的比例來分隔主軸全部空閒空間。其實說白了,就和Android裏面LinearLayoutweight一樣的。但是我們還是來舉個例子吧。

return Scaffold(
      appBar: new AppBar(
        title: Text("$_title"),
      ),
      body: Center(
        child: Flex(
          children: <Widget>[
            Flex(
              direction: Axis.horizontal,
              children: <Widget>[
                Expanded(
                  flex: 1,
                  child: Container(
                    color: Colors.blue,
                    child: Text("1234"),
                  ),
                ),
                Expanded(
                  flex: 2,
                  child: Container(
                    color: Colors.red,
                    child: Text("1234"),
                  ),
                ),
              ],
            ),
            Flex(
              direction: Axis.horizontal,
              children: <Widget>[
                Expanded(
                  flex: 3,
                  child: Container(
                    color: Colors.blue,
                    child: Text("1234"),
                  ),
                ),
                Expanded(
                  flex: 1,
                  child: Container(
                    color: Colors.red,
                    child: Text("1234"),
                  ),
                ),
              ],
            ),
            Flex(
              direction: Axis.horizontal,
              children: <Widget>[
                Expanded(
                  flex: 1,
                  child: Container(
                    color: Colors.blue,
                    child: Text("1234"),
                  ),
                ),
                Expanded(
                  flex: 1,
                  child: Container(
                    color: Colors.red,
                    child: Text("1234"),
                  ),
                ),
              ],
            ),
          ],
          direction: Axis.vertical,
        ),
      ),
);

Flex示例

2.3流式佈局(Wrap、Flow)

如果使用線性佈局的話,當需要顯示的內容超出屏幕邊界的時候就會報錯。
超出屏幕邊界

爲了避免這種情況,我們就可以使用流式佈局。當需要顯示的內容超出屏幕邊界的時候,就自動折行來繼續顯示。

Flutter中通過WrapFlow來實現流式佈局。

Wrap

我們來看下Wrap主要的一些參數:

Wrap({
  ...
  this.direction = Axis.horizontal,
  this.alignment = WrapAlignment.start,
  this.spacing = 0.0,
  this.runAlignment = WrapAlignment.start,
  this.runSpacing = 0.0,
  this.crossAxisAlignment = WrapCrossAlignment.start,
  this.textDirection,
  this.verticalDirection = VerticalDirection.down,
  List<Widget> children = const <Widget>[],
})

其中很多參數RowColumn中都有,就不再介紹了,主要介紹點不同的:

  • spacing:主軸方向子widget的間距
  • runSpacing:縱軸方向的間距
  • runAlignment:縱軸方向的對齊方式

下面有一個示例:

Wrap(
  spacing: 8.0, // 主軸(水平)方向間距
  runSpacing: 4.0, // 縱軸(垂直)方向間距
  alignment: WrapAlignment.center, //沿主軸方向居中
  children: <Widget>[
    new Chip(
      avatar: new CircleAvatar(backgroundColor: Colors.blue, child: Text('A')),
      label: new Text('Hamilton'),
    ),
    new Chip(
      avatar: new CircleAvatar(backgroundColor: Colors.blue, child: Text('M')),
      label: new Text('Lafayette'),
    ),
    new Chip(
      avatar: new CircleAvatar(backgroundColor: Colors.blue, child: Text('H')),
      label: new Text('Mulligan'),
    ),
    new Chip(
      avatar: new CircleAvatar(backgroundColor: Colors.blue, child: Text('J')),
      label: new Text('Laurens'),
    ),
  ],
)

Wrap示例

Flow

Flow較複雜,需要自己實現子Widget的位置轉換,一般不推薦使用Flow。但是如果需要自定義佈局策略,或者對性能要求較高,這個時候就得用Flow了。

但是由於太過於複雜,我也沒咋用過,我就不在這講了?,大家有需要的可以百度,或者看我推薦的這篇:4.4 流式佈局-《Flutter實戰》

2.4層疊佈局(Stack)

這個佈局類似於Android中的Frame,允許在父佈局的任意地方放置佈局。

Stack({
  this.alignment = AlignmentDirectional.topStart,
  this.textDirection,
  this.fit = StackFit.loose,
  this.overflow = Overflow.clip,
  List<Widget> children = const <Widget>[],
})
  • alignment:決定如何去對齊;
  • textDirection:確定alignment的參考系;
  • fit:沒有定位的子Widget如何去適應Stack的大小;
  • overflow:決定超出Stack顯示空間的子Widget如何去顯示,如果Overflow.clip,則超出部分會隱藏,Overflow.visible則不會。

2.5對齊與相對定位(Align)

Align可以調整子Widget的位置,並且可以根據子Widget的寬高來確定自身的寬高。

Align({
  Key key,
  this.alignment = Alignment.center,
  this.widthFactor,
  this.heightFactor,
  Widget child,
})
  • alignment:代表子Widget在父Widget的起始位置;
  • widthFactor和heightFactor確定Align本身的寬高。

來看一個簡單的例子:

Container(
  height: 120.0,
  width: 120.0,
  color: Colors.blue[50],
  child: Align(
    alignment: Alignment.topRight,
    child: FlutterLogo(
      size: 60,
    ),
  ),
)

運行結果:
-w100

3. 容器類組件

3.1 填充(Padding)

用過Android的同學一定熟悉,Padding就是負責留白的嘛。
但是跟Android裏面不同的是,在Android裏面我們一般是在一個View裏面添加Padding,但是在Flutter裏面,Padding直接變成了一個Widget、一個佈局。

使用方法:

Padding ({
  EdgeInsetsGeometry padding,
  Widget child,
})

對於EdgeInsetsGeometry我們一般使用EdgeInsets類。

EdgeInsets

EdgeInsets類提供了幾個便捷的方法:

  • fromLTRB(double left, double top, double right, double bottom):分別指定四個方向的填充;
  • all(double value) : 所有方向均使用相同數值的填充;
  • only({left, top, right ,bottom }):可以設置具體某個方向的填充(可以同時指定多個方向);
  • symmetric({ vertical, horizontal }):用於設置對稱方向的填充,vertical指top和bottom,horizontal指left和right;

示例:

body: Column(
  children: <Widget>[
    Padding(
      padding: EdgeInsets.only(left: 12.0, bottom: 10.0),
      child: Text("1234"),
    ),
    Padding(
      padding: EdgeInsets.fromLTRB(0.0, 0.0, 10.0, 10.0),
      child: Text("1234"),
    ),
    Padding(
      padding: EdgeInsets.symmetric(vertical: 10.0),
      child: Text("1234"),
    ),
  ],
),

運行結果:
Padding示例

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章