追梦App是我们实验室一个大佬发起的,详情见https://blog.csdn.net/qq_46101869/category_10342068.html
然后我按着设计图用flutter去实现前端,设计图是这样的↓,这是追梦App的引导部分
一开始我看到显示翻页的小圆点想用的是swiper,可是这里又是点击下一步的按钮才翻页,知识盲区,百度了很久只找到修改pagination的。于是后来打算老老实实做三个页面,小圆点是画的。
这个背景很好康,但是我在实现的时候有些复杂,用了两次贝塞尔曲线,用不同透明度的颜色叠加。
class background extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ClipPath(
clipper: MyClipper(),
child: Container(
height: 400,
color: Theme.of(context).primaryColor.withOpacity(0.05),
child: ClipPath(
clipper: MyClipper2(),
child: Container(
height:400,
width: double.infinity,
color: Theme.of(context).primaryColor.withOpacity(0.06),
),
)
),
)
],
);
}
}
class MyClipper extends CustomClipper {
@override
Path getClip(Size size) {
var path = new Path();
path.lineTo(0, size.height - 100);
var controllPoint = Offset(50, size.height);
var endPoint = Offset(size.width/2+50, size.height);
path.quadraticBezierTo(controllPoint.dx, controllPoint.dy, endPoint.dx, endPoint.dy);
controllPoint=Offset(size.width-80,size.height);
endPoint=Offset(size.width,size.height-50);
path.quadraticBezierTo(controllPoint.dx, controllPoint.dy, endPoint.dx, endPoint.dy);
path.lineTo(size.width, size.height);
path.lineTo(size.width, 0);
return path;
}
@override
bool shouldReclip(CustomClipper oldClipper) {
return true;
}
}
class MyClipper2 extends CustomClipper {
@override
Path getClip(Size size) {
var path = new Path();
path.lineTo(0, size.height - 110);
var controllPoint = Offset(50, size.height-20);
var endPoint = Offset(size.width/2, size.height-20);
path.quadraticBezierTo(controllPoint.dx, controllPoint.dy, endPoint.dx, endPoint.dy);
controllPoint=Offset(size.width-100,size.height-20);
endPoint=Offset(size.width,size.height-80);
path.quadraticBezierTo(controllPoint.dx, controllPoint.dy, endPoint.dx, endPoint.dy);
path.lineTo(size.width, size.height);
path.lineTo(size.width, 0);
return path;
}
@override
bool shouldReclip(CustomClipper oldClipper) {
return true;
}
}
按顺序实现,p1中白底的的文本框要求能够输入文字,所以用了textField
class TextFieldDemo extends StatefulWidget {
@override
_TextFieldDemoState createState() => _TextFieldDemoState();
}
class _TextFieldDemoState extends State {
final textEditingController = TextEditingController();
@override
void dispose() {
textEditingController.dispose();
super.dispose();
}
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return TextField(
textAlign: TextAlign.center,
controller: textEditingController,
onSubmitted: (value){
debugPrint('submit: $value');
},
decoration: InputDecoration(
hintText: '在此输入你的目标描述',
border: InputBorder.none,//input没有下划线了
fillColor: Colors.white,
filled: true,
),
style: TextStyle(
color: Theme.of(context).primaryColor,
fontSize: 15
),
);
}
}
接下来就是一段导语 和 模拟的分页指示器
//导语
Container(
width:270,
child: Text('你的目标将会是你之后道路上一直要追寻的梦想,我们将会在你追寻的道路上帮助你',
style: TextStyle(
color:Colors.black54,
fontSize: 16,
decoration: TextDecoration.none
),
textAlign: TextAlign.center,
),
),
SizedBox(height: 35),
//分页指示器
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 11,
width: 11,
child: CircleAvatar(),
),
SizedBox(width: 10,),
Container(
height: 10,
width: 10,
child: CircleAvatar(backgroundColor: Theme.of(context).primaryColor.withOpacity(0.1)),
),
SizedBox(width: 10,),
Container(
height: 10,
width: 10,
child: CircleAvatar(backgroundColor:Theme.of(context).primaryColor.withOpacity(0.1)),
),
],
),
然后是两个按钮,个人认为返回键没有存在的必要,点击下一步可以进入p2
//两个按钮
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
height: 50,
width: 100,
child:FlatButton(
child: Text('返回',style:TextStyle(fontSize: 16,color: Colors.black54)),
onPressed:(){},
color: Theme.of(context).primaryColor.withOpacity(0.06),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
),
),
SizedBox(width: 50,),
SizedBox(
height: 50,
width: 100,
child:FlatButton(
child: Text('下一步',style:TextStyle(fontSize: 16,color: Colors.white)),
onPressed:(){
Navigator.of(context).push(
MaterialPageRoute(builder: (context)=>Target2()),
);
},
color: Theme.of(context).primaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
),
)
],
)
所以我做好了第一个页面↓字体什么的都还没设置
然后进入p2,在p1的基础上,把p1的文本框改成了一个IconButton,
IconButton(
icon: Icon(Icons.add_circle),
iconSize: 100,
color: Theme.of(context).primaryColor,
splashColor: Colors.transparent,
focusColor: Theme.of(context).primaryColor.withOpacity(0.9),
onPressed: (){
showModalBottomSheet(
backgroundColor: Theme.of(context).primaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topRight: Radius.circular(40),
topLeft: Radius.circular(40)
)
),
context: context,
builder: (BuildContext context){
return BottomPage();
}
);
},
),
点击iconButton,弹出如p3所示的选项页面,我就做到这里啦,还没有细化完成。
class BottomPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
height: double.infinity/2,
padding: EdgeInsets.all(32.0),
);
}
}
所以p2 p3的展示: