Flutter —— 通讯录页面

865 阅读2分钟

这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战

1. 通讯录页面

做完了发现页面和我的页面,接下来做通讯录页面。将_currentIndex 改成1方便开发。 发现通讯录页面还是要用到之前的_themColor,那么就将_themColor抽取出来以便公用。 创建一个文件来存放这些需要公用的常量。然后放入主题色之后,在别的地方import这个文件就能使用了。

//主题色
const Color weChatThemColor = Color.fromRGBO(220, 220, 220, 1.0);

然后就可以将通讯录页面的Appbar的背景色改为weChatThemColor了,接下来要添加右上角的按钮,这里就可以用AppBar里面的actions。在actions里面添加一个图片,这里可以放多个,多个的话会从左到右排列。

 actions: [
          Container(
            margin: EdgeInsets.only(right: 10),
            child: Image(
              image: AssetImage("images/icon_friends_add.png"),
              height: 20,
              width: 20,
            ),
          ),
        ],

接着在Container外面包一层GestureDetector来添加手势响应。

在这里插入图片描述

接下来要做通讯录页的cell,那么如果只有通讯录页里面用,则可以放在一个文件里面,因为在一个文件里面,所以可以使用_令其私有,私有后本文件里面依然可以使用。

class _FriendCell extends StatelessWidget {
  _FriendCell({this.imageUrl, this.name, this.groupTitle, this.imageAssets});

  final String? imageUrl;
  final String? name;
  final String? groupTitle;
  final String? imageAssets;
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

根据需要的内容创建模型

class Friends {
  Friends({this.imageUrl, this.name, this.indexLetter,this.ImageAssets});
  final String? imageUrl;
  final String? name;
  final String? indexLetter;
  final String? ImageAssets;
}

创建完之后,可以将通讯录页面scaffold里面的body设为:

	Container(
        child: ListView.builder(
          itemBuilder: _itemForRow,
          itemCount: datas.length + _headerData.length,
        ),
        color: weChatThemColor,
      ),`

接下来添加头部四个的数据,这里是asset图片所以传ImageAssets。

  final List<Friends> _headerData = [
    Friends(ImageAssets: 'images/新的朋友.png', name: '新的朋友'),
    Friends(ImageAssets: 'images/群聊.png', name: '群聊'),
    Friends(ImageAssets: 'images/标签.png', name: '标签'),
    Friends(ImageAssets: 'images/公众号.png', name: '公众号'), 
  ];

然后构建_itemForRow方法,就是在_itemForRow里面判断如果小于_headerData的长度,则传入imageAssets,否则就是imageUrl。注意这里datas的index需要减去_headerData的长度,否则会缺少前面几个数据。

 Widget _itemForRow(BuildContext context, int index) {
    if (index < _headerData.length) {
      return _FriendCell(
          imageAssets: _headerData[index].ImageAssets,
          name: _headerData[index].name);
    } else {
      return _FriendCell(
          imageUrl: datas[index - _headerData.length].imageUrl, name: datas[index -  _headerData.length ].name);
    }
  }

然后开始写界面,这里简单的使用一个Row来包含图片和昵称,然后使用ClipRRect将图片剪裁为圆角矩形,并且判断imageUrl是否为空,不为空则使用NetworkImage加载网络图片,否则就使用AssetImage加载本地图片。

Widget build(BuildContext context) {
    return Container(
      color: Colors.white,
      child: Row(
        children: [
          Container(
            margin: EdgeInsets.all(10),
            width: 34,
            height: 34,
            child: ClipRRect(
              //剪裁为圆角矩形
              borderRadius: BorderRadius.circular(5.0),
              child:
                   Image(
                      image:  imageUrl != null ? NetworkImage(imageUrl!) : AssetImage(imageAssets ?? "") as ImageProvider,
                      height: 20,
                      width: 20,
                    )
            ),
          ), //图片
          Container(
            child: Text(name ?? "",style: const TextStyle(fontSize: 18),),
          ), //昵称
        ],
      ),
    );

运行后得到:

在这里插入图片描述

发现少了下划线,那么这里看到下划线是从昵称开始的,这里就可以选择将昵称和下划线做成一个整体。

 Container(
            width: screenWidth(context) - 54,
            child: Column(
              children: [
                Container(
                  child: Text(name ?? "", style: const TextStyle(fontSize: 18)),
                  alignment: Alignment.centerLeft,
                  height: 54,
                ),
                Container(
                  color: weChatThemColor,
                  height: 0.5,
                )
              ],
            ),
          ), //昵称

接下来要实现分组显示,在_itemForRow中多传一个groupTitle,这里在else添加因为上面的没有title。

 Widget _itemForRow(BuildContext context, int index) {
    if (index < _headerData.length) {
      return _FriendCell(
          imageAssets: _headerData[index].ImageAssets,
          name: _headerData[index].name);
    } else {
      return _FriendCell(
        imageUrl: datas[index - _headerData.length].imageUrl,
        name: datas[index - _headerData.length].name,
        groupTitle: datas[index - _headerData.length].indexLetter,
      );
    }

然后到_FriendCell里面将刚才的内容用Column包起来,然后在cell内容上面添加头部。

在这里插入图片描述

在头部里面设置alignment为Alignment.centerLeft,为了避免文字太左边给个10的内边距,然后根据是否有groupTitle判断是否显示这个控件。

  Container(
          alignment: Alignment.centerLeft,
          padding: EdgeInsets.only(left: 10),
          height: groupTitle != null ? 30 : 0,
          color: weChatThemColor,
          child: groupTitle != null ? Text(groupTitle!,style: TextStyle(color: Colors.grey),) : null,
        ), 

这里还需要对数据进行排序,那么这里先声明一个_listDatas,然后在initState的时候进行添加数据,然后根据数据的indexLetter进行排序。

  void initState() {
    // TODO: implement initState
    super.initState();
    //_listDatas = [];
    _listDatas.addAll(datas);
       _listDatas.sort((Friends a,Friends b)
        {
         return (a.indexLetter ?? "").compareTo(b.indexLetter ?? "");
        });
  }

然后在_itemForRow里面根据判断当前indexLetter和前面一个indexLetter是否相等来判断是否传groupTitle。

Widget _itemForRow(BuildContext context, int index) {
    if (index < _headerData.length) {
      return _FriendCell(
          imageAssets: _headerData[index].ImageAssets,
          name: _headerData[index].name);
    }
    bool _hiddenIndexLetter =  index - 4 > 0 &&
        _listDatas[index - _headerData.length].indexLetter ==
            _listDatas[index - _headerData.length - 1].indexLetter;

    return _FriendCell(
      imageUrl: _listDatas[index - _headerData.length].imageUrl,
      name: _listDatas[index - _headerData.length].name,
      groupTitle: _hiddenIndexLetter ? null : _listDatas[index - _headerData.length].indexLetter,
    );

  }