带你Flutter带你Fly之构建布局

作者: 树獭非懒 | 来源:发表于2019-01-13 22:23 被阅读12次

    实现一个App的页面主要由两部分组成,一个是UI页面的构建,一个是与UI的交互。

    本次笔者通过实现一个页面来介绍如何实现Flutter布局的构建。这个页面长这个模样

    布局构建.png

    在正式内容看之前,请记住一句话

    Flutter一切皆组件(Widget)

    很可能你已经听过了,但不是很理解,相信看(-跟着做-)完这篇文章,你会对这句话深有体会的。

    解剖布局

    解剖布局.png

    整体来看的话,分为四个部分,也就是按行来分,图片、title、图标文字、大段文本。每个部分又细分几个小部分(具体后面详细介绍)。

    整体可以看成一个大列,一个大列分为4行(row),这个列(Column)和行(row)都是控件(Widget)。下面就一个个解剖每一行的内容。

    构建页面的框架

    重写 build 方法,为了方便(懒),我们接下来的代码都在这个方法里

    void main() => runApp(MyApp());
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        
        
        //...
        
        return new MaterialApp(
          title: 'Flutter Demo',
          theme: new ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: page,
        );
      }
    }
    

    可以看到 home 我使用了一个 page 组件,这个组件用于将刚刚划分的四行内容组合起来

    实现标题行

    第一行是图片比较简单(最后会提及到),我们先看第二行标题行

    标题行解剖.png

    标题行分为三列,第一列又分为两行,不同样式风格的text文本。第二列是个星星图标,第三列是text文本。

    定义一个标题行组件,实现上述的功能

       Widget titleSection = new Container(
              padding: const EdgeInsets.all(32.0),
              child: new Row(    //标题行的内容
                children: [
                  new Expanded(
                    child: new Column(  //摆放第一列的内容: 标题
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        new Container(
                          padding: const EdgeInsets.only(bottom: 8.0),
                          child: new Text(  //摆放第一列第一行的内容
                            'Pavlova',
                          style: new TextStyle(
                            fontWeight: FontWeight.bold,
                            fontSize: 18.0
                          ),
                        ),
                      ),
                          new Text(      //摆放第一列的第二行内容: 分类
                            'Food',
                            style: new TextStyle(
                              color: Colors.grey[500],
                              fontSize: 15.0
                            ),
                          )
                    ], 
                  ), 
                ),
                new Icon(       //摆放第二列的内容:星星图标
                    Icons.star,
                    color: Colors.red[500],
                  ),
    
                new Text(
                  '1k',
                  style: TextStyle(
                    fontSize: 13.0,
                  ),
                  ) //摆放第三列的内容: 收藏数
                ]
              ),
          );
    

    实现按钮行部分

    第三行是按钮行,是由三列图标(icon)加 文本(text) 构成

    按钮行解剖.png

    定义按钮行的组件,由于三列UI都是一样的,写三个相同的组件太冗余。我们只需要定义一个方法,把图标和文本作为参数,返回使用这些参数构成的这个组件就好了。

     Column buildBottomText(IconData icon,String text){
          Color color=Theme.of(context).primaryColor; //主题的主颜色
          return new Column(
            mainAxisSize: MainAxisSize.min,
            mainAxisAlignment: MainAxisAlignment.center, 
            children: [
              new Icon(icon,color: color),
              new Container(
                margin: const EdgeInsets.only(top: 8.0),
                child: new Text(
                  text,
                  style: TextStyle(
                      fontSize: 12.0,
                      fontWeight: FontWeight.w400,
                      color: color
                  ),  
                ),
              ),
            ],
          );
        }
    
    

    实现大文本部分

    最后一行是一大串文本,这个就比较简单了

    定义一个有文本样式的文本组件就ok了

    Widget bigText = new Container(
          padding: const EdgeInsets.all(32.0),
          child: Text(
            'Lake Oeschinen lies at the fo....', //文本内容省略
             softWrap: true,
             style: TextStyle(
               fontSize: 15.0
             ),
          ),
      );
    

    整合组件

    按之前说的,我们应该要用 Column (列)的组件将上面实现的几行组件整合起来,但是忽略了一个情况,当我们的文本内容过多,这一个页面是显示不完的,余下的部分就会被遮挡的。

    这个时候我们可以用 ListView 的组件,让这个页面可以上下滚动

    定义 page 组件

    //把这几个组件用listview整合起来
      Widget page=new Scaffold(
        appBar: new AppBar(
          title: new Text(
            'Image Introduction'
            ),
        ),
        body: new ListView(
          children: <Widget>[
           new Image.asset(
              'images/pavlova.jpg',
              width: 600.0,
              height: 240.0,
              fit: BoxFit.cover,
            ),
            titleSection,
            new Row(
               mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                 children: [
                   buildBottomText(Icons.star, 'start'),
                   buildBottomText(Icons.share, 'share'),
                   buildBottomText(Icons.favorite, 'like')
                 ],
                ),
                bigText,
          ],
        ),
      );
    

    可以看到 ListView 的第一个孩子内容就是图片了,看起来还是比较简单的,只需要把路径图片写上就好了。在项目的最外层新建一个 images 文件夹,把图片放入,但是运行App,会发现图片加载不出来,抛出找不到该图片的异常

    这是因为我们少了一个步骤,没有在 pubspec.yaml 这个文件里面把该图片加入进去,所以在 Image.asset 的asset 里面就找不到该图片

    把该文件里的 #assets 的 # 去掉,加上这个图片路径

    assets:
       - images/pavlova.jpg
    

    一些细节问题

    1.为什么要用容器 Container 组件?

    Container 主要有三个作用:

    • 添加内边距,外边距和边框
    • 改变背景颜色和背景图像
    • 可以容纳一个子控件,这个控件也可以是 Row 或 Column ,甚至控件树的根结点。

    比如下面代码,可以把 container 容器与四周添加 32.0 的间距

     padding: const EdgeInsets.all(32.0),
    

    2.mainAxisAlignment 和 crossAxisAlignment 属性有什么作用?

    这两个属性用于控制行或列的排列方式

    对齐控件.png

    比如在 Column 组件上将主轴对齐设置为 spaceEvenly,这将会在垂直方向上均匀划分剩余的空间

    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
    

    3.ListView 组件有什么作用?

    • 一个类似 Column 布局的控件
    • 可以在水平或垂直方向上进行布局
    • 内容超长时可以提供滚动
    • 比 Column 更少配置,但更易于使用并支持滚动

    4.在实现标题行组件的时候用到了 Expanded 组件,它是干什么的?

    假如我想实现下面这张图的效果,中间的图片宽度是另外两张的两倍,这个就可以用到 Expanded 了

    expand组件.png

    比如上面的图片可以这么实现

    child: new Row(
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          new Expanded(
            child: new Image.asset('images/pic1.jpg'),
          ),
          new Expanded(
            flex: 2,
            child: new Image.asset('images/pic2.jpg'),
          ),
          new Expanded(
           child: new Image.asset('images/pic3.jpg'),
         )
        ]
    ),
    

    Expanded 控件具有 flex 属性,它是一个整数,用于确定控件的弹性因子。Expanded 控件的默认弹性因子是1。

    总结

    实现完这样一个小清新的页面,相信你知道了如何去构建一些简单的 Flutter 布局。肯定对 Flutter一切皆组件 这句话有了更深刻的理解了吧,真的是哪里都是组件[汗]。

    相关文章

      网友评论

        本文标题:带你Flutter带你Fly之构建布局

        本文链接:https://www.haomeiwen.com/subject/itcrdqtx.html