作者:真丶深红骑士
链接:
https://juejin.im/post/5c617e34f265da2d90581613
本文由作者授权发布。
一、前言
前一天学习了 Flutter
基本控件和基本布局,我是觉得蛮有意思的。作为前端开发者,如何开发出好看,用户体验好的界面尤其重要。今天学习的方向主要有三:
1. 加深布局的熟练度。
2. 学习手势,页面跳转交互。
3. 学习动画。
二、布局
因为我是从事 Android
开发,学习了 Flutter
之后,发现其布局和在 Android
下布局是不一样的, Android
布局是在 XML
文件下,直观性强一点,基本是整体到局部,首先是确定根布局是用 LinearLayout
还是 RelativeLayout
或者是 constraintLayout
等。而在 Flutter
下,都是由 Widget
来拼接起来,很多时候都是 Row
+ Column
合成,我自己是在草稿上画出用什么 Widget
来拼出需求布局,然后才去实现。
1.布局一
直接上需求:
很容易看出三块竖直排列,跟 Widget
用 Column
来实现,局部第一行是 Text
,第二行是 Row
行,但是 Row
并不是都是统一样式,多线程和Java深入是带圆角背景的,下面再仔细讲解,第三行是两个文本(作者文本和时间文本),一个图标,第一个文本很容易想到 Expanded
,当s时间文本和图标摆放后,其会占满剩余主轴空间。
1.1.封装TextStyle和Padding
首先我看到整个布局下字体的颜色至少四种,有加粗和不加粗的,并且有部分加了 padding
,还是封装 TextStyle
和 padding
把:
1 /** 2 * TextStyle:封装 3 * colors:颜色 4 * fontsizes:字体大小 5 * isFontWeight:是否加粗 6 */ 7 TextStyle getTextStyle(Color colors,double fontsizes,bool isFontWeight){ 8 return TextStyle( 9 color:colors,10 fontSize: fontsizes,11 fontWeight: isFontWeight == true ? FontWeight.bold : FontWeight.normal ,12 );13 }14 /**15 * 组件加上下左右padding16 * w:所要加padding的组件17 * all:加多少padding18 */19 Widget getPadding(Widget w,double all){20 return Padding(21 child:w,22 padding:EdgeInsets.all(all),23 );24 }2526 /**27 * 组件选择性加padding28 * 这里用了位置可选命名参数{param1,param2,...}来命名参数,也调用的时候可以不传29 *30 */31 Widget getPaddingfromLTRB(Widget w,{double l,double t,double,r,double b}){32 return Padding(33 child:w,34 padding:EdgeInsets.fromLTRB(l ?? 0,t ?? 0,r ?? 0,b ?? 0),35 );36 }
1.2.实现第一行
因为上面分析,整体是用 Column
来实现,下面实现第一行 Java synchronized原理总结
1 Widget ColumnWidget = Column( 2 //主轴上设置居中 3 mainAxisAlignment: MainAxisAlignment.center, 4 //交叉轴(水平方向)设置从左开始 5 crossAxisAlignment: CrossAxisAlignment.start, 6 children: <Widget>[ 7 //第一行 8 getPaddingfromLTRB(Text('Java synchronized原理总结', 9 style: getTextStyle(Colors.black, 16,true),10 ),t:0.0),11 ],12 );
1.3.实现第二行
1.3.1实现渐变圆角Text
第二行可以看到 多线程
和 Java深入
是带渐变效果的圆角,一看到这,我是没有头绪的,查了网上的资料发现 Container
是有设置 圆角
和 渐变
属性的:
1 //抽取第二行渐变text效果 2 Container getText(String text,LinearGradient linearGradient){ 3 return Container( 4 //距离左边距离10dp 5 margin: const EdgeInsets.only(left: 10), 6 //约束 相当于直接制定了该Container的宽和高,且它的优先级要高于width和height 7 constraints: new BoxConstraints.expand( 8 width: 70.0, height: 30.0,), 9 //文字居中10 alignment: Alignment.center,11 child: new Text(12 text,13 style:getTextStyle(Colors.white,14,false),14 ),15 decoration: new BoxDecoration(16 color: Colors.blue,17 //圆角18 borderRadius: new BorderRadius.all(new Radius.circular(6.0)),19 //添加渐变20 gradient:linearGradient,21 ),22 );2324 }
1.3.2.整合第二行
1//第二行 2 Widget rowWidget = Row( 3 //主轴左边对齐 4 mainAxisAlignment: MainAxisAlignment.start, 5 //交叉轴(竖直方向)居中 6 crossAxisAlignment: CrossAxisAlignment.center, 7 children: <Widget>[ 8 Text("分类:", 9 style: getTextStyle(Colors.blue,14,true),1011 ),12 getText("多线程", l1),13 getText("Java深入", l2),14 ],1516 );1718 //根Widget19 Widget ColumnWidget = Column(20 //主轴上设置居中21 mainAxisAlignment: MainAxisAlignment.center,22 //交叉轴(水平方向)设置从左开始23 crossAxisAlignment: CrossAxisAlignment.start,24 children: <Widget>[25 //第一行26 getPaddingfromLTRB(Text('Java synchronized原理总结',27 style: getTextStyle(Colors.black, 16,true),28 ),t:0.0),29 //第二行30 getPaddingfromLTRB(rowWidget,t:10.0),31 ],32 );
1.4.实现第三行
第三行就简单了,直接一个 Row
Widget,内部嵌套 Expanded
、 Text
、 Icon
就Ok了,代码如下:
1 //第三行 2 Widget rowthreeWidget = Row( 3 mainAxisAlignment: MainAxisAlignment.start, 4 crossAxisAlignment: CrossAxisAlignment.center, 5 children: <Widget>[ 6 new Expanded( 7 child: Text( 8 "作者:EnjoyMoving", 9 style: getTextStyle(Colors.grey[400], 14, true),10 ),11 ),12 getPaddingfromLTRB(Text(13 '时间:2019-02-02',14 style: getTextStyle(Colors.black, 14, true),15 ), r :10.0),16 getPaddingfromLTRB(Icon(17 Icons.favorite_border,18 color:Colors.grey[400],19 ),r:0.0)20 ],21 );
1.5.整体
1 //根Widget 2 Widget ColumnWidget = Column( 3 //主轴上设置居中 4 mainAxisAlignment: MainAxisAlignment.center, 5 //交叉轴(水平方向)设置从左开始 6 crossAxisAlignment: CrossAxisAlignment.start, 7 children: <Widget>[ 8 //第一行 9 getPaddingfromLTRB(Text('Java synchronized原理总结',10 style: getTextStyle(Colors.black, 16,true),11 ),t:0.0),12 //第二行13 getPaddingfromLTRB(rowWidget,t:10.0),14 //第三行15 getPaddingfromLTRB(rowthreeWidget,t:10.0),1617 ],18 );19 return new Scaffold(20 appBar: new AppBar(21 title: new Text('Flutter Demo'),22 ),23 //用card裹住24 body: Card(25 child: Container(26 //高度27 height: 160.0,28 //颜色29 color: Colors.white,30 padding: EdgeInsets.all(10.0),31 child: Center(32 child: ColumnWidget,33 )34 ),35 ),36 );
最终效果如下:
2.布局二
直接上电影卡片布局,如下:
大致把图看了一遍,大致框架是最外层是用 Row
,左孩子是图片,右孩子是 Column
,其孩子分为五行,最后一行主演还是用 Row
来实现,上分析图:
2.1.实现右边图片
1//根Widget 布局二 开始 2 //右边图片布局 3 Widget LayoutTwoLeft = Container( 4 //这次使用裁剪实现圆角矩形 5 child:ClipRRect( 6 //设置圆角 7 borderRadius: BorderRadius.circular(4.0), 8 child: Image.network( 9 'https://img3.doubanio.com//view//photo//s_ratio_poster//public//p2545472803.webp',10 width: 100.0,11 height: 150.0,12 fit:BoxFit.fill,13 ),1415 ),16 );17 //整体18 Widget RowWidget = Row(19 //主轴上设置居中20 mainAxisAlignment: MainAxisAlignment.start,21 //交叉轴(水平方向)设置从左开始22 crossAxisAlignment: CrossAxisAlignment.center,23 children: <Widget>[24 LayoutTwoLeft,25 ],26 );27
2.2.实现圆形头像
就是用自带的 CircleAvatar
这个 Widget
来实现:
1 //右下角圆形2 CircleAvatar getCircleAvator(String image_url){3 //圆形头像4 return CircleAvatar(5 backgroundColor: Colors.white,6 backgroundImage: NetworkImage(image_url),7 );8 }
2.3.实现右边布局
右布局就是用一个 Column
来实现,一列一列往下实现即可:
1 //右布局 2 Widget LayoutTwoRightColumn = Column( 3 crossAxisAlignment: CrossAxisAlignment.start, 4 children: <Widget>[ 5 //电影名称 6 Text( 7 '流浪地球', 8 style: getTextStyle(Colors.black, 20.0, true), 9 ),1011 //豆瓣评分12 Text(13 '豆瓣评分:7.9',14 style: getTextStyle(Colors.black54, 16.0, false),15 ),1617 //类型18 Text(19 '类型:科幻、太空、灾难',20 style:getTextStyle(Colors.black54, 16.0, false),21 ),2223 //导演24 Text(25 '导演:郭帆',26 style: getTextStyle(Colors.black54, 16.0, false),27 ),2829 //主演30 Container(31 margin: EdgeInsets.only(top:8.0),32 child:Row(33 children: <Widget>[34 Text('主演:'),35 //以Row从左到右排列头像36 Row(37 children: <Widget>[38 Container(39 margin: EdgeInsets.only(left:2.0),40 child: getCircleAvator('https://img3.doubanio.com//view//celebrity//s_ratio_celebrity//public//p1533348792.03.webp'),41 ),42 Container(43 margin: EdgeInsets.only(left:12.0),44 child: getCircleAvator('https://img3.doubanio.com//view//celebrity//s_ratio_celebrity//public//p1501738155.24.webp'),45 ),46 Container(47 margin: EdgeInsets.only(left:12.0),48 child: getCircleAvator('https://img3.doubanio.com//view//celebrity//s_ratio_celebrity//public//p1540619056.43.webp'),49 ),5051 ],52 ),53 ],54 ),55 ),56 ],57 );5859 //布局二 右布局 用Expanded占满剩余空间60 Widget LayoutTwoRightExpanded = Expanded(61 child:Container(62 //距离左布局1063 margin:EdgeInsets.only(left:10.0),64 //高度65 height:150.0,66 child: LayoutTwoRightColumn,67 ),68 );
右布局用 Expanded
就是为了占满剩余空间。
2.4.整合
1 //整体 2 Widget RowWidget = Row( 3 //主轴上设置从开始方向对齐 4 mainAxisAlignment: MainAxisAlignment.start, 5 //交叉轴(水平方向)居中 6 crossAxisAlignment: CrossAxisAlignment.center, 7 children: <Widget>[ 8 LayoutTwoLeft, 9 LayoutTwoRightExpanded,10 ],11 );12 return new Scaffold(13 appBar: new AppBar(14 title: new Text('Flutter Demo'),15 ),16 body: Card(17 child: Container(18 //alignment: Alignment(0.0, 0.0),19 height: 160.0,20 color: Colors.white,21 padding: EdgeInsets.all(10.0),22 child: Center(23 // 布局一24 // child: ColumnWidget,2526 // 布局二27 child:RowWidget,28 )29 ),30 ),31 );
运行效果图如下:
3.布局三
同样直接上需求:
一看还是根布局直接用 Column
,一行一行实现就可以了,这个布局稍微简单一点,上分析图:
3.1.实现第一行
1 //布局三开始第一行 2 Widget LayoutThreeOne = Row( 3 children: <Widget>[ 4 Expanded( 5 child: Row( 6 children: <Widget>[ 7 Text('作者:'), 8 Text('HuYounger', 9 style: getTextStyle(Colors.redAccent[400], 14, false),10 ),11 ],12 )13 ),14 //收藏图标15 getPaddingfromLTRB(Icon(Icons.favorite,color:Colors.red),r:10.0),16 //分享图标17 Icon(Icons.share,color:Colors.black),18 ],19 );
3.2.实现第三行
1 //布局三开始第三行 2 Widget LayoutThreeThree = Row( 3 children: <Widget>[ 4 Expanded( 5 child: Row( 6 children: <Widget>[ 7 Text('分类:'), 8 getPaddingfromLTRB(Text('开发环境/Android', 9 style:getTextStyle(Colors.deepPurpleAccent, 14, false)),l:8.0),10 ],11 ),12 ),13 Text('发布时间:2018-12-13'),14 ],15 );
3.3.整合
1 //布局三整合 2 Widget LayoutThreeColumn = Column( 3 //主轴上设置居中 4 mainAxisAlignment: MainAxisAlignment.center, 5 //交叉轴(水平方向)设置从左开始 6 crossAxisAlignment: CrossAxisAlignment.start, 7 children: <Widget>[ 8 //第一行 9 LayoutThreeOne,10 //第二行11 getPaddingfromLTRB(Text('Android Monitor使用介绍',12 style:getTextStyle(Colors.black, 18, false),13 ),t:10.0),14 //第三行15 getPaddingfromLTRB(LayoutThreeThree,t:10.0),16 ],1718 );19 return new Scaffold(20 appBar: new AppBar(21 title: new Text('Flutter Demo'),22 ),23 body: Card(24 child: Container(25 //alignment: Alignment(0.0, 0.0),26 height: 160.0,27 color: Colors.white,28 padding: EdgeInsets.all(10.0),29 child: Center(30 // 布局一31 // child: ColumnWidget,3233 // 布局二34 // child:RowWidget,3536 // 布局三37 child:LayoutThreeColumn,38 )39 ),40 ),41 );42 }
运行效果:
由于微信字数限制,本文未完,请链接以下地址继续查阅:
https://juejin.im/post/5c617e34f265da2d90581613
推荐阅读:
第一站小红书图片裁剪控件之二,自定义CoordinatorLayout联动效果