上文中进行了基础页面的搭建,并且创建了底部导航栏,那么今天就开始搭建发现页面和我的页面。
先来到上次的rootpage中将_currentIndex改为2,这样默认选择的就是发现页面,方便进行页面搭建。
要搭建页面,心里就要大概思考要用什么部件来搭建这个页面。
那么看到这个界面,看到有AppBar,那么就想到要用scaffold,然后在body用container包着row来实现。
这里将AppBar的Title 颜色改成黑色,然后背景色改为灰色,并且将底部阴影的大小改为0,并且为安卓设置centerTitle为true,这样切出去的时候标题就会在中间, 然后创建一个container。
class _DiscoverPageState extends State {
Color _themColor = Color.fromRGBO(220, 220, 220, 1.0);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('发现页面',style: TextStyle(color: Colors.black),),
backgroundColor: _themColor,
elevation: 0,
centerTitle: true,
),
body: Container(
height: 800,
color: _themColor,
),
);
}
}
接下来创建discover cell。日常开发中,都是先使用StatelessWidget,当后面需要保留状态时才修改成StatefulWidget。所以这里先用StatelessWidget来创建DiscoverCell,然后创建四个需要传进来的参数,创建构造方法,其中title和imageName必须要有所以是required。这里页面的话思考使用Row,并且在Row里面也使用2个Row,一个左边一个右边 ,所以这里用到spacebetween。
class DiscoverCell extends StatelessWidget {
final String? title;
final String? imageName;
final String? subTitle;
final String? subImageName;
DiscoverCell({required this.title, required this.imageName, this.subTitle, this.subImageName}): assert(title != null,"title 不能为空"),assert(imageName != null,"imageName 不能为空");
@override
Widget build(BuildContext context) {
return Container(
height: 55,
color: Colors.white,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// left
Container(),
// right
Container(),
],
),
);
}
}
这里添加所需要的部件,并且给给container添加内边距padding。
Container(
padding: EdgeInsets.all(10),
child: Row(
children: [
//image
Image(
image: AssetImage(imageName!),
width: 20,
),
const SizedBox(
width: 15,
),
//title
Text(title!),
],
),
),
// right
Container(
padding: EdgeInsets.all(10),
child: Row(
children: [
// subtitle
Text(subTitle ?? ""),
// subimage
Image(
image: AssetImage(subImageName ?? ""),
width: 20,
),
//箭头
Image(
image: AssetImage('images/icon_right.png'),
width: 15,
),
],
)),
],
),
);
DiscoverCell创建完,就要构建discover page。在之前的scaffold的body的container中添加child:ListView,然后添加上需要的Cell,用SizedBox创建间距。这里使用Row里面包含两个Container的方法来实现分割线前部分白色,后部分灰色的效果。
child: ListView(
children: [
DiscoverCell(
imageName: 'images/朋友圈.png',
title: '朋友圈',
),
const SizedBox(
height: 10,
),
DiscoverCell(
imageName: 'images/扫一扫2.png',
title: '扫一扫',
),
Row(
children: [
Container(width: 50, height: 0.5, color: Colors.white),
Container(height: 0.5, color: Colors.grey)
],
),
DiscoverCell(
imageName: 'images/摇一摇.png',
title: '摇一摇',
),
SizedBox(
height: 10,
),
DiscoverCell(
imageName: 'images/看一看icon.png',
title: '看一看',
),
Row(
children: [
Container(width: 50, height: 0.5, color: Colors.white),
Container(height: 0.5, color: Colors.grey)
],
),
DiscoverCell(
imageName: 'images/搜一搜 2.png',
title: '搜一搜',
),
SizedBox(
height: 10,
),
DiscoverCell(
imageName: 'images/附近的人icon.png',
title: '附近的人',
),
SizedBox(
height: 10,
),
DiscoverCell(
imageName: 'images/购物.png',
title: '购物',
subTitle: '618限时特价',
subImageName: 'images/badge.png',
),
Row(
children: [
Container(width: 50, height: 0.5, color: Colors.white),
Container(height: 0.5, color: Colors.grey)
],
),
DiscoverCell(
imageName: 'images/游戏.png',
title: '游戏',
),
SizedBox(
height: 10,
),
DiscoverCell(
imageName: 'images/小程序.png',
title: '小程序',
),
],
),
),
接下来要点击cell之后跳转页面。要响应点击是在,在DiscoverCell外面套一层GestureDetector。GestureDetector是一个用于手势识别的功能性组件,我们通过它可以来识别各种手势。
创建一个跳转页面:
class DiscoverChildPage extends StatelessWidget {
final String title;
DiscoverChildPage({required this.title});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("$title"),
),
body: Center(
child: Text("$title"),
),
);
}
}
然后在点击的时候添加跳转方法。
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) =>
DiscoverChildPage(title: '$title')));
print('$title');
},
接下来给cell添加一个点击变灰色的效果,那么就说明,cell要变成有状态的,那么DiscoverCell就要改成StatefulWidget。整个cell有状态,并不代表渲染是整体渲染。Widget是界面的描述,不是界面,将整个cell改成有状态的,界面的描述会被重新创建,而不是界面。如果为了追求性能,可以把container抽取出来做一个StatefulWidget。这里为了方便直接修改整个DiscoverCell为StatefulWidget。
class DiscoverCell extends StatefulWidget {
final String? title;
final String? imageName;
final String? subTitle;
final String? subImageName;
DiscoverCell(
{required this.title,
required this.imageName,
this.subTitle,
this.subImageName})
: assert(title != null, "title 不能为空"),
assert(imageName != null, "imageName 不能为空");
@override
_DiscoverCellState createState() => _DiscoverCellState();
}
class _DiscoverCellState extends State {
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) =>
DiscoverChildPage(title: '$widget.title')));
print('$widget.title');
},
onTapCancel: () {},
onTapDown: (TapDownDetails details) {},
child: Container(
height: 55,
color: Colors.white,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// left
Container(
padding: EdgeInsets.all(10),
child: Row(
children: [
//image
Image(
image: AssetImage(widget.imageName!),
width: 20,
),
const SizedBox(
width: 15,
),
//title
Text(widget.title!),
],
),
),
// right
Container(
padding: EdgeInsets.all(10),
child: Row(
children: [
// subtitle
Text(widget.subTitle ?? ""),
// subimage
widget.subImageName != null
? Image(
image: AssetImage(widget.subImageName!),
width: 12,
)
: Container(),
//箭头
Image(
image: AssetImage('images/icon_right.png'),
width: 15,
),
],
)),
],
),
));
}
}
添加一个_currentColor,然后在onTapDown里面修改_currentColor为灰色,onTap里面修改为白色,_currentColor用来作为Container的颜色。
我的页面和发现页面实际上差不多,就是头部不一样了,没有了Appbar并且多了一个相机。
要创建我的页面,先将_currentIndex改为3方便创建。因为这里的相机是悬浮的,所以这里使用stack。这里简单的创建一个Positioned来放相机,然后cell用之前DiscoverCell,之后就可以专心做头部的界面了,这里创建headerWidget来专门做头部界面。这里Positioned放在后面因为相机需要在上层。
import 'package:flutter/material.dart';
import 'discover/discover_cell.dart';
class MinePage extends StatefulWidget {
const MinePage({Key? key}) : super(key: key);
@override
_MinePageState createState() => _MinePageState();
}
class _MinePageState extends State {
Widget headerWidget() {
return Container();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Stack(
children: [
Container(
child: ListView(
children: [
headerWidget(),
SizedBox(
height: 10,
),
DiscoverCell(
imageName: 'images/微信 支付.png',
title: '支付',
),
SizedBox(
height: 10,
),
DiscoverCell(
imageName: 'images/微信收藏.png',
title: '收藏',
),
Row(
children: [
Container(width: 50, height: 0.5, color: Colors.white),
Container(height: 0.5, color: Colors.grey)
],
), //分割线
DiscoverCell(
imageName: 'images/微信相册.png',
title: '相册',
),
Row(
children: [
Container(width: 50, height: 0.5, color: Colors.white),
Container(height: 0.5, color: Colors.grey)
],
), //分割线
DiscoverCell(
imageName: 'images/微信卡包.png',
title: '卡包',
),
Row(
children: [
Container(width: 50, height: 0.5, color: Colors.white),
Container(height: 0.5, color: Colors.grey),
],
),
DiscoverCell(
imageName: 'images/微信表情.png',
title: '表情',
),
SizedBox(
height: 10,
),
DiscoverCell(
imageName: 'images/微信设置.png',
title: '设置',
),
],
),
),
Positioned(
child: Image.asset(
"images/相机.png",
width: 25,
),
right: 10,
top: 40,
),
],
),
),
);
}
}
给headerWidget里面的Container高度以及颜色,就可以在屏幕上看到了
Widget headerWidget() {
return Container(
height: 300,
color: Colors.red,
);
}
这里看到Container没有在顶部,这是Flutter为刘海屏设置的一个Padding。
那么要去掉这个padding,就需要使用MediaQuery.removePadding,这里有removeTop,removeBottom,removeLeft,removeRight四个选项。
这样padding就没了。
回到headerWidget,在container里面添加一个Container,并且为其添加margin,这样这个Container就会在外面container的下部分。
return Container(
height: 200,
color: Colors.white,
child: Container(
color: Colors.red,
margin: EdgeInsets.only(top: 100,bottom: 20,left: 10,right: 10),
),
);
在里面的container里面child添加row,然后先添加头像。这里使用ClipRRect来给图片添加圆角。
child: Row(
children: [
//头像
Container(
child: ClipRRect(
child: Image(image: AssetImage('images/Hank.png')),
borderRadius: BorderRadius.circular(5.0),
),
),
//右边的部分
],
),
接下来右边的部分用Stack + Positioned就可以啦。
Container(
margin: EdgeInsets.only(left: 10),
width: MediaQuery.of(context).size.width - 20 - 90,
height: 100,
child: Stack(
children: [
Positioned(
child: Text("Hank",
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
),),
left: 0,
top: 0,
),
Positioned(
child: Text(
"微信号:1234",
style: TextStyle(
color: Color.fromRGBO(144, 144, 144, 1.0),
),
),
left: 0,
bottom: 0,
),
Positioned(
child: Image(
image: AssetImage('images/icon_right.png'),
width: 15,
),
right: 0,
bottom: 0,
),
],
),
),