Flutter開發 - 寫一個塊狀帶訂單數量角標的個人中心頁面,這個佈局有點意思

猿門射戟發表於2021-03-05

個人中心頁面


在這裡插入圖片描述
為什麼要挑這個頁面來講解下佈局呢,因為它比較具有代表意義,塊狀的佈局,包含Row,Column,Stack。最主要的是角標的處理方式。在一般的頁面中,我們都採用上下佈局,左右佈局,列表等來做,很巧合的是這裡基本都有,同時有一個要注意的點是角標,屬於絕對位置的佈局。

首先我們來解析一下這個個人中心頁面;

  1. 頂部導航欄;
  2. 頭部個人資訊;
  3. 訂單和其他中的Item項(角標要注意下);

這個頁面由三部分組成,我們分別來說下三部分怎麼實現:

  • 頂部導航欄
appBar: AppBar(
          backgroundColor: Color.fromRGBO(73, 129, 245, 1),
          title: Text(
            "我的",
            style: TextStyle(
                color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold),
          ),
          ///導航欄下的那條黑線
          elevation: 0,
          ///右側按鈕,可設定多個
          actions: <Widget>[
            FlatButton(
              child: Text(
                "設定",
                style: TextStyle(
                    color: Colors.white,
                    fontSize: 15,
                    fontWeight: FontWeight.bold),
              ),
              onPressed: () {

              },
            ),
          ],
        ),
複製程式碼

此模組比較簡單和常規,一目瞭然,不再過多複述。

頭部個人資訊

return Container(
		///設定背景漸變色
      decoration: BoxDecoration(
          gradient: LinearGradient(
            colors: [
              Color.fromRGBO(73, 129, 245, 1),
              Color.fromRGBO(115, 172, 248, 1),
            ],
            begin: Alignment.topCenter,
            end: Alignment.bottomCenter,
          )),
      child: Container(
        margin: EdgeInsets.only(top: 16, bottom: 16),
        child: Row(
          children: <Widget>[
            Container(
              margin: EdgeInsets.only(left: 16),
              ///切成圓形
              child: ClipOval(
                child: Image(
                    image: AssetImage('images/codingfire.png'),
                    width: 48),
              ),
            ),
            Expanded(
              flex: 100,
              child: Container(
                margin: EdgeInsets.only(left: 12),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start, //元素左對齊
                  children: <Widget>[
                    Container(
                      child: Text(
                        Glb().userName,///可加長,會顯示點點點
                        style: TextStyle(
                            fontSize: 19,
                            fontWeight: FontWeight.bold,
                            color: Colors.white),
                        maxLines: 1,
                        overflow: TextOverflow.ellipsis,
                      ),
                    ),
                    Container(
                      child: Text(
                        "黃河之水天上來,奔流到海不復回",///可加長,會顯示點點點
                        overflow: TextOverflow.ellipsis,
                        style: TextStyle(
                          fontSize: 12,
                          color: Colors.white,
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ),
//            Spacer(),
            Container(
              height: 24,
              padding: EdgeInsets.only(right: 16, left: 6),
              child: OutlineButton(
                color: Colors.white,
                child: Baseline(
                  baseline: 12,
                  baselineType: TextBaseline.alphabetic,
                  child: Text("編輯資訊",
                      style: TextStyle(color: Colors.white, fontSize: 12),
                      textAlign: TextAlign.center),
                ),
                ///設定圓角
                shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(12.0)),
                borderSide: new BorderSide(color: Colors.white),
                onPressed: () {

                },
              ),
            ),
          ],
        ),
      ),
    );
複製程式碼

這裡有一張簡單的佈局圖可以說明佈局的關係,外層是Row,暱稱和副考標題所在的區域可以用Column來進行佈局。這裡說下為什麼要用Expanded,是為了讓文字自動撐開的同時,在超出右邊區域的時候不至於報錯,並自己顯示點點點,設定點點點要設定:

overflow: TextOverflow.ellipsis,
複製程式碼

在這裡插入圖片描述

訂單和其他中的Item項(角標要注意下)

接下來要寫的是塊狀區域,塊狀區域每行有四塊,每塊中有最多三個元素,分別是icon,標題和角標;我們可以參照個人資訊部分用Row很容易的實現這樣的效果,但是角標加入的時候會導致icon或者標題位置變化,因為角標是需要蓋在icon右上角的,按照盒子佈局的方式,會擠到其他元素導致變形,這時要用到Stack佈局,前端中也存在這樣的佈局方式。下面看下具體實現:

return Container(
      color: Colors.white,
      height: 92,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        children: <Widget>[
          itemCell(
              Icon(
                Icons.access_time,
                color: Color(0xFF3479FD),
              ),
              '全部訂單',
              "https://blog.csdn.net/CodingFire",
              10),
          itemCell(
              Icon(
                Icons.access_time,
                color: Color(0xFF3479FD),
              ),
              '待發貨',
              "https://blog.csdn.net/CodingFire",
              100),
          itemCell(
              Icon(
                Icons.access_time,
                color: Color(0xFF3479FD),
              ),
              '待收貨',
              'https://blog.csdn.net/CodingFire',
              0),
          itemCell(
              Icon(
                Icons.access_time,
                color: Color(0xFF3479FD),
              ),
              '確認收貨',
              'https://blog.csdn.net/CodingFire',
              1),
        ],
      ),
    );
複製程式碼

這裡你看到的很簡單,是塊狀的處理方式,單獨對塊做了封裝,並傳入需要的icon,標題,跳轉連結和訂單數量。下面我們來看看對塊的封裝:

Widget itemCell(Icon itemIcon, String itemTitle, String url, int count) {
    double c_width;
    if (count < 10) {
      c_width = 20;
    } else if (count > 100) {
      c_width = 35;
    } else
      c_width = 30;
    return GestureDetector(
      onTap: () {

      },
      child: Container(
        color: Colors.white,
        child: Stack(
          children: <Widget>[
            Stack(
              children: <Widget>[
                Container(
                  height: 27,
                  margin: EdgeInsets.only(top: 0),
                  child: IconButton(
                    icon: itemIcon,
                    tooltip: 'item',
                    iconSize: 23,
                  ),
                ),
                Container(
                ///Offstage表示按條件可隱藏的包裝
                  child: Offstage(
                  ///這裡隱藏的條件是count=0的時候,Offstage包含的模組(角標)會隱藏,考慮的是下面沒有角標的item
                    offstage: count == 0,
                    child: Container(
                      margin: EdgeInsets.only(left: 30),
                      width: c_width,
                      height: 14,
                      alignment: Alignment.center,
                      decoration: BoxDecoration(
                        borderRadius: BorderRadius.only(
                            topLeft: Radius.circular(7),
                            bottomRight: Radius.circular(7),
                            topRight: Radius.circular(7)),
                        color: Color(0xFFFF491C),
                      ),
                      child: Text(
                        count >= 100 ? '99+' : count.toString(),
                        style: TextStyle(
                            color: Colors.white,
                            fontSize: 12,
                            fontWeight: FontWeight.w500),
                      ),
                    ),
                  ),
                )
              ],
            ),
            Container(
              width: 50,
              margin: EdgeInsets.only(top: 35),
              child: Text(
                itemTitle,
                textAlign: TextAlign.center,
                style: TextStyle(
                    fontSize: 12,
                    color: Colors.black,
                    fontWeight: FontWeight.w500),
              ),
            ),
          ],
        ),
      ),
    );
  }
複製程式碼

其實也不復雜吧,只是要用到Stack的佈局方式,讓角標可以蓋在icon右上角而不至於擠到佈局變形。


以上就是個人中心頁面佈局的全部,如需程式碼可單獨找博主要下,暫時先不放上去,因為還有一篇關於單例靈活使用的部落格,程式碼會放在一起,後續會更新上地址。

備註:以上佈局並非最佳方案,大家可自行嘗試。

如果你細心的話會發現這裡已經用到了單例中的資料:

Glb().userName
複製程式碼

補充

今天,博主發現了一個問題,在適配iPad和安卓iPad時發現下面的文字換行了,因為設定了width:50,這是不合理的,應該去掉寬度設定。但同時發現icon下的文字不會自動居中,因為使用的是Stack,所以沒有辦法設定讓文字整體豎直居中,所以要調整佈局為Column,角標通過Positioned來處理,完美解決,下面看下修改後的程式碼:

Widget itemCell(Icon itemIcon, String itemTitle, String url, int count) {
    double c_width;
    if (count < 10) {
      c_width = 20;
    } else if (count > 100) {
      c_width = 35;
    } else
      c_width = 30;
    return GestureDetector(
      onTap: () {

      },
      child: Container(
        color: Colors.white,
        child: Column(
          children: <Widget>[
            Stack(
              children: <Widget>[
                Container(
                  height: 23,
                  ///這裡左右一定要撐開20的區域,要不然紅色角標會顯示不全(我覺得這裡的佈局還可以再優化)
                  margin: EdgeInsets.only(top: 28, left: 20, right: 20),
                  child: IconButton(
                    padding: EdgeInsets.all(0),
                    icon: itemIcon,
                    tooltip: 'item',
                    iconSize: 23,
                  ),
                ),
                Positioned(
                  left: 17,
                  top: 20,
                  child: Offstage(
                    offstage: count == 0,
                    child: Container(
                      margin: EdgeInsets.only(left: 30),
                      width: c_width,
                      height: 14,
                      alignment: Alignment.center,
                      decoration: BoxDecoration(
                        borderRadius: BorderRadius.only(
                            topLeft: Radius.circular(7),
                            bottomRight: Radius.circular(7),
                            topRight: Radius.circular(7)),
                        color: Color(0xFFFF491C),
                      ),
                      child: Text(
                        count >= 100 ? '99+' : count.toString(),
                        style: TextStyle(
                            color: Colors.white,
                            fontSize: 12,
                            fontWeight: FontWeight.w500),
                      ),
                    ),
                  ),
                )
              ],
            ),
            Container(
              color: Colors.white,
              margin: EdgeInsets.only(top: 4),
              child: Text(
                itemTitle,
                textAlign: TextAlign.center,
                style: TextStyle(
                    fontSize: 12,
                    color: Colors.black,
                    fontWeight: FontWeight.w500),
              ),
            ),
          ],
        ),
      ),
    );
  }
複製程式碼

相關文章