欢迎访问移动开发之家(rcyd.net),关注移动开发教程。移动开发之家  移动开发问答|  每日更新
页面位置 : > > 内容正文

Flutter Row 实例 —— 新手礼包,本文在3.30日进入

来源: 开发者 投稿于  被查看 46189 次 评论:149

Flutter Row 实例 —— 新手礼包,本文在3.30日进入


大家好,我是 17。

本文在 3.30 日进入全站综合热榜前 3。

新手礼包一共 3 篇文章,每篇都是描述尽量详细,实例讲解,包会!

  • Flutter Row 实例 —— 新手礼包
  • Flutter TextField UI 实例 —— 新手礼包
  • Flutter TextField 交互实例 —— 新手礼包

本篇介绍 Row 的用法,用实例讲解 flex 弹性布局原理。本来在 Flutter 弹性布局的基石: Flex 和 Flexible 一文中的内容已经包含 Row 了,但因为 Row 太常用了,所以单写一篇。

Row 的尺寸

默认情况下, Row 在宽度上尽量大,在高度上只要能包住所有的 children 即可。

第一个示例给出全部代码,后面的只给出 Row 的代码。在 Row 的外面加一个 Container ,是为了给 Row 加一个 border,方便查看。减去 margin,padding,border 后 ,Row 的宽度为 300。

MaterialApp(
  home: Scaffold(
     body: Container(
         width: 342,
         alignment: Alignment.center,
         child: Container(
             padding: const EdgeInsets.all(10),
             margin: const EdgeInsets.all(10),
             decoration:
                 BoxDecoration(border: Border.all(color: Colors.blue)),
             child: Row(
               children: [
                 Container(
                   width: 100,
                   height: 50,
                   color: const Color.fromARGB(255, 82, 143, 222),
                 ),
               ],
             )))));

在本例中,Row 的宽度达到允许的最大值 300。高度为 50,正好可以包含蓝色块的高度。MainAxisSize.min 可以让 Row 的宽度也正好能包含 children。

Row(
    mainAxisSize: MainAxisSize.min,
    children: [
      Container(
        width: 100,
        height: 50,
        color: const Color.fromARGB(255, 82, 143, 222),
      ),
    ],
  );

mainAxisSize:MainAxisSize.min 让 Row 的宽度收缩,直到正好包含 所有 children 为止。本例中,正好包含蓝色块,最终宽度为 100。

mainAxisSize 的默认值是 mainAxisSize.max

非弹性布局

非弹性布局是说在 children 中没有 Expanded,Flexible 这种有 flex 参数的 child。

Row(
    children: [
      Container(
        width: 100,
        height: 50,
        color: const Color.fromARGB(255, 82, 143, 222),
      ),
      const Text(
        "IAM17",
        style:
            TextStyle(color: Color(0xFFC45F84), fontSize: 24),
      )
    ],
);

非弹性布局中,Row 的 children 在宽度方面没有限制, child 按自己期望的尺寸在水平方向依次排列。如果 children 的总宽度没有超过 Row 的宽度,没有什么问题。如果超过了 Row 的宽度,在开发环境下,会给出警告。

比如修改 Container 的 width:100width:400,这个时候 Row 已经没有多余的空间给 Text 了,甚是连 Container 也放不下。

在生产环境中,多出来的部分会被直接截断。

弹性布局

弹性布局是说在 children 中有 Expanded,Flexible 这种有 flex 参数的 child。

简单来说,Row 分配空间的过程是这样的。

这里说的 Flexible 的 fit 参数的值为 FlexFit.loose。Expanded 就是 fit 参数为 FlexFit.tight 的 Flexible。

让 child 占用所有分配到的空间,用 Expanded 包起来

Row(
   children: [
     Expanded(
         child: Container(
       width: 100,
       height: 50,
       color: const Color.fromARGB(255, 82, 143, 222),
     )),
     const Text(
       "IAM17",
       style:
           TextStyle(color: Color(0xFFC45F84), fontSize: 24),
     )
   ],
 )

Row 的宽度为 300,先给非弹性块 Text 分配固定大小空间,剩余的全分给的 Expanded。child 蓝色块占用所有 Expanded 分配到的空间。

我们注意到 Container 的 width 是 100,实际上,就算是这里写 0,或写 1000 都没有关系,用 Expanded 包起来的 child 的 width 属性会被忽略。

Expanded 包起来的 child 的 width 是不能自定义的,如果 child 要自定义 width 又要保持弹性布局怎么办?用 Flexible!

让 child 可以在分配到的空间内自行决定大小,用 Flexible 包起来。

在下面的例子中 Row 总的可用宽度为 300,两个 Container 各占 100,还余 100 空白在两个 Container 之间

Row(
     mainAxisAlignment: MainAxisAlignment.spaceBetween,
     children: [
       Container(
         width: 100,
         height: 50,
         color: Colors.blue,
       ),
       Container(
         width: 100,
         height: 50,
         color: Colors.red,
       ),
     ],
   )

现在我们把第一个 container 用 Flexible 包起来。

Row(
     mainAxisAlignment: MainAxisAlignment.spaceBetween,
     children: [
       Flexible(
           child: Container(
         width: 100,
         height: 50,
         color: Colors.blue,
       )),
       Container(
         width: 100,
         height: 50,
         color: Colors.red,
       ),
     ],
   )

重新执行查看效果,发现没什么变化。这是因为 总宽度为 300,分给非弹性红色块 100后还有 200, 唯一的弹性块拿到 200。蓝色块的 100 在分配到的空间范围内,所以没有什么反应。

把第一个 Container 的 width 加大到 150 查看效果,发现第一个 Container 的宽度变为 150 了。同理 150 也在 分配到的 200 之内。

继续加大 width 的宽度到 200,发现他们已经紧贴到一起了。继续加大就没有任何效果了,但也不会报错。

继续加大到 200 以上就超过分配到的 200了,所以宽度不再增加。不会报错是因为蓝色块被 Flexible 限制在 200 以内,加上红色块的总宽度在 300 以内,没有超出,当然不会报错。

如果左面 Container 的 宽度不是我们指定的,而是 Container 的 child 撑起来的,那么就可以实现宽度自适应的布局效果,不用担心会超出边界。

Flexible 的 flex 与 fit 参数

Flexible 的 flex 决定了可以分配多少剩余空间。fit 参数决定 child 能否自行决定大小。

看下面的的例子,红色块为固定宽度,绿蓝为弹性宽度。在fit: FlexFit.tight 的情况下,绿色块和红色块的 width 无效。因为 Flexible 的 flex 已经决定了宽度值,child 只能用这个值不能修改。

Row(children: [
     Container(
             width: 20, height: 50, color: Colors.red),
     Flexible(
         fit: FlexFit.tight,
         flex: 1,
         child: Container(
             width: 100, height: 50, color: Colors.green)),
     Flexible(
         fit: FlexFit.tight,
         flex: 2,
         child: Container(
             width: 100, height: 50, color: Colors.blue))
   ]

本例中 Row 的宽度为 320,首先分配 20 给固定宽度的红色块,剩余的 300 由两个弹性块瓜分。根据 flex 值,绿色块得到 100,蓝色块得到 200。Flex 值越大,得到的空间越大。

fit: FlexFit.tight 的 Flexible ,一般是用 Expanded。

fit: FlexFit.loose 的情况下,绿色块和红色块的 width 是有作用的,可以在 0 和最大值之间自定义自己的宽度。

我们把第二个 Flexible 的 fit: FlexFit.tight 修改为 fit: FlexFit.loose,蓝色块的 width 起作用了,显示为 100 宽。

我们调整一下摆放方式,蓝色块省出来的 100 空间被填充到各个块之间了。

 child: Row(
     mainAxisAlignment: MainAxisAlignment.spaceBetween,
     ...

在 children 之间加空白。

在 children 之间增加固定空白用 SizedBox

我们发现 Container 和 文本紧挨在一起了,想要他们之间有一个距离。可以用 Padding 把 Container 或 Text 包起来,但是这样写起来比较麻烦,而且多了一个层级,也不美观,不如用 SizedBox。

Row(
   children: [
      Container(
        width: 100,
        height: 50,
        color: const Color.fromARGB(255, 82, 143, 222),
      ),
      const SizedBox(width: 20,),
      const Text(
        "IAM17",
        style:
            TextStyle(color: Color(0xFFC45F84), fontSize: 24),
      )
    ],
  )

在 children 之间增加弹性空白用 Spacer

 Row(
     children: [
        Container(
         width: 100,
         height: 50,
         color: const Color.fromARGB(255, 82, 143, 222),
       ),
       const Spacer(),
       const Text(
         "IAM17",
         style:
             TextStyle(color: Color(0xFFC45F84), fontSize: 24),
       )
     ],
   )

文本 “IAM17” 与 蓝色块 之间的空白是 文本 “IAM17” 与 文本 “Flutter” 之间空白宽度的两倍。Row 的宽度如果增加或缩小,空白的宽度也会增加会缩小,但会保持两倍的关系。

平均分配空白

平均分配空白用 mainAxisAlignment 参数,具体用法详见 Flutter Wrap 图例。虽然讲的是 Wrap Widget,但是 alignment 与 Row 的 mainAxisAlignment 用法是一样的。比如两端对齐:

Row(
   mainAxisAlignment: MainAxisAlignment.spaceBetween,
   ...

Row 嵌套

Row 嵌套的时候需要注意下,因为一不小心会报错。

Row(
     children: [
       Row(children: [
         Expanded(
             child: Container(
           width: 100,
           height: 50,
           color: Color.fromARGB(255, 210, 74, 137),
         ))
       ]),
     ],
   )

Expanded 是要占据所有的可用空间,内层的 Row 在宽度可以是无限,Expanded 无法占据无限空间,所以报错。解决的办法很简单,让 内层的 Row 的宽度有限就可以了。

Row(
   children: [
     Expanded(
         child: Row(children: [
       Expanded(
           child: Container(
         width: 100,
         height: 50,
         color: Color.fromARGB(255, 210, 74, 137),
       ))
     ])),
   ],
 )

或者给 Row 加一个宽度约束。比如用 SizedBox 包起来。

Row(
   children: [
     SizedBox(
         width: 150,
         child: Row(children: [
           Expanded(
               child: Container(
             width: 100,
             height: 50,
             color: Color.fromARGB(255, 210, 74, 137),
           ))
         ])),
   ],
 );

用 Flexible 包起来也是可以的,Flexible 与 Expanded 的区别在于 Flexible 给 child loose 约束,Expanded 给 child tight 约束。通俗一点的说法是 Flexible 的 child 的宽度可以从 0 到 最大值之间自己决定。Expanded 的 child 的宽度只能是固定值,不能修改。

Row 嵌套的时候报错本质上是因为宽度无限,遇到其它宽度无限的场景也会出现这样的问题,比如 ListView 横向滚动的时候,把 Row 嵌套在 ListView 中也会有类似的问题。

ListView(
     scrollDirection: Axis.horizontal,
     children: [
       Row(children: [
         Expanded(
             child: Container(
           width: 100,
           height: 50,
           color: Color.fromARGB(255, 210, 74, 137),
         ))
       ]),
     ],
   )

解决办法相同,也是把 Row 用 Flexible 或 Expanded 包起来,或加一个宽度约束。

ListView(
    scrollDirection: Axis.horizontal,
    children: [
      Expanded(
          child: Row(children: [
        Expanded(
            child: Container(
          width: 100,
          height: 50,
          color: Color.fromARGB(255, 210, 74, 137),
        ))
      ])),
    ],
  );

到这里 Flutter Row 的常用的用法就都介绍完了。谢谢观看!

相关频道:

用户评论