草庐IT

Flutter Row 实例 —— 新手礼包

IAM17前端 2023-04-13 原文

大家好,我是 17。

本文在 3.31 日全站综合热榜第一。

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

本篇介绍 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 分配空间的过程是这样的。

  1. 先分配非弹性 child,比如 Container,Text,这些没有 flex 属性的 Widget。
  2. 把剩余的空间按 flex 值的大小,分给所有的弹性块。
  3. 如果是用 Expanded 包起来的 child, child 的大小就是 第 2 步分配空间的大小;如果是用 Flexible 包起来,child 的大小可以从 0 到 第 2 步分配空间的大小 之间自行决定。

这里说的 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 的常用的用法就都介绍完了。谢谢观看!

有关Flutter Row 实例 —— 新手礼包的更多相关文章

  1. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

    我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

  2. ruby 正则表达式 - 如何替换字符串中匹配项的第 n 个实例 - 2

    在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg

  3. ruby-on-rails - Rails 应用程序中的 Rails : How are you using application_controller. rb 是新手吗? - 2

    刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr

  4. ruby-on-rails - Rails - 从另一个模型中创建一个模型的实例 - 2

    我有一个正在构建的应用程序,我需要一个模型来创建另一个模型的实例。我希望每辆车都有4个轮胎。汽车模型classCar轮胎模型classTire但是,在make_tires内部有一个错误,如果我为Tire尝试它,则没有用于创建或新建的activerecord方法。当我检查轮胎时,它没有这些方法。我该如何补救?错误是这样的:未定义的方法'create'forActiveRecord::AttributeMethods::Serialization::Tire::Module我测试了两个环境:测试和开发,它们都因相同的错误而失败。 最佳答案

  5. ruby-on-rails - RSpec:避免使用允许接收的任何实例 - 2

    我正在处理旧代码的一部分。beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)endRubocop错误如下:Avoidstubbingusing'allow_any_instance_of'我读到了RuboCop::RSpec:AnyInstance我试着像下面那样改变它。由此beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)end对此:let(:sport_

  6. ruby-on-rails - 使用 ruby​​ 将多个实例变量转换为散列的更好方法? - 2

    我收到格式为的回复#我需要将其转换为哈希值(针对活跃商家)。目前我正在遍历变量并执行此操作:response.instance_variables.eachdo|r|my_hash.merge!(r.to_s.delete("@").intern=>response.instance_eval(r.to_s.delete("@")))end这有效,它将生成{:first="charlie",:last=>"kelly"},但它似乎有点hacky和不稳定。有更好的方法吗?编辑:我刚刚意识到我可以使用instance_variable_get作为该等式的第二部分,但这仍然是主要问题。

  7. ruby - 为什么当我调用类的实例方法时,初始化不显示为方法? - 2

    我正在写一篇关于在Ruby中几乎一切都是对象的博客文章,我试图通过以下示例来展示这一点:classCoolBeansattr_accessor:beansdefinitialize@bean=[]enddefcount_beans@beans.countendend所以从类中我们可以看出它有4个方法(当然,除非我错了):它可以在创建新实例时初始化一个默认的空bean数组它可以计算它有多少个bean它可以读取它有多少个bean(通过attr_accessor)它可以向空数组写入(或添加)更多bean(也通过attr_accessor)但是,当我询问类本身它有哪些实例方法时,我没有看到默认

  8. ruby - 在 Ruby 中,在类方法的上下文中,什么是实例变量和类变量? - 2

    如果我有以下一段Ruby代码:classBlahdefself.bleh@blih="Hello"@@bloh="World"endend@blih和@@bloh到底是什么?@blih是Blah类中的一个实例变量,@@bloh是Blah类中的一个类变量,对吗?这是否意味着@@bloh是Blah的类Class中的一个变量? 最佳答案 人们似乎忽略了该方法是类方法。@blih将是常量Bleh的类Class实例的实例变量。因此:irb(main):001:0>classBlehirb(main):002:1>defself.blehirb

  9. ruby - 从外部访问类的实例变量 - 2

    我理解(我认为)Ruby中类变量和类的实例变量之间的区别。我想知道如何从该类外部访问该类的实例变量。从内部(即在类方法中而不是实例方法中),它可以直接访问,但是从外部,有没有办法做MyClass.class.[@$#]variablename?我没有任何具体原因要这样做,只是学习Ruby并想知道是否可行。 最佳答案 classMyClass@my_class_instance_var="foo"class上述yield:>>foo我相信Arkku演示了如何从类外部访问类变量(@@),而不是类实例变量(@)。我从这篇文章中提取了上述内

  10. ruby - 为什么不能使用类IO的实例方法noecho? - 2

    print"Enteryourpassword:"pass=STDIN.noecho(&:gets)puts"Yourpasswordis#{pass}!"输出:Enteryourpassword:input.rb:2:in`':undefinedmethod`noecho'for#>(NoMethodError) 最佳答案 一开始require'io/console'后来的Ruby1.9.3 关于ruby-为什么不能使用类IO的实例方法noecho?,我们在StackOverflow上

随机推荐