带你Flutter带你Fly之构建布局

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

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

《带你Flutter带你Fly之构建布局》 布局构建.png

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

Flutter一切皆组件(Widget)

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

解剖布局

《带你Flutter带你Fly之构建布局》 解剖布局.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 组件,这个组件用于将刚刚划分的四行内容组合起来

实现标题行

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

《带你Flutter带你Fly之构建布局》 标题行解剖.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) 构成

《带你Flutter带你Fly之构建布局》 按钮行解剖.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 属性有什么作用?

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

《带你Flutter带你Fly之构建布局》 对齐控件.png

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

mainAxisAlignment: MainAxisAlignment.spaceEvenly,

3.ListView 组件有什么作用?

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

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

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

《带你Flutter带你Fly之构建布局》 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一切皆组件 这句话有了更深刻的理解了吧,真的是哪里都是组件[汗]。

    原文作者:树獭非懒
    原文地址: https://www.jianshu.com/p/e4f184736c6a
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞