Flutter 仿写微信发现、我的页面
发现页面实现
这里我们仿照微信的发现页面用 Flutter
类实现这页面的简单布局及每个 cell
的点击效果及点击每条 cell
之后跳转到一个新的页面。这里我们分几步分别来实现这些功能。
自定义 cell
针对 cell
的布局我们可以分为两部分,左边跟右边,左边是主图片加标题,右边是子标题、 子图片加箭头。主图片名称、主标题、子标题、子图片名称这些都可以由于 cell
初始化的时候由外部传进来。这里主图片、标题跟箭头是固定的每个 cell
都有的,子标题、 子图片是可选的。针对 cell
布局的代码如下。
String title;
String imageName;
String subTitle;
String subImageName;
DiscoverCell(this.title, this.imageName, this.subTitle, this.subImageName);
Container(
color: _currentColor,
height: 55,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// left
Container(
padding: EdgeInsets.all(10),
child: Row(
children: [
// 图标
Image(image: AssetImage(widget.imageName), width: 20,),
// 间隙
SizedBox(width: 15,),
// Title
Text(widget.title),
],
),
),
// right
Container(
padding: EdgeInsets.all(10),
child: Row(
children: [
// subTitle
widget.subTitle != null ? Text(widget.subTitle) : Text(''),
// subImage
widget.subImageName.length > 0 ? Image.asset(widget.subImageName, width: 15,) : Container(),
// 箭头
Image(image: AssetImage('images/icon_right.png'), width: 15,)
],
),
),
],
),
)
列表布局
class _DiscoverPageState extends State<DiscoverPage> {
Color _themColor = Color.fromRGBO(230, 230, 230, 1.0);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: _themColor,
// 安卓里面用到,切 app 的时候显示
centerTitle: true,
title: Text('发现'),
elevation: 0.0,
),
body: Container(
height: 800,
color: _themColor,
child: ListView(
children: [
DiscoverCell('朋友圈', 'images/朋友圈.png', '', ''),
SizedBox(height: 10,),
DiscoverCell('扫一扫', 'images/扫一扫2.png', '', ''),
//分割线
Row(
children: [
// 左边线条
Container(width: 50, height: 0.5, color: Colors.white,),
// 右边线条
Container(height: 0.5, color: Colors.grey,)
],
),
DiscoverCell('摇一摇', 'images/摇一摇.png', '', ''),
SizedBox(height: 10,),
DiscoverCell('看一看', 'images/看一看icon.png', '', ''),
//分割线
Row(
children: [
// 左边线条
Container(width: 50, height: 0.5, color: Colors.white,),
// 右边线条
Container(height: 0.5, color: Colors.grey,)
],
),
DiscoverCell('搜一搜', 'images/搜一搜.png', '', ''),
SizedBox(height: 10,),
DiscoverCell('附近的人', 'images/附近的人icon.png', '', ''),
SizedBox(height: 10,),
DiscoverCell('购物', 'images/购物.png', '双十一限时特价', 'images/badge.png'),
Row(
children: [
// 左边线条
Container(width: 50, height: 0.5, color: Colors.white,),
// 右边线条
Container(height: 0.5, color: Colors.grey,)
],
),
DiscoverCell('游戏', 'images/游戏.png', '', ''),
SizedBox(height: 10,),
DiscoverCell('小程序', 'images/小程序.png', '', ''),
],
),
)
);
}
}
针对列表的布局我们用的是 ListView
,在 children
里面按顺序添加每条 cell
数据,这里每组之间的间隔我们用 SizedBox
,cell
上的下划线我们用 Row
来实现,分为左边线条跟右边线条。
cell 点击跳转
return GestureDetector(
// cell 手势点击
onTap: (){
Navigator.of(context).push(
MaterialPageRoute(builder:
(BuildContext context) => DiscoverChildPage(widget.title)
)
);
这里我们定义一个新的页面 DiscoverChildPage
标题由 cell
点击的时候传入。在 cell
中我们添加点击方法,然后 push
到一个新的页面。
cell 添加点击状态
GestureDetector(
// cell 手势点击
onTap: (){
Navigator.of(context).push(
MaterialPageRoute(builder:
(BuildContext context) => DiscoverChildPage(widget.title)
)
);
setState(() {
_currentColor = Colors.white;
});
},
// cell 手势点击下去
onTapDown: (TapDownDetails details){
setState(() {
_currentColor = Colors.grey;
});
},
// cell 手势点击取消
onTapCancel: (){
setState(() {
_currentColor = Colors.white;
});
},
child: Container(
color: _currentColor,
),
);
为 cell
加上点击状态的话就需要继承于有状态的 Widget
,然后在不同的点击状态下设置不同的颜色,然后调用 setState
方法。因为调用 setState
方法的时候会重新构建 widget
,所以针对复杂的控件的时候我们只让需要改变状态的子控件继承于 StatefulWidget
,把需要改变的部分抽取出来。我们这里因为 cell
整体的子控件也不多,所以我们就直接让 cell
继承于 StatefulWidget
。也是为了偷下懒🐶。
我的页面实现
我的页面首先我们可以分为两大块,列表跟相机,这里我们采用 Stack
部件来布局。其中列表部分又可以分为两大块头部跟底部 cell
部分。按照这种布局思路我们的代码如下。
class _MinePageState extends State<MinePage> {
Widget headerWidget() {
return Container(
height: 200,
color: Colors.white,
child: Container(
margin: EdgeInsets.only(top: 90, bottom: 20, left: 16, right: 10),
child: Row(
children: [
// 头像
Container(
width: 70,
height: 70,
// 设置圆角属性
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
image: DecorationImage(
image: AssetImage('images/ChenXi.JPG')
)
),
),
// 右边部分
Expanded(child: Container(
padding: EdgeInsets.only(left: 10, top: 8, right: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 昵称
Container(
height: 35,
child: Text('Chenxi', style: TextStyle(fontSize: 25, color: Colors.black87),)
),
// 微信号加箭头
Container(
height: 35,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('微信号:CX123', style: TextStyle(fontSize: 17, color: Colors.grey),),
Image(image: AssetImage('images/icon_right.png'), width: 15,)
],
),
),
],
),
)),
],
),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Color.fromRGBO(230, 230, 230, 1.0),
child: Stack(
children: [
// 列表
Container(
child: MediaQuery.removePadding(
removeTop: true,
context: context,
child: ListView(
children: [
// 头部
headerWidget(),
// 列表
SizedBox(height: 10,),
DiscoverCell('支付', 'images/微信 支付.png', '', ''),
SizedBox(height: 10,),
DiscoverCell('收藏', 'images/微信收藏.png', '', ''),
//分割线
Row(
children: [
// 左边线条
Container(width: 50, height: 0.5, color: Colors.white,),
// 右边线条
Container(height: 0.5, color: Colors.grey,)
],
),
DiscoverCell('朋友圈', 'images/微信相册.png', '', ''),
//分割线
Row(
children: [
// 左边线条
Container(width: 50, height: 0.5, color: Colors.white,),
// 右边线条
Container(height: 0.5, color: Colors.grey,)
],
),
DiscoverCell('卡包', 'images/微信卡包.png', '', ''),
//分割线
Row(
children: [
// 左边线条
Container(width: 50, height: 0.5, color: Colors.white,),
// 右边线条
Container(height: 0.5, color: Colors.grey,)
],
),
DiscoverCell('表情', 'images/微信表情.png', '', ''),
SizedBox(height: 10,),
DiscoverCell('设置', 'images/微信设置.png', '', ''),
],
)
),
),
// 相机
Container(
margin: EdgeInsets.only(right: 10, top: 25),
height: 25,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Image(image: AssetImage('images/相机.png')),
],
),
),
],
),
),
);
}
}
总结:其实实现这些布局的思路有很多种,如果需要评定哪种布局方式更好的话,我们只需要遵循用到小部件最少,复杂度最低这个原则总归是没有错的。