前言
这篇文章首要是编写仿微信的发现页、我的页面,因为这两个功用模块比较少,就放到一起了
这儿首要介绍界面搭建,小组件封装等作用
源码地址
小组件cell
介绍发现页和我的页面之前,先介绍一下他们都再用的图文混排的 cell
,就像下面这样,咱们顺道把 朋友圈后面的东西也做了
//因为咱们封装的小组件,咱们要设置一些特点,在外面调用时,能够经过结构方法传递进来
//如下所示,因为状况会更新,挑选的StatefulWidget
class ItemCell extends StatefulWidget {
final String imageUrl;
final String text;
final String? subImageUrl;
final String? subText;
final bool hasLine;
//require表明的必须传入,直接赋值的能够不传,为默许值
const ItemCell({Key? key, required this.imageUrl, required this.text,
this.subImageUrl, this.subText, this.hasLine = false}) : super(key: key);
@override
State<ItemCell> createState() => _ItemCellState();
}
class _ItemCellState extends State<ItemCell> {
}
下面则是 build
中的代码完成,将关键内容标出来
//GestureDetector为点击手势,咱们能够经过点击按下作用和cancel等作用,配合container的color
//完成相似的按下变色作用,当需求特别的自定义时,能够这样做
//直接换成TextButton也能够,留意button有个外边
return GestureDetector(
//点击生效后,康复色彩
onTap: () {
setState(() {
backColor = Colors.white;
});
},
//按下时,布景变色,仿ios作用
onTapDown: (TapDownDetails details) {
setState(() {
backColor = Colors.grey;
});
},
//撤销点击时康复白色作用
onTapCancel: () {
setState(() {
backColor = Colors.white;
});
},
//设置children Column,笔直布局,则是将内部分为上下两块
//上面是内容,下面是下划线
child: Column(
children: [
//设置一个container,能够设置布景色高度等
Container(
color: backColor,
height: 46,
//设置内边距padding,外边距为margin
padding: const EdgeInsets.only(left: 16, right: 10),
//第一排为横向布局,将左边的图片文字,和右边的案牍箭头分为两部分
child: Row(
//左右两头对齐,中心内容等距离
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
//经过水平布局row,来设置左边图片和文字
Row(
children: [
Image.asset(
widget.imageUrl,
width: 16,
height: 16,
fit: BoxFit.fitWidth,
),
Container(
width: 14,
),
Text(
widget.text,
style: const TextStyle(
fontSize: 12,
color: Colors.black
)
),
],
),
//设置右侧案牍,左边提示,右侧箭头
Row(
children: [
//设置提示案牍
Row(
children: [
//也能够运用三目运算符
//剪裁图片,假如设置border,请运用 container的
//右上角红圈的话,Stack嵌套Position即可
widget.subImageUrl != null ?
SizedBox(
width: 24,
height: 24,
//留意图片右上角有个红点,除了设置圆角,还得运用stack布局设置红点方位
child: Stack(
alignment: Alignment.center,
children: [
//头像,这儿选用其间一种方法,剪裁,别的一种在下面
ClipRRect(
borderRadius: BorderRadius.circular(4),
child: Image.asset(
widget.subImageUrl!,
width: 20,
height: 20,
fit: BoxFit.fitWidth,
)
),
// 红点,能够一张图片处理,默许运用圆角布景
Positioned(
right: 0,
top: 0,
child: Container(
width: 6,
height: 6,
//设置了 decoration 不能设置Container的color特点
decoration: BoxDecoration(
//也能够设置image特点,给图片加圆角
color: Colors.red,
border: Border.all(color: Colors.red, width: 1),
borderRadius:
const BorderRadius.all(Radius.circular(3))
),
)
)
],
)
) : Container(),
//设置距离
const SizedBox(width: 2),
//设置活动子标题,假如有的话
widget.subText != null ?
Text(
widget.subText!,
style: const TextStyle(
fontSize: 12,
color: Colors.black
)
) : Container(),
],
),
//距离
const SizedBox(width: 2),
//右侧箭头
Image.asset(
"images/icon_right.png",
width: 12,
height: 12,
fit: BoxFit.fitWidth,
)
],
)
]
),
),
//经过三目运算符来动态控制下划线的显示,Container完全能够当占位符
widget.hasLine ?
Row(
children: [
//两个Container都能够
Container(width: 46, height: 1, color: Colors.white),
Expanded(child: Container(height: 1, color:
const Color.fromRGBO(0xe1, 0xe1, 0xe1, 1))),],
) :
Container(height: 1, color: Colors.white)
],
)
);
就这样一个小组件完成了
假如小组件点击后有回调,那么能够定义一个Function
特点,和 name 等相同,结构方法传入即可,例如:
//不带参回调
final Function() onClick;
//带参回调
final Function(int index) onClick;
发现页
发现页没有多少特别的当地,直接ListView
调配多个cell
即可,ListView
默许笔直笔直方向布局,可设置水平
//跳转新页面必备
Scaffold(
//顶部导航
appBar: AppBar(
title: const Text("发现"),
foregroundColor: Colors.black,
backgroundColor: const Color.fromRGBO(0xe1, 0xe1, 0xe1, 1),
elevation: 0 //去掉阴影
),
body: Container(
//设置ListView的布景,ListView组件默许不能设置色彩,只能用这个了
color: const Color.fromRGBO(0xe1, 0xe1, 0xe1, 1),
child: ListView(
children: const <Widget>[
ItemCell(imageUrl: "images/朋友圈.png", text: "朋友圈",
subImageUrl: 'images/head.jpg', subText: "2个朋友赞过"),
//也能够运用Container,假如不设置色彩或者边距,那么直接SizeBox更适宜
SizedBox(height: 6),
ItemCell(imageUrl: "images/扫一扫2.png", text: "扫一扫", hasLine: true),
ItemCell(imageUrl: "images/摇一摇.png", text: "摇一摇"),
SizedBox(height: 6),
ItemCell(imageUrl: "images/看一看.png", text: "看一看", hasLine: true),
ItemCell(imageUrl: "images/搜一搜.png", text: "搜一搜"),
SizedBox(height: 6),
ItemCell(imageUrl: "images/附近.png", text: "附近"),
SizedBox(height: 6),
ItemCell(imageUrl: "images/购物.png", text: "购物", hasLine: true),
ItemCell(imageUrl: "images/游戏.png", text: "游戏"),
SizedBox(height: 6),
ItemCell(imageUrl: "images/小程序.png", text: "小程序"),
]
),
)
);
作用如下所示
我的
我的页面如下所示,咱们从顶部开端
//先放置一个ListView,确保能滚动,避免一些小手机无法看到底部 item
ListView(
children: [
//为了确保滑动底部运用默许的白色,而里边默许布景灰色,给内容包括一个Container设置为灰色
//这样空出来的距离色彩都是灰色
//当然完成手法有多中
Container(
color: const Color.fromRGBO(0xe1, 0xe1, 0xe1, 1),
//因为方法的内容为上下布局,选用Column
child: Column(
children: [
//顶部个人信息
Container(
//默许布景白色
color: Colors.white,
height: 140,
//将顶部分为左边内容和右侧箭头
//这样左边内容也能够随意调整,后面也能够依据内容约束长度
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
//将左边图片和右侧文字分为两个挨着的部分
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
//左边图片
Container(
margin: const EdgeInsets.only(left: 12),
width: 60,
height: 60,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage("images/head.jpg")
),
borderRadius: BorderRadius.all(Radius.circular(6))
)
),
//距离
const SizedBox(width: 12),
//右侧文字上下布局
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text("剪刀石头布",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold
),
),
SizedBox(height: 4),
Text("微信号: rock666")
]
)
]
),
//右侧箭头
Container(
margin: const EdgeInsets.only(right: ),
child: Image.asset(
"images/icon_right.png",
width: 12,
height: 12,
fit: BoxFit.fitWidth,
)
)
],
),
),
//下面的功用item
const SizedBox(height: 6),
const ItemCell(imageUrl: "images/微信卡包.png", text: "卡包"),
//也能够运用Container
const SizedBox(height: 6),
const ItemCell(imageUrl: "images/微信保藏.png", text: "shoucang ", hasLine: true),
const ItemCell(imageUrl: "images/微信相册.png", text: "摇一摇", hasLine: true),
const ItemCell(imageUrl: "images/微信表情.png", text: "看一看"),
const SizedBox(height: 6),
const ItemCell(imageUrl: "images/微信设置.png", text: "附近"),
//底部灰色部分,看起来好看一些,能够依据全屏高度计算剩余
//例如:MediaQuery.of(context).size.height-占用空间 来计算
//const SizedBox(height: 150),
],
),
),
],
)
保存状况 AutomaticKeepAliveClientMixin
保存状况时,需求进行三步,承继
、重写
、调用父类build
方法,如下所示
留意:组件状况保存只是适用于 StatefulWidget
类型组件
//1、承继 AutomaticKeepAliveClientMixin
class _DiscoverState extends State<Discover> with AutomaticKeepAliveClientMixin {
//2、重写 wantKeepAlive 为 true
//重写该方法,保持住状况,且只有StatefulWidget才能够
@override
bool get wantKeepAlive => true;
@override
Widget build(BuildContext context) {
//3、调用父类的 build方 法
super.build(context);
return Scaffold(
appBar: AppBar(
title: const Text("发现"),
foregroundColor: Colors.black,
backgroundColor: const Color.fromRGBO(0xe1, 0xe1, 0xe1, 1),
elevation: 0 //去掉阴影
),
body: Container(
color: const Color.fromRGBO(0xe1, 0xe1, 0xe1, 1),
child: const Text("发现页"),
),
);
}
最后
功用看起来很简略,介绍的也比价简略,这儿没有多介绍原理,没有一步步告知思路,究竟代码也不多,个人直接贴出啦的方法比较好,首要过程注释出来,方便了解