flutter-仿微信项目实战四(聊天列表、列表弹层)

前言

前面已经介绍过网络请求了,这里主要介绍聊天列表搭建,还有扫一扫那里的弹层,该弹层主要采用系统控件 PopupMenuButton

ps:如果想自己定制弹窗,前面有介绍Dialog的自定义

源码地址

自定义dialog弹出层地址

聊天列表

按照之前的介绍,其实ListView.builder很容易就实现聊天列表,这里介绍他主要是引出 ListTile 类,至少能减少一些代码嵌套

先看看效果

image.png

代码如下所示:

//ListView.builder表格复用
ListView.builder(
    itemBuilder: itemForRow,
    itemCount: list.length,
)

//Row的效果如下
Widget itemForRow(BuildContext context, int index) {
  //这个就是跟微信首页聊天的一样,可以设置图片,标题内容
  final item = list[index];
  //使用ListTile组件
  return ListTile(
    //设置标题
    title: Text(item.name,
        style: const TextStyle(fontSize: 12, color: Colors.black87)),
    //设置子标题
    subtitle: Text(
      item.message,
      style: const TextStyle(fontSize: 12, color: Colors.black87),
      overflow: TextOverflow.ellipsis, //设置省略效果
    ),
    //设置左侧视图
    leading: CircleAvatar(backgroundImage: NetworkImage(item.imgUrl)),
  );
}
复制代码

ListTile 默认没有下划线,如果想要下划线,外面包裹一层就可以了,如下所示

Column(
  children: [
    ListTile(
      title: Text(item.name,
          style: const TextStyle(fontSize: 12, color: Colors.black87)),
      subtitle: Text(
        item.message,
        style: const TextStyle(fontSize: 12, color: Colors.black87),
        overflow: TextOverflow.ellipsis, //设置省略效果
      ),
      leading: CircleAvatar(backgroundImage: NetworkImage(item.imgUrl)),
    ),
    Row(
      children: [
        //两个Container都可以
        Container(width: 46, height: 1, color: Colors.white),
        //Container内容默认不会填充满整个屏幕
        Expanded(child: Container(height: 1, color: const Color.fromRGBO(0xe1, 0xe1, 0xe1, 1))),
      ],
    )
  ],
);
复制代码

聊天数据

通过 http 框架请求,然后一步一步翻译成我们使用的 class 类

//这里主要简介数据获取,网络失败后的 catch 自行处理即可
Future<List<dynamic>> getData() async {
  //使用 http 框架的 get请求网络数据,请求的结果在body中,为 json字符串
  Response res = await get(
      Uri.parse("http://rap2api.taobao.org/app/mock/224518/api/chat/list"));
  //通过系统的 dart:convert 框架, 使用jsonDecode将json字符串转化为map
  final body = jsonDecode(res.body);
  //取出数据,转化为我们的类
  final chatList = body["chat_list"];
  for (final item in chatList) {
    // fromJson为我们定义的工厂构造方法,将 Map 转化为我们的对象
    final chatData = ChatListData.formJson(item);
    list.add(chatData);
  }
  return list;
}
复制代码

ChatListData 类的定义如下所示,由于没有看到 dart 反射相关,这部分内容只能自己编写了(不像JavaScript 没有class类的限制可以直接赋值与调用)

class ChatListData {
  final String imgUrl;
  final String name;
  final String message;

  ChatListData({required this.imgUrl, required this.name, required this.message});

  factory ChatListData.formJson(Map<String, dynamic> json) {
    return ChatListData(
        imgUrl: json["imageUrl"],
        name: json['name'],
        message: json['message']);
  }
}
复制代码

扫一扫弹层

点击微信聊天页右上角加号,弹出扫一扫的点击界面,先看效果

image.png

先声明一下右侧弹层的数据信息,这里直接采用的 Map 解决,字节也可以放到一个类中,如下所示

List popList = [
  {'imgUrl': 'images/发起群聊.png', 'name': '发起群聊'},
  {'imgUrl': 'images/添加朋友.png', 'name': '添加朋友'},
  {'imgUrl': 'images/扫一扫1.png', 'name': '扫一扫'},
  {'imgUrl': 'images/收付款.png', 'name': '收付款'},
];
复制代码

然后介绍弹层,弹层 PopupMenuButton 实际上显示默认是一个按钮组件,只是在加入了 Dialog,点击时会弹出弹窗层,多了这么一个功能而已

//导航
AppBar(
  title: const Text("聊天"),
  foregroundColor: Colors.black,
  backgroundColor: const Color.fromRGBO(0xe1, 0xe1, 0xe1, 1),
  elevation: 0, //去掉阴影
  //导航右侧按钮组
  actions: [
    //外部container可以用来调整间距,不放到里面的Container
    //是因为这个margin会增加点击区域,毕竟外面包了一层TextButton一样的东西
    Container(
      margin: const EdgeInsets.only(right: 10),
      //使用 PopupMenuButton 来定义右侧点击弹层功能
      child: PopupMenuButton(
        //弹层实物位置,相对于当前组件的偏移
        offset: const Offset(0, 56),
        //我们看到的按钮的信息,组件给其默认添加点击事件
        child: Container(
          width: 40,
          height: 40,
          alignment: Alignment.center,
          child: Image.asset("images/圆加.png", width: 20),
        ),
        //返回内部组件信息列表,单行 item 使用 PopupMenuItem
        //使用 .map<PopupMenuItem> 的原因可以动态生成多个 item
        itemBuilder: (BuildContext context) {
          return popList.map<PopupMenuItem>((item) {
            return PopupMenuItem(
              //水平布局,左侧图片,右侧问题,中间间隔使用 Sizebox即可
              child: Row(
                children: [
                  Image.asset(item['imgUrl'], width: 18),
                  const SizedBox(width: 10),
                  Text(item['name'],
                      style: const TextStyle(color: Colors.white)),
                ],
              ),
            );
          }).toList();
        },
      ),
    )
  ],
),
复制代码

仅仅这样发现,弹窗背景颜色不对,弹窗的背景我们需要在 MaterialApp 组件的 theme 属性中加入cardColor才可以

MaterialApp(
  //去掉debug自选
  debugShowCheckedModeBanner: false,
  //Android任务管理器界面title,可以设置称自己的app名字
  title: "Flutter Demo",
  theme: ThemeData(
    //这个就是设置默认弹层的背景颜色
    cardColor: const Color.fromRGBO(0x33, 0x33, 0x33, 0.8),
  ),
  home: const HomePage()
);
复制代码

最后

快来试一下吧,越用越顺手

猜你喜欢

转载自juejin.im/post/7087422451893665828