Flutter 仿寫微信發現、我的頁面

語言: CN / TW / HK

這是我參與11月更文挑戰的第5天,活動詳情檢視:2021最後一次更文挑戰

發現頁面實現

image.png

這裡我們仿照微信的發現頁面用 Flutter 類實現這頁面的簡單佈局及每個 cell 的點選效果及點選每條 cell 之後跳轉到一個新的頁面。這裡我們分幾步分別來實現這些功能。

自定義 cell

image.png

針對 cell 的佈局我們可以分為兩部分,左邊跟右邊,左邊是主圖片加標題,右邊是子標題、 子圖片加箭頭。主圖片名稱、主標題、子標題、子圖片名稱這些都可以由於 cell 初始化的時候由外部傳進來。這裡主圖片、標題跟箭頭是固定的每個 cell 都有的,子標題、 子圖片是可選的。針對 cell 佈局的程式碼如下。

String title; String imageName; String subTitle; String subImageName; DiscoverCell(this.title, this.imageName, this.subTitle, this.subImageName);

Container( color: _currentColor, height: 55, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // left Container( padding: EdgeInsets.all(10), child: Row( children: [ // 圖示 Image(image: AssetImage(widget.imageName), width: 20,), // 間隙 SizedBox(width: 15,), // Title Text(widget.title), ], ), ), // right Container( padding: EdgeInsets.all(10), child: Row( children: [ // subTitle widget.subTitle != null ? Text(widget.subTitle) : Text(''), // subImage widget.subImageName.length > 0 ? Image.asset(widget.subImageName, width: 15,) : Container(), // 箭頭 Image(image: AssetImage('images/icon_right.png'), width: 15,) ], ), ), ], ), )

列表佈局

class _DiscoverPageState extends State<DiscoverPage> { Color _themColor = Color.fromRGBO(230, 230, 230, 1.0); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: _themColor, // 安卓裡面用到,切 app 的時候顯示 centerTitle: true, title: Text('發現'), elevation: 0.0, ), body: Container( height: 800, color: _themColor, child: ListView( children: [ DiscoverCell('朋友圈', 'images/朋友圈.png', '', ''), SizedBox(height: 10,), DiscoverCell('掃一掃', 'images/掃一掃2.png', '', ''), //分割線 Row( children: [ // 左邊線條 Container(width: 50, height: 0.5, color: Colors.white,), // 右邊線條 Container(height: 0.5, color: Colors.grey,) ], ), DiscoverCell('搖一搖', 'images/搖一搖.png', '', ''), SizedBox(height: 10,), DiscoverCell('看一看', 'images/看一看icon.png', '', ''), //分割線 Row( children: [ // 左邊線條 Container(width: 50, height: 0.5, color: Colors.white,), // 右邊線條 Container(height: 0.5, color: Colors.grey,) ], ), DiscoverCell('搜一搜', 'images/搜一搜.png', '', ''), SizedBox(height: 10,), DiscoverCell('附近的人', 'images/附近的人icon.png', '', ''), SizedBox(height: 10,), DiscoverCell('購物', 'images/購物.png', '雙十一限時特價', 'images/badge.png'), Row( children: [ // 左邊線條 Container(width: 50, height: 0.5, color: Colors.white,), // 右邊線條 Container(height: 0.5, color: Colors.grey,) ], ), DiscoverCell('遊戲', 'images/遊戲.png', '', ''), SizedBox(height: 10,), DiscoverCell('小程式', 'images/小程式.png', '', ''), ], ), ) ); } }

針對列表的佈局我們用的是 ListView,在 children 裡面按順序新增每條 cell 資料,這裡每組之間的間隔我們用 SizedBoxcell 上的下劃線我們用 Row 來實現,分為左邊線條跟右邊線條。

cell 點選跳轉

image.png

return GestureDetector( // cell 手勢點選 onTap: (){ Navigator.of(context).push( MaterialPageRoute(builder: (BuildContext context) => DiscoverChildPage(widget.title) ) );

這裡我們定義一個新的頁面 DiscoverChildPage 標題由 cell 點選的時候傳入。在 cell 中我們新增點選方法,然後 push 到一個新的頁面。

cell 新增點選狀態

GestureDetector( // cell 手勢點選 onTap: (){ Navigator.of(context).push( MaterialPageRoute(builder: (BuildContext context) => DiscoverChildPage(widget.title) ) ); setState(() { _currentColor = Colors.white; }); }, // cell 手勢點選下去 onTapDown: (TapDownDetails details){ setState(() { _currentColor = Colors.grey; }); }, // cell 手勢點選取消 onTapCancel: (){ setState(() { _currentColor = Colors.white; }); }, child: Container( color: _currentColor, ), );

cell 加上點選狀態的話就需要繼承於有狀態的 Widget,然後在不同的點選狀態下設定不同的顏色,然後呼叫 setState 方法。因為呼叫 setState 方法的時候會重新構建 widget,所以針對複雜的控制元件的時候我們只讓需要改變狀態的子控制元件繼承於 StatefulWidget,把需要改變的部分抽取出來。我們這裡因為 cell 整體的子控制元件也不多,所以我們就直接讓 cell 繼承於 StatefulWidget 。也是為了偷下懶🐶。

我的頁面實現

image.png

我的頁面首先我們可以分為兩大塊,列表跟相機,這裡我們採用 Stack 部件來佈局。其中列表部分又可以分為兩大塊頭部跟底部 cell 部分。按照這種佈局思路我們的程式碼如下。

``` class _MinePageState extends State { Widget headerWidget() { return Container( height: 200, color: Colors.white, child: Container( margin: EdgeInsets.only(top: 90, bottom: 20, left: 16, right: 10), child: Row( children: [ // 頭像 Container( width: 70, height: 70, // 設定圓角屬性 decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), image: DecorationImage( image: AssetImage('images/ChenXi.JPG') ) ), ), // 右邊部分 Expanded(child: Container( padding: EdgeInsets.only(left: 10, top: 8, right: 10), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 暱稱 Container( height: 35, child: Text('Chenxi', style: TextStyle(fontSize: 25, color: Colors.black87),) ), // 微訊號加箭頭 Container( height: 35, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('微訊號:CX123', style: TextStyle(fontSize: 17, color: Colors.grey),), Image(image: AssetImage('images/icon_right.png'), width: 15,) ], ), ), ], ), )), ], ), ), ); }

@override Widget build(BuildContext context) { return Scaffold( body: Container( color: Color.fromRGBO(230, 230, 230, 1.0), child: Stack( children: [ // 列表 Container( child: MediaQuery.removePadding( removeTop: true, context: context, child: ListView( children: [ // 頭部 headerWidget(),

              // 列表
              SizedBox(height: 10,),
              DiscoverCell('支付', 'images/微信 支付.png', '', ''),
              SizedBox(height: 10,),
              DiscoverCell('收藏', 'images/微信收藏.png', '', ''),
              //分割線
              Row(
                children: [
                  // 左邊線條
                  Container(width: 50, height: 0.5, color: Colors.white,),
                  // 右邊線條
                  Container(height: 0.5, color: Colors.grey,)
                ],
              ),
              DiscoverCell('朋友圈', 'images/微信相簿.png', '', ''),
              //分割線
              Row(
                children: [
                  // 左邊線條
                  Container(width: 50, height: 0.5, color: Colors.white,),
                  // 右邊線條
                  Container(height: 0.5, color: Colors.grey,)
                ],
              ),
              DiscoverCell('卡包', 'images/微信卡包.png', '', ''),
              //分割線
              Row(
                children: [
                  // 左邊線條
                  Container(width: 50, height: 0.5, color: Colors.white,),
                  // 右邊線條
                  Container(height: 0.5, color: Colors.grey,)
                ],
              ),
              DiscoverCell('表情', 'images/微信表情.png', '', ''),
              SizedBox(height: 10,),
              DiscoverCell('設定', 'images/微信設定.png', '', ''),
            ],
          )
          ),
        ),
        // 相機
        Container(
          margin: EdgeInsets.only(right: 10, top: 25),
          height: 25,
          child: Row(
            mainAxisAlignment: MainAxisAlignment.end,
            children: [
              Image(image: AssetImage('images/相機.png')),
            ],
          ),
        ),
      ],
    ),
  ),
);

} } ```

總結:其實實現這些佈局的方式有很多,如果需要評定哪種佈局方式更好的話,我們遵循頁面複雜度最低的佈局方式總歸是沒有錯的。