前言
我们来完成上一节剩下的任务消息未读数量小红点
- 1.导航栏右边菜单按钮(扫一扫等)
未完成❌ - 2.导航栏下方的搜索框
完成✅ - 3.聊天列表
完成✅ - 4.消息表左滑
未完成❌ - 5.消息未读数量小红点
完成✅
预览
Badge简介
Badge的构造器如下:
const Badge({
super.key,
this.backgroundColor,
this.textColor,
this.smallSize,
this.largeSize,
this.textStyle,
this.padding,
this.alignment,
this.label,
this.isLabelVisible = true,
this.child,
})
其中常用的属性为:
backgroundColor: 背景颜色textColor: 文本颜色smallSize: 徽章的直径(如果label为空)。默认为BadgeTheme的小尺寸,如果主题值为null,则为 6largeSize: 徽章的高度(如果label为非空)。默认为BadgeTheme的大尺寸,如果主题值为 null,则默认为 16。如果默认值被覆盖,则覆盖padding和alignment也可能很有用。textStyle: 文本样式padding: 内边距alignment: 对齐位置label: 文本内容isLabelVisible: 是否显示,默认true
实现
徽章与头像确定位置
- 数值大于99时显示99
- 数值大于1位数时,调整显示的位置
Badge(
label: Text("${num > 99 ? '99' : num}"),
backgroundColor: Colors.red,
alignment: num < 10
? const AlignmentDirectional(40, -6)
: const AlignmentDirectional(30, -6),
child: Image.network(
avatar,
width: 50,
height: 50,
fit: BoxFit.cover,
),
)
抽离控件
为了方便管理维护,抽离成单独的组件
新建文件lib/ui/badge_avatar.dart
import 'package:flutter/material.dart';
class BadgeAvatar extends StatelessWidget {
final String avatar;
final int num;
const BadgeAvatar({Key? key, required this.avatar, required this.num})
: super(key: key);
@override
Widget build(BuildContext context) {
if (num == 0) {
return Image.network(
avatar,
width: 50,
height: 50,
fit: BoxFit.cover,
);
}
return Badge(
label: Text("${num > 99 ? '99' : num}"),
backgroundColor: Colors.red,
alignment: num < 10
? const AlignmentDirectional(40, -6)
: const AlignmentDirectional(30, -6),
child: Image.network(
avatar,
width: 50,
height: 50,
fit: BoxFit.cover,
),
);
}
}
调整message.dart
import 'package:date_format/date_format.dart';
import 'package:flutter/material.dart';
import 'package:pseudo_we_chat/ui/badge_avatar.dart';
import '../../constant/style.dart';
class MessagePage extends StatefulWidget {
const MessagePage({Key? key}) : super(key: key);
@override
State<MessagePage> createState() => _MessagePageState();
}
class _MessagePageState extends State<MessagePage> {
//定义列表数据
final List<MessageData> _messageList = [
MessageData(
id: BigInt.from(1),
avatar:
"https://d36tnp772eyphs.cloudfront.net/blogs/1/2018/02/Taj-Mahal.jpg",
name: "张三123",
message:
"ZFLEX对UIKit的一层封装,主要包含一个数据驱动的列表框架、和UIKit中常用控件的链式拓展,ZZFLEX相关资料正在整理中,目前已经开源,ZFLEX对UIKit的一层封装,主要包含一个数据驱动的列表框架、和UIKit中常用控件的链式拓展,ZZFLEX相关资料正在整理中,目前已经开源",
lastTime: DateTime.now(),
unReadNum: 13),
MessageData(
id: BigInt.from(2),
avatar: "https://www.itying.com/images/flutter/3.png",
name: "云淡风轻",
message: "[图片]",
lastTime: DateTime.now().subtract(const Duration(minutes: 30)),
unReadNum: 0),
MessageData(
id: BigInt.from(1),
avatar: "https://www.itying.com/images/flutter/2.png",
name: "魅力人生",
message: "今天是个好日子。",
lastTime: DateTime.now().subtract(const Duration(hours: 1)),
unReadNum: 9),
MessageData(
id: BigInt.from(1),
avatar: "https://www.itying.com/images/flutter/1.png",
name: "随访飘逸",
message: "你好啊",
lastTime: DateTime.now().subtract(const Duration(hours: 2)),
unReadNum: 66),
MessageData(
id: BigInt.from(1),
avatar:
"https://d36tnp772eyphs.cloudfront.net/blogs/1/2018/02/Taj-Mahal.jpg",
name: "张三",
message: "Hello word!",
lastTime: DateTime.now(),
unReadNum: 120),
MessageData(
id: BigInt.from(1),
avatar: "https://www.itying.com/images/flutter/3.png",
name: "云淡风轻",
message: "[图片]",
lastTime: DateTime.now().subtract(const Duration(minutes: 30)),
unReadNum: 1),
MessageData(
id: BigInt.from(1),
avatar: "https://www.itying.com/images/flutter/2.png",
name: "魅力人生",
message: "今天是个好日子。",
lastTime: DateTime.now().subtract(const Duration(hours: 1)),
unReadNum: 1),
MessageData(
id: BigInt.from(1),
avatar: "https://www.itying.com/images/flutter/1.png",
name: "随访飘逸",
message: "你好啊",
lastTime: DateTime.now().subtract(const Duration(hours: 2)),
unReadNum: 1),
MessageData(
id: BigInt.from(1),
avatar:
"https://d36tnp772eyphs.cloudfront.net/blogs/1/2018/02/Taj-Mahal.jpg",
name: "张三",
message: "Hello word!",
lastTime: DateTime.now(),
unReadNum: 1),
MessageData(
id: BigInt.from(1),
avatar: "https://www.itying.com/images/flutter/3.png",
name: "云淡风轻",
message: "[图片]",
lastTime: DateTime.now().subtract(const Duration(minutes: 30)),
unReadNum: 1),
MessageData(
id: BigInt.from(1),
avatar: "https://www.itying.com/images/flutter/2.png",
name: "魅力人生",
message: "今天是个好日子。",
lastTime: DateTime.now().subtract(const Duration(hours: 1)),
unReadNum: 10),
MessageData(
id: BigInt.from(1),
avatar: "https://www.itying.com/images/flutter/1.png",
name: "随访飘逸",
message: "你好啊",
lastTime: DateTime.now().subtract(const Duration(hours: 2)),
unReadNum: 1),
MessageData(
id: BigInt.from(1),
avatar:
"https://d36tnp772eyphs.cloudfront.net/blogs/1/2018/02/Taj-Mahal.jpg",
name: "张三",
message: "Hello word!",
lastTime: DateTime.now(),
unReadNum: 1),
MessageData(
id: BigInt.from(1),
avatar: "https://www.itying.com/images/flutter/3.png",
name: "云淡风轻",
message: "[图片]",
lastTime: DateTime.now().subtract(const Duration(minutes: 30)),
unReadNum: 1),
MessageData(
id: BigInt.from(1),
avatar: "https://www.itying.com/images/flutter/2.png",
name: "魅力人生",
message: "今天是个好日子。",
lastTime: DateTime.now().subtract(const Duration(hours: 1)),
unReadNum: 1),
MessageData(
id: BigInt.from(1),
avatar: "https://www.itying.com/images/flutter/1.png",
name: "随访飘逸",
message: "你好啊",
lastTime: DateTime.now().subtract(const Duration(hours: 2)),
unReadNum: 1)
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
title: const Text(
"微信",
style: TextStyle(color: Style.appBarTextColor),
),
backgroundColor: Style.appBarBackgroundColor,
),
body: Container(
color: Style.contentBackgroundColor,
child: Column(
children: [
//聊天列表
Expanded(
flex: 1,
child: ListView.builder(
itemCount: _messageList.length,
itemBuilder: (BuildContext context, int index) {
var item = _messageList[index];
return Column(
children: [
//搜索按钮
index == 0
? Container(
width: double.infinity,
padding: const EdgeInsets.only(
left: 10, right: 10, top: 5, bottom: 5),
decoration: const BoxDecoration(
color:
Style.messageSearchBackgroundColor),
child: ElevatedButton(
style: ButtonStyle(
elevation:
MaterialStateProperty.all(0),
backgroundColor:
MaterialStateProperty.all(
Colors.white)),
child: Row(
mainAxisAlignment:
MainAxisAlignment.center,
children: const [
Icon(
Icons.search,
color: Colors.grey,
),
Text(
"搜索",
style: TextStyle(
fontSize: 20,
color: Colors.grey),
)
],
),
onPressed: () {},
),
)
: const SizedBox(),
Column(
children: [
ListTile(
//头像
leading: BadgeAvatar(
avatar: item.avatar,
num: item.unReadNum,
),
//名称和时间
title: Row(
children: [
//名称
Expanded(
flex: 17,
child: Text(item.name,
style: const TextStyle(
fontSize: 18,
fontWeight:
FontWeight.w500))),
//右侧时间
Expanded(
flex: 3,
child: Text(
formatDate(
item.lastTime, [HH, ':', nn]),
style: const TextStyle(
fontSize: 12,
color: Colors.grey),
)),
],
),
//消息内容
subtitle: Padding(
padding: const EdgeInsets.only(top: 10),
child: Text(item.message,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: const TextStyle(
fontSize: 16, color: Colors.grey)),
),
),
//下划线
Padding(
padding: index == _messageList.length - 1
? const EdgeInsets.only(left: 0)
: const EdgeInsets.only(left: 80),
child: const Divider(),
)
],
)
],
);
}),
),
],
)));
}
}
class MessageData {
const MessageData(
{required this.id,
required this.avatar,
required this.name,
required this.message,
required this.unReadNum,
required this.lastTime});
/// id
final BigInt id;
/// 头像
final String avatar;
/// 名称
final String name;
/// 消息
final String message;
/// 消息未读数量
final int unReadNum;
/// 消息时间
final DateTime lastTime;
}