Flutter学习之布局、交互、动画

作者:真丶深红骑士
链接:
https://juejin.im/post/5c617e34f265da2d90581613

本文由作者授权发布。

一、前言

前一天学习了 Flutter 基本控件和基本布局,我是觉得蛮有意思的。作为前端开发者,如何开发出好看,用户体验好的界面尤其重要。今天学习的方向主要有三:

  • 1. 加深布局的熟练度。

  • 2. 学习手势,页面跳转交互。

  • 3. 学习动画。

二、布局

因为我是从事 Android 开发,学习了 Flutter 之后,发现其布局和在 Android 下布局是不一样的, Android 布局是在 XML 文件下,直观性强一点,基本是整体到局部,首先是确定根布局是用 LinearLayout 还是 RelativeLayout 或者是 constraintLayout等。而在 Flutter 下,都是由 Widget 来拼接起来,很多时候都是 Row + Column 合成,我自己是在草稿上画出用什么 Widget 来拼出需求布局,然后才去实现。

1.布局一

直接上需求:

640?wx_fmt=png
需求图


很容易看出三块竖直排列,跟 Widget 用 Column 来实现,局部第一行是 Text ,第二行是 Row 行,但是 Row 并不是都是统一样式,多线程和Java深入是带圆角背景的,下面再仔细讲解,第三行是两个文本(作者文本和时间文本),一个图标,第一个文本很容易想到 Expanded ,当s时间文本和图标摆放后,其会占满剩余主轴空间。


640?wx_fmt=png
分析布局一
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    );

最终效果如下:

640?wx_fmt=png
布局一实现效果

2.布局二

直接上电影卡片布局,如下:

640?wx_fmt=png
布局二需求图


大致把图看了一遍,大致框架是最外层是用 Row ,左孩子是图片,右孩子是 Column ,其孩子分为五行,最后一行主演还是用 Row 来实现,上分析图:


640?wx_fmt=png
布局二分析图
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      );

运行效果图如下:

640?wx_fmt=png
布局二实现效果图

3.布局三

同样直接上需求:

640?wx_fmt=png
需求三布局


一看还是根布局直接用 Column ,一行一行实现就可以了,这个布局稍微简单一点,上分析图:


640?wx_fmt=png
需求三布局分析图
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    }

运行效果:

640?wx_fmt=png


布局三效果图



由于微信字数限制,本文未完,请链接以下地址继续查阅:

https://juejin.im/post/5c617e34f265da2d90581613

推荐阅读:


Android控件人生第一站,小红书任意拖拽标签控件


第一站小红书图片裁剪控件之二,自定义CoordinatorLayout联动效果


第一站仿小红书图片裁剪控件,深度解析大厂炫酷控件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值