flutter chat UI 聊天界面实战
flutter chat UI 聊天界面实战
上一篇笔记完成了一个登陆界面的花架子,只能远观,不能使用,这篇再试一下一个聊天界面UI,依旧是花架子,哈哈。
寻找聊天界面模板
依旧先找一个好看的模板来照猫画虎吧。找了半天,最终决定用下边这个ui当做模板,个人感觉挺好看的的。
UI实现
话不多说,开始着手实现这个模板。首先分析一下模板布局和元素,看来看去,想来想去,对于我来说这个模板还是有点复杂,还是简化的实现吧。
将模板分成两部分、四个模块来实现,最右边的一竖条感觉有点难看,直接砍掉他,把这部分的按钮放在聊天框的上部分来实现。
两部分拆分
首先,依旧是新建项目并运行,实现一个空白模板,和上一篇login UI实现的一样,先使用Row水平布局组件,将整个白板分割成Leftbody、Rightbody两部分。
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false, //去掉右上角debug标识
theme: ThemeData(
//主题设置
primarySwatch: Colors.blue,
),
home: const SelectionArea(
//子组件支持文字选定 3.3新特性
child: Scaffold(
//子组件
body: MyAppbody(),
),
),
);
}
}
class MyAppbody extends StatelessWidget {
const MyAppbody({super.key});
@override
Widget build(BuildContext context) {
return Center(
child: Row(
//水平布局
children: const <Widget>[
//子组件
Expanded(
//空间占比组件
flex: 1, //左侧空间占比
child: Leftbody(), //左侧布局
),
Expanded(
flex: 2, //右侧空间占比
child: Rightbody(), //右侧布局
),
],
),
);
}
}
Leftbody实现
在lib目录下新建leftbody文件夹,在此文件夹下新建leftbody.dart,将左侧部分所有代码都写在这个文件中。分析左侧部分布局,外层先是一个竖向布局,然后竖向布局里面依次放置图标、用户、好友列表。
import 'package:flutter/material.dart';
class Leftbody extends StatelessWidget {
const Leftbody({super.key});
@override
Widget build(BuildContext context) {
return Column(
children: const <Widget>[
Expanded(
//空间占比组件
flex: 1, //空间占比
child: Padding(
padding: EdgeInsets.fromLTRB(0, 0, 0, 0), //填充
child: Logo(), //logo实现
),
),
Expanded(
//空间占比组件
flex: 1, //空间占比
child: Padding(
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
//child: User(), //User
),
),
Expanded(
//空间占比组件
flex: 1, //空间占比
child: Padding(
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
//child: Friends(), //Friends
),
),
],
);
}
}
招牌logo部分
logo部分是一个图片和一段文字构成,我我们就横向放置一个图片组件和一个文字组件来实现,图片利用CircleAvatar进行圆角切割,文字设置为粗体。
class Logo extends StatelessWidget {
const Logo({super.key});
@override
Widget build(BuildContext context) {
return Row(
children: const <Widget>[
CircleAvatar(
//图片圆形剪裁
radius: 20, //圆形直径,(半径)?
backgroundColor: Colors.white, //背景颜色设置为白色
backgroundImage: AssetImage(
"images/1.png",//logo图片
),
),
Padding(
padding: EdgeInsets.fromLTRB(10, 0, 0, 0),
),
Text(
"Messenger",
style: TextStyle(
fontSize: 20, //字体大小
fontWeight: FontWeight.bold, //字体加粗
),
),
],
);
}
}
效果:
分割线
在logo下边有一个分割线,我们用Divider实现,可以设置它的左右缩进长度和颜色。
Divider(height: 1.0,indent: 60.0,endIndent:60.0,color: Colors.grey,),//分割线
效果: 4.png
用户信息展示部分
这部分是使用者自己的信息展示,包括头像,主标题名字,副标题介绍,在线情况,后边还有一个设置图标按钮。先实现一个带头像和通知圆点的个人信息展示模块,这里简化实现了,去掉了Online展示模块,因为我不会。。。
使用ListTile组件实现列表,头部使用Badge插件实现小圆点通知,通过position设置小圆点显示位置,通过badgeColor设置圆点颜色,通过borderSide设置圆点白色边框,头像显示依然使用CircleAvatar组件,尾部用一个不可点击的图标装饰,当然也可以放一个带图标按钮。
class User extends StatelessWidget {
const User({super.key});
@override
Widget build(BuildContext context) {
return ListTile(
leading: Badge(
//头部部件
//通知小圆点
badgeColor: Colors.green, //小圆点颜色
position: const BadgePosition(
start: 35, top: 35, end: 0, bottom: 0), //小圆点显示位置
borderSide:
const BorderSide(color: Colors.white, width: 1.5), //外层白色圆圈框框
child: const CircleAvatar(
//图片圆形剪裁
radius: 25, //圆形直径,(半径)?
backgroundColor: Colors.white, //背景颜色设置为白色
backgroundImage: AssetImage(
"images/1.jpg", //图片
),
),
),
title: const Text(
"George xiaolan",
style: TextStyle(
fontSize: 15, //字体大小
fontWeight: FontWeight.bold, //字体加粗
),
), //标题
subtitle: const Text(
"Google",
style: TextStyle(
fontSize: 10, //字体大小
fontWeight: FontWeight.bold, //字体加粗
),
),
trailing: const Icon(Icons.settings), //尾部图标
);
}
}
效果:
搜索框
对照模板,简化处理,略过Active Chats,直接实现好友搜索框。我使用TextField实现搜索框,使用prefixIcon、suffixIcon实现头尾搜索图标和叉叉图标,通过border设置圆角显示,通过fillColor设置背景为灰色同时设置透明度。hintText可以设置提示文字。这样一个简单的搜索框就实现了。
class SearchWidget extends StatefulWidget {
final double? height; // 高度
final double? width; // 宽度
final String? hintText; // 输入提示
final ValueChanged<String>? onEditingComplete; // 编辑完成的事件回调
const SearchWidget(
{Key? key,
this.height,
this.width,
this.hintText,
this.onEditingComplete})
: super(key: key);
@override
State<SearchWidget> createState() => _SearchWidgetState();
}
class _SearchWidgetState extends State<SearchWidget> {
var controller = TextEditingController();
@override
void initState() {
super.initState();
}
/// 清除查询关键词
clearKeywords() {
controller.text = "";
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constrains) {
return TextField(
controller: controller,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.search),//头部搜索图标
filled: true,
fillColor: Colors.grey.withAlpha(50), // 设置输入框背景色为灰色,并设置透明度
hintText: "Search people",
hintStyle: const TextStyle(color: Colors.grey, fontSize: 14),
contentPadding: const EdgeInsets.only(bottom: 10),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),//圆角边框
borderSide: BorderSide.none),
suffixIcon: IconButton(//尾部叉叉图标
icon: const Icon(
Icons.close,
size: 17,
),
onPressed: clearKeywords,//清空操作
splashColor: Theme.of(context).primaryColor,
)
),
);
});
}
}
效果:
好友列表
接着实现好友列表。使用ListView包裹ListTile实现列表。ListTile基本上可以实现很多功能,包括头像、标题、子标题,需要特别关注的是上线状态小圆圈和消息提示小圆圈的实现,我们依旧使用Badge插件实现。为了简单起见,并且自己能力有限,所以好友列表使用若干ListTile实现,这种方式只适合实现一个花架子,如果真实使用,这样肯定是行不通的。
class Friends extends StatelessWidget {
const Friends({super.key});
@override
Widget build(BuildContext context) {
return ListView(
scrollDirection: Axis.vertical,
children: const <Widget>[
Friends1(),
Friends3(),
Friends4(),
Friends5(),
Friends6(),
Friends7(),
Friends8(),
Friends9(),
],
);
}
}
class Friends1 extends StatelessWidget {
const Friends1({super.key});
@override
Widget build(BuildContext context) {
return ListTile(
onTap: () {},
leading: Badge(
//头部部件
//通知小圆点
badgeColor: Colors.green, //小圆点颜色
position: const BadgePosition(
start: 35, top: 35, end: 0, bottom: 0), //小圆点显示位置
borderSide:
const BorderSide(color: Colors.white, width: 1.5), //外层白色圆圈框框
child: const CircleAvatar(
//图片圆形剪裁
radius: 25, //圆形直径,(半径)?
backgroundColor: Colors.white, //背景颜色设置为白色
backgroundImage: AssetImage(
"images/2.jpg", //图片
),
),
),
title: const Text(
"Facebo anaolan",
style: TextStyle(
fontSize: 15, //字体大小
fontWeight: FontWeight.bold, //字体加粗
),
), //标题
subtitle: const Text(
"Facebow",
style: TextStyle(
fontSize: 10, //字体大小
fontWeight: FontWeight.bold, //字体加粗
),
),
trailing: Badge(
//尾部部件
//通知小圆点
// padding: EdgeInsets.all(2),
badgeContent: const Text(
'3',
style: TextStyle(
fontSize: 10, //字体大小
// fontWeight: FontWeight.bold, //字体加粗
color: Colors.white,
),
),
badgeColor: Colors.green, //小圆点颜色
borderRadius: BorderRadius.circular(10),
position: const BadgePosition(
start: 10, top: 10, end: 0, bottom: 0), //小圆点显示位置
borderSide: const BorderSide(color: Colors.white, width: 1), //外层白色圆圈框框
), //尾部图标
);
}
}
效果:
总结
这个项目暂时先实现到这里。下边是模板和自己的效果图,不得不说差距还是很大的,并且只能看看,不能实际使用,自己的差距还是很大的,后边还要继续学习。还有很多坑需要实现,包括列表的动态实现,时间戳显示,搜索栏实现条目搜索,上线下线图标变化,在以后的学习中再慢慢补课吧。
转载自:https://juejin.cn/post/7231527422200512570