Nealyang/PersonalBlog

首页List UI编写

Nealyang opened this issue · 0 comments

介绍

这一章节中,我们将完成首页List部分的编写

该章节后,咱们的list界面应该长这个样子
img

cell 结构分析

cell

cell的整体是一个Column结构,每一行中,再是一个横向排列的。底部的点赞和评论按钮结构比较统一,可以考虑单独抽出来作为一个新的widget

作者和标题之间的点(.)无法通过文字直接模拟,所以这里我们把点也同样抽出来作为一个widget

cell 相关widget编写

个人编写习惯:先从小写到大,在大(iindexListCell)中引入你要写的小(inTextDot)以方便看样式,然后编写玩小的后再放到大的中使用.

  • lib/widgets/inTextDot.dart
  import 'package:flutter/material.dart';
  
  class InTextDot extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      return Container(
        width: 3.0,
        height: 3.0,
        margin: const EdgeInsets.symmetric(horizontal: 6.0),
        decoration: BoxDecoration(
            color: Color(0xFFB2BAC2),
            borderRadius: BorderRadius.all(Radius.circular(3.0))),
      );
    }
  }

文字中的点,比较容易,我们直接写一个container,用于做点,然后边距也作为这个widget该有的属性。注意看这里我们样式中对于Container自身的装饰属性所谓decoration,同样我们之前说的vscode功能,鼠标悬停在Widget上,可以看到他的属性,以及这个属性的值类型

de

  • lib/widgets/goodAndCommentCell.dart
    code
    这里点赞和评论的cell很简单,其实仔细分析可以看到,点赞和评论也是同一个组件才对,不应该写两遍。这里我就不优化,之所以是截图,希望大家可以自己动手,发挥自己的聪明才智,编写这一个UI,或者说,优化我上面的代码~

goodAndCommentCell 代码链接

  • lib/widgets/indexListCell.dart
    首先我们定义一些基础变量
  final IndexCell cellInfo;

  IndexListCell({Key key, this.cellInfo}) : super(key: key);

  TextStyle titleTextStyle = TextStyle(
    color: Color(0xFFB2BAC2),
    fontWeight: FontWeight.w300,
    fontSize: 13.0,
  );

由于第一行中,热门、专栏、推荐等是不一定存在的,所以这里我们需要写一个方法去判断是否存在这个字段

  List<Widget> _buildFirstRow() {
      List<Widget> _listRow = new List();
      if (cellInfo.hot) {
        _listRow.add(Text(
          '热',
          style: TextStyle(
            color: Color(0xFFF53040),
            fontWeight: FontWeight.w600,
          ),
        ));
        _listRow.add(InTextDot());
      }
      if (cellInfo.isCollection == 'post') {
        _listRow.add(Text(
          '专栏',
          style: TextStyle(
            color: Color(0xFFBC30DA),
            fontWeight: FontWeight.w600,
          ),
        ));
        _listRow.add(InTextDot());
      }
      _listRow.add(Text(cellInfo.username, style: titleTextStyle));
      _listRow.add(InTextDot());
      _listRow.add(Text(cellInfo.createdTime, style: titleTextStyle));
      _listRow.add(InTextDot());
      _listRow.add(Expanded(
        //防止文本超长
        child: Text(
          cellInfo.tag,
          style: titleTextStyle,
          overflow: TextOverflow.ellipsis,
        ),
      ));
      return _listRow;
    }

而cell的布局如上我们分析的那样,Column中一行一行的搞下去

所以此处build方法如下:

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.all(10.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Container(
            height: 20.0,
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.center,
              children: _buildFirstRow(),
            ),
          ),
          Container(
            margin: const EdgeInsets.symmetric(vertical: 9.0),
            child: Text(
              cellInfo.title,
              style: TextStyle(
                color: Color(0xFF393C3F),
                fontSize: 14.0,
                fontWeight: FontWeight.w600,
              ),
              overflow: TextOverflow.ellipsis,
            ),
          ),
          GoodAndCommentCell(cellInfo.collectionCount, cellInfo.commentCount),
          SizedBox(
            height: 15.0,
          ),
          Divider(
            height: 2.0,
          ),
        ],
      ),
    );
  }

对于一些样式的调整和边距的调整,大家可以根据自己的审美哈~实际开发中,这些也根据设计师给我们的样式来做相应开发。

头部header

分析线上页面,我们可以看到在列表的cell上部,还有个头部,在我们登陆状态和非登录状态是不同的,所以这里,我们肯定是需要给他一个Widget组件来实现的

  • lib/widgets/indexListHeader.dart
  import 'package:flutter/material.dart';
  
  class IndexListHeader extends StatelessWidget {
    final bool hasLogin;
  
    IndexListHeader(this.hasLogin);
  
    @override
    Widget build(BuildContext context) {
      if (hasLogin) {
      } else {}
      return Container(
        padding: const EdgeInsets.all(10.0),
        child: Column(
          children: <Widget>[
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: <Widget>[
                Text(
                  '热门文章',
                  style: TextStyle(
                    color: Color(0xFF434343),
                    fontSize: 16.0,
                    fontWeight: FontWeight.bold
                  ),
                ),
                FlatButton(
                  child: Text('查看更多', style: TextStyle(color: Color(0xFF757575))),
                  onPressed: () {
                    print('查看更多');
                  },
                )
              ],
            ),
            Divider(),
          ],
        ),
      );
    }
  }

这里我们还未涉及到登陆,所以暂时先留一个口子(构造函数)在这,后面做到了登陆部分再回来修改UI。

之前我们说到在使用一些widget的时候,有些属性是必须的,不然编译器会报错,like this

worning

当然,我们可以按住command键,点击widget,到源码中去查看一些相关属性的编写和类型

ListView.builder

毫无疑问,这里我们使用ListView 布局,因为考虑性能且不确定长度,这里我们不会给ListView传入children而是直接只用它的构造函数 builder,而且这样的长列表对于非可视区域部分的cell还会做相应的资源回收。

  • lib/pages/indexPage.dart
  _renderList(context , index){
    if(index == 0){
      return IndexListHeader(false);
    }
    return  IndexListCell(cellInfo: _listData[index-1]);
  }
  
  @override
  Widget build(BuildContext context) {
      print(_listData.length);
      if (_listData.length == 0) {
        return Center(
          child: CircularProgressIndicator(),
        );
      }
      return ListView.builder(
        itemCount: _listData.length+1,//添加一个header
        itemBuilder: (context,index)=> _renderList(context,index),
      );
    }
  }

注意上面我们给ListView传递的长度是length+1,因为我们要塞列表的头部~

同样,我们也处理了数据未请求到的时候loading的展示

至此,我们的app已经有点样子出来了有么有

总结

如上,我们基本首页已经有了雏形,至此,你应该学会

  • ListView的使用
  • 组件的拆分、细化和组合
  • VsCode的一些使用技巧