Flutter 開發從 0 到 1(三)布局與 ListView

上周日出去玩了,因此沒時間寫文章。我司加班到 11 點,第二天可以晚上班一個小時,加班到 12 點,可以晚上班兩個小時,以此類推,為什麼說這個,對的,加班第二天我沒有多睡覺,而是起來抓緊時間寫文章,好了,廢話不多說,進入今天的主題。

布局

說到 Android 布局,不是很難,會在對應的 xml 裡布局,Flutter 里沒有 xml,都在代碼里寫,給人感覺就很難,看下我們要實現的布局:

打開 PhotoShop,看下背景色 #ededed,日期字體顏色 #a6a6a6,標題字體顏色 #1b1b1b,摘要字體顏色 #808080。

公眾號這是列表,我先將 item 搞定。看這布局都是線性布局,這要在之前,分分鐘搞定,但 Flutter……好吧,Flutter 布局可沒那麼簡單,我花了好幾個小時才做好,期間遇到了不少困難。

Flutter 也有橫向 Row 布局和豎向 Column 布局,我本想分 1 和 2 兩個部分,最外層豎向 Column 包含 1 和 2,2 本身是 Column,包含一個 image 和兩個 text,直接使用 Column 可以完成,當我需要設置 2 裏面白色的背景色,發現 Column 根本沒有背景色屬性,於是把 2 最外層改造成 Container。

使用 Container 沒有問題,布局也很快實現了,接下來是實現四角的圓角效果。

Container 有 decoration,可以實現圓角,我遇到了兩個問題:

1、當 shape: BoxShape.circle 時不能設置 borderRadius ,會異常
異常信息:’shape != BoxShape.circle ||borderRadius == null’: is not true.

2、使用 BoxDecoration
Container 不能使用 color,會報錯:
Cannot provide both a color and a decoration
To provide both, use “decoration: BoxDecoration(color: color)”.

以為這樣就實現了圓角,不,不會那麼順利的,發現圖片根本沒有圓角效果,這和之前 Java 實現方式不一樣,最外層都實現了圓角,對裏面的布局(圖片)居然沒有生效,最後只好把布局實現如下:

分成 1、2、3 部分,3 還是 Container,2 和 3 圓角效果只對上下部分分別實現,完整代碼如下:

blogItem() {
    var date = new Padding(
        padding: const EdgeInsets.only(
          top: 20.0,
          left: 10.0,
          right: 10.0,
        ),
        child: new Text(
          '2020年6月28日 22:49',
          textAlign: TextAlign.center,
          style: TextStyle(color: ColorCommon.dateColor, fontSize: 18),
        ));

    var cover = new Padding(
        padding: const EdgeInsets.only(
          top: 10.0,
          left: 10.0,
          right: 10.0,
        ),
        child: new ClipRRect(
            borderRadius: BorderRadius.only(
                topLeft: Radius.circular(10.0),
                topRight: Radius.circular(10.0)),
            child: new Image.network(
              '//pic1.win4000.com/wallpaper/2020-04-21/5e9e676001e20.jpg',
            )));

    var title = new Text(
      'APP 開發從 0 到 1(一)需求與準備',
      style: TextStyle(color: ColorCommon.titleColor, fontSize: 22),
    );

    var summary = new Padding(
        padding: const EdgeInsets.only(
          top: 5.0,
        ),
        child: new Text('一個人做一個項目,你也可以。',
            textAlign: TextAlign.left,
            style: TextStyle(color: ColorCommon.summaryColor, fontSize: 18)));

    var titleSummary = new Container(
      padding: const EdgeInsets.all(10.0),
      alignment: Alignment.topLeft,
      decoration: new BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.only(
            bottomLeft: Radius.circular(10.0),
            bottomRight: Radius.circular(10.0)),
        shape: BoxShape.rectangle,
      ),
      margin: const EdgeInsets.only(left: 10, right: 10.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[title, summary],
      ),
    );

    var blogItem = new Column(
      children: <Widget>[date, cover, titleSummary, date, cover, titleSummary],
    );

    return blogItem;
  }

實際效果如下:

好了,關於 Flutter 布局就講到這裡,我只是針對這個項目所需要的去實現,Flutter 布局的東西還有很多,其屬性讓人眼花撩亂,可以去官網一個個去仔細學習和實踐。

ListView

blogItem 寫好了,完成了大頭,接下來就要填充真實的數據,在《APP 開發從 0 到 1(二)框架與網絡》,有說到網絡請求,其中有提到 setstate(){} 方法,調用這個方法會回調 build 方法,這樣我們可以在 build 方法加個判斷,先加載 Progress,待網絡數據請求完,再顯示 ListView,代碼如下:

@override
  Widget build(BuildContext context) {
    var content;
    if (blogList.isEmpty) {
      content = new Center(
        // 可選參數 child:
        child: new CircularProgressIndicator(),
      );
    } else {
      content = new ListView(children: blogItem());
    }

    return Scaffold(
      backgroundColor: ColorCommon.backgroundColor,
      appBar: AppBar(
        title: Text('AndBlog'),
      ),
      body: content,
      floatingActionButton: FloatingActionButton(
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }

blogItem() {
    List<Widget> widgets = [];
    for (int i = 0; i < blogList.length; i++) {
      Blog blog = blogList[i];
      var date = new Padding(
          padding: const EdgeInsets.only(
            top: 20.0,
            left: 10.0,
            right: 10.0,
          ),
          child: new Text(
            // 填充真實數據
            blog.date,
            textAlign: TextAlign.center,
            style: TextStyle(color: ColorCommon.dateColor, fontSize: 18),
          ));

      //……

      var blogItem = new Column(
        children: <Widget>[
          date,
          cover,
          titleSummary,
        ],
      );
      widgets.add(blogItem);
    }
    return widgets;
  }

這樣 ListView 也完成了,接下來需要完成的是 ListView 加載更多。

Tags: