Flutter Widget 静态布局实战

作者: d74f37143a31 | 来源:发表于2019-01-26 21:51 被阅读10次

    前面两篇文章介绍了 Flutter win环境的安装,以及利用listview 实现了简单布局。(这篇文章篇幅有点长,读完大概需要8.88分钟)
    Flutter 入门实现 ListView 列表页面以及收藏页面
    Flutter 环境搭建以及填坑指南(Win10 系统且已有 Android 开发环境

    这篇文章主要介绍以下内容:
    1. ListView 能实现什么效果?
    2. widget 如何添加到 ListView 中?
    3. ListView 点击事件,单个 widget 点击事件
    4. ( 重点 ) widget 如何垂直、水平摆放?
    5. 图片、Icon、Text widget 的简单使用

    在这篇文章Flutter 入门实现 ListView 列表页面以及收藏页面中虽然实现了一个列表,但是怎么实现的还没仔细研究,现在就先从研究 ListView的实现开始吧。

    先看一下之前实现的效果:

    ListView 列表
    实现的代码主要是RandomWordsState类中的下面代码:
    return new ListTile(
          // 单词布局
          title: new Text(
            pair.asPascalCase,
            style: _biggerFont,
          ),
          // 喜欢小心心布局
          trailing: new Icon(
            alreadySaved ? Icons.favorite : Icons.favorite_border,
            color: alreadySaved ? Colors.red : null,
          ),
          // ListView item 的点击事件
          onTap: () {
            // 通知框架状态已经改变
            setState(() {
              if (alreadySaved) {
                _saved.remove(pair);
              } else {
                _saved.add(pair);
              }
            });
          },
        );
    

    从上面的代码中可以看出实现这个列表主要就是操作了 ListTile 这个 Widget,那么我们看看这个ListTile的构造方法都提供了上面功能吧!注释都写在代码中了~

    /// Requires one of its ancestors to be a [Material] widget.
      const ListTile({
        Key key,
        // 在列表的左边添加的 widget(如文末图中的左边图片)
        this.leading,
        // 标题
        this.title,
        // 副标题
        this.subtitle,
        // 在列表的右边添加的 widget(如文末图中的右边心形 icon)
        this.trailing,
        // 如果 isThreeLine 为 true, subtitle 则不能为 null,默认为 false
        // 如果 副标题为空则列表平铺一行显示,如果 副标题 不为空,则副标题所占的布局是两行
        // 如果 isThreeLine = true 则副标题可以显示三行。
        this.isThreeLine = false,
        // bool 类型,默认为false ,如果为 true 则 ListTile 在垂直方向是密集型摆放
        //(具体效果待后面去实现,这里只是看了注释后的理解)
        this.dense,
        // 内容的边距
        this.contentPadding,
        // item 是否能点击
        this.enabled = true,
        // item 的点击事件
        this.onTap,
        // item 的长按事件
        this.onLongPress,
        // item 是否选中标记
        this.selected = false,
      }) 
    

    flutter 的开发语言是dart,这个语言之前没学过,这里就先不深入学习了,网上有比较好的这个博主 恋猫月亮
    写的系列的文章写的很好。我这里就只写实现上面图片中展示的效果。

    1. ListView 能实现什么效果?

    ListView 不仅可以实现列表布局,还可以实现 Android 中的 ScrollView 的功能。关于 滚动的 widget 还有其他的实现,可参考可滚动Widget简介

    实现列表功能,如果是符合ListTile的样式,直接使用ListTile实现很方便。(目前还没学到如何实现多布局,后面学习再写一篇)

    实现ScrollView功能,直接在build方法的body中添加我们的 widget。伪代码如下:

    定义了一个DetailScreen详情页面,继承了StatelessWidget,在build方法中返回了页面的内容:appBar 是标题栏,body 是显示的内容

    class DetailScreen extends StatelessWidget {
     // Declare a field that holds the pair
     final WordPair pair;
    
     // In the constructor, require a pair
     DetailScreen({Key key, @required this.pair}) : super(key: key);
    
     @override
     Widget build(BuildContext context) {
     return new Scaffold(
          appBar: new AppBar(
            // 接收传递过来的单词做标题名字
            title: new Text("${pair.asPascalCase}"),
          ),
          // 使用 ListView 做滚动列表
          body: new ListView(  
            children: [
              // 显示网络图片
              new Image.asset(
                'images/wali.jpg',
                width: 600.0,
                height: 240.0,
                fit: BoxFit.cover,
              ),
              // 标题行(文末有实现效果)
              titleSection,
              // 按钮行(文末有实现效果)
              buttonSection,
              // 描述文本(文末有实现效果)
              textSection,
            ],
          ),
        );
      }
    }
    

    2. widget 如何添加到 ListView 中?

    上面的伪代码中在构建一个有状态的widget的时候会重写 build方法,在该方法中的 body返回我们实现的 widget即可。

    3. ListView 点击事件,单个 widget 点击事件

    如果ListView使用ListTile实现列表的话,直接使用ListTile中的 onTap实现列表点击效果。如果不是用ListTile实现,那就使用单个widget的点击事件吧。

    widget的如果有onTap方法可以直接调用该方法即可实现,如果没有该方法,则需要使用GestureDetector来实现点击效果,例如上图中的心形喜欢点击事件,伪代码实现如下:

    关于GestureDetector可参考该文章 手势识别GestureDetector

    trailing: new GestureDetector(
            // 心形喜欢 icon
            child: new Icon(
              alreadySaved ? Icons.favorite : Icons.favorite_border,
              color: alreadySaved ? Colors.red : null,
            ),
            // 点击事件
            onTap: (){
              // 通知框架状态已经改变
              setState(() {
                if (alreadySaved) {
                  _saved.remove(pair);
                } else {
                  _saved.add(pair);
                }
              });
            },
          ),
    

    4. widget 如何垂直、水平摆放?

    最常见的布局模式之一是垂直或水平排列widget。在Flutter可以使用行Row水平排列widget,并使用列Column垂直排列widget。 同时,每个孩子本身可以是一个Row或一个Column,依此类推。以下示例显示如何在行或列内嵌套行或列。

    通过下面两张图片可以学到在实现布局时如何拆分:
    参考链接 widget 布局

    左侧的一列和右侧的图片
    左侧的Column widget树嵌套行和列

    在控制行或列对齐其子项时使用mainAxisAlignment( 主轴 ) 和crossAxisAlignment( 横轴 ) 属性来。 对于行(Row)来说,主轴是水平方向,横轴垂直方向。对于列(Column)来说,主轴垂直方向,横轴水平方向。

    Row
    Column
    MainAxisAlignmentCrossAxisAlignment 类提供了很多控制对齐的常量.
    MainAxisAlignment
    • center → const MainAxisAlignment :子 widget在主轴方向上居中显示

    • end → const MainAxisAlignment :子 widget在主轴方向上居右边显示,如果是水平方向,那么由 TextDirection 来决定end是在左边(ltr)还是在右边(rtl)。如果是竖直方向,那么由 VerticalDirection 来决定end是在上边(up)还是在下边(down)

    • start → const MainAxisAlignment:子 widget在主轴方向上居左边显示,同理 end

    • spaceAround → const MainAxisAlignment:子widget中的第一个和最后一个widget距离边的距离是它与中间的距离的一半。(这里比较抽象,后期实现效果了会顺带讲解)

    • spaceBetween → const MainAxisAlignment:子widget中的第一个和最后一个贴边,剩余的子widget将中间的空间平分。

    • spaceEvenly → const MainAxisAlignment:子widget 将布局空间完全平分

      Row 方向完全平分
    • values → const List<<wbr>MainAxisAlignment>:根据值得大小来分配空间,值越大空间分配越多。

    CrossAxisAlignment 属性相同的基本和上面介绍的一样,就不赘述了。

    Expanded widget

    Expanded widget,可以将widget的大小设置为适和行或列 Expanded有个属性flex(弹性系数),默认情况下,每个widget的弹性系数为1,也就是会铺满布局。

    例如实现:三张图片水平铺满屏幕,即可使用Expanded包裹Image然后设置flex:1,不设置也行,因为默认flex就是1

    效果图

    伪代码如下:

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

    聚集 widgets

    默认情况下,行或列沿着其主轴会尽可能占用尽可能多的空间,但如果要将孩子紧密聚集在一起,可以将mainAxisSize设置为MainAxisSize.min

    例如实现:五个星形图标图标紧凑在一起。


    效果图

    伪代码如下:

    class _MyHomePageState extends State<MyHomePage> {
      @override
      Widget build(BuildContext context) {
        var packedRow = new Row(
          mainAxisSize: MainAxisSize.min,
          children: [
            new Icon(Icons.star, color: Colors.green[500]),
            new Icon(Icons.star, color: Colors.green[500]),
            new Icon(Icons.star, color: Colors.green[500]),
            new Icon(Icons.star, color: Colors.black),
            new Icon(Icons.star, color: Colors.black),
          ],
        );
    
      // ...
    }
    

    5. 图片、Icon、Text widget 的简单使用

    这些都是基础控件使用起来很简单的,可查看这个文档介绍:基础 Widget

    在学习完上面的内容我们可以实现出目前的样式了:


    flutter_1.gif

    实现列表功能,以及一个可滚动的详情页面,顶部的标题是 listview 传递过去的单词。

    文章篇幅过长了,这里就不介绍上图的实现过程了,源码地址如下
    觉得还凑合的给个star,好让我继续坚持写下去~
    源码地址

    看源码或者上文中不理解的可以联系我,我会知无不言的。

    本文完。

    相关文章

      网友评论

        本文标题:Flutter Widget 静态布局实战

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