Day 17 | Flutter的常用 widgets - Container、Row、Column

StatefulWidget 的build

回到昨天 StatefulWidget 的 build

https://ithelp.ithome.com.tw/upload/images/20210930/20112906GVWMdP32Cx.png

会先看到 Scaffold 这个 widget ,这是一个Material 所提供的一个基本的排版widget,通常都会当作一个页面的开始,这里有用上三个参数 appBarbodyfloatingActionButton

appBar 就是app最上方的工具列, 这里是用 AppBar 这个widget ,主要就是一些页面标题或者操作选单或回到上一页按钮会出现在这里,appBar的型别是 PreferredSizeWidget ,所以其实只要是PreferredSizeWidget 都可以传进 appBar 也不一定要用AppBar 就是了。

然後是body 就是主要的显示区块,通常都会放置我们主要要操作及显示的Widgets。

floatingActionButton 则是那颗悬浮按钮通常会是那页最主要的操作,像是google导航的导航按钮就是做在这颗按钮。而他的位置预设会是右下角也可以使用其他参数来控制他的位置。

body 里的 widget其实我们光是看命名也很好猜出他们的功能是什麽, Center 就是置中用、 Column 就是一个直行的容器, Text 就是放文字的。这里就能体会到 aggressive composability 的好处就是当每个功能/排版/容器都是一个widget时我们可以从命名就能知道他的作用是什麽,而缺点是等你开始组成一个复杂的layout你会发现这个深度会令人无法直视。

常用widget

就 flutter 官方上的 widget 列表的数量来说一个一个介绍显然不切实际,所以大概就只会简介一下常用的widget。

通常我在找我想用的widget时如果是没有比较复杂互动的widget的话,我倾向先去我 catalog 来查找,当然如果真的很复杂的功能就会去找第三方套件之类的。

所以我们来看一下这个 catalog 有哪些常用的widget。

基本上最最最常用的还是 Layout 这个分类的widget,在这个分类下有分成这三种widget。

Single-child layout widgets

接受widget的参数是 child 也就是只能传入单一个 widget 的 widget,像是 ContainerSizedBoxExpandedPadding 等等。

Multi-child layout widgets

接受widget的参数是 children 所以就是能传入 List<Widget> ,这类的有 RowColumnStackListViewGirdView 等等。

Sliver widgets

可以自定义滚动效果的widget,但这部分我就没有实作就不额外说明了。

Container

而另外一个最常用的就是 Container ,基本上是可以想像成没有很自由的 div 在使用,大部分是拿来当作一个元件的基底widget,可以设定固定宽高及padding,可以使用 decoration 做额外的样式等等设定。

Container(
	width: 300,
  height: 300,
  child: Text('这是一个高宽都是300px的Container'),
  decoration: BoxDecoration(color: Colors.black26),
),

https://ithelp.ithome.com.tw/upload/images/20210930/20112906vubUXjiz1F.png

Row Column

在Flutter中的RowColumn 都是flex的所以如果有写过网页前端的读者应该能蛮容易理解Row、Column的排版逻辑。

基本上跟css中的flexBox的概念一样,会有两条轴与主轴方向平行一致就是Main Axis与之垂直就是Cross Axis。

https://ithelp.ithome.com.tw/upload/images/20210930/201129068DZrat0xwX.png
https://ithelp.ithome.com.tw/upload/images/20210930/20112906BSoPWAN9gL.png

所以在预设专案的这段中

 Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        const Text(
          'You have pushed the button this many times:',
        ),
        Text(
        '$_counter',
        style: Theme.of(context).textTheme.headline4,
        ),
      ],
    ),

ColumnmainAxisAlignment 的意思是指在主轴上的对齐模式,所以以上面例子来看就是:对在主轴方向置中,也就是从画面上是垂直置中。

MainAxisAlignment 总共有几个值

MainAxisAlignment.start: 尽可能将children向主轴起点放置

MainAxisAlignment.end: 尽可能将children向主轴终点放置

MainAxisAlignment.center: 尽可能将children向主轴中间放置

MainAxisAlignment.spaceBetween: 将剩余间距分散在各元素之间但第一个元素的前面及最後一个没有间距。

MainAxisAlignment.spaceAround: 将剩余间距分散在各元素之间但第一个元素的前面及最後一个没只有一半的间距。

MainAxisAlignment.spaceEvenly: 将间距平均分散在每个元素之间包含第一个元素的前面及最後一个元素後面

使用 Row 来排列的话,这三者的差异在画面上的差异是这样:

https://ithelp.ithome.com.tw/upload/images/20210930/20112906aM4Ba2PVKP.png

CrossAxisAlignment 则是垂直轴的排列方式:

CrossAxisAlignment.start:元素的开始位置是在垂直轴的起点

CrossAxisAlignment.end:元素的开始位置是在垂直轴的终点

CrossAxisAlignment.center:元素的开始位置是在垂直轴的中间

CrossAxisAlignment.stretch:将元素占满垂直轴

CrossAxisAlignment.baseline:将元素按照垂直轴的基准线排列(不常用)

Row 来说 CrossAxisAlignment.start 就是元素会贴着上面,CrossAxisAlignment.center就会是垂直置中。

Row及Column的要注意的一点是 「不能换行」 ,他们就是单纯的一条线,如果超出外面容器的宽度就会直接跳出错误。

这边是一个 Container 包着 Column 再包 Row

Container(
  width: 300,
  child: Column(
  children: [
	  Text('这是一个宽度300px的Container'),
		  Row(
			  children: [
		      ...List.generate(
		          5,
		          (_) => const Text('123456456'),
		          )
		        ],
		      )
		    ],
		  ),
	decoration: BoxDecoration(color: Colors.black26),
),
    

https://ithelp.ithome.com.tw/upload/images/20210930/201129066daptvvxb4.png

以这个例子来说通常是会将 Row 换成 Wrap 来达成换行的需求。

Container(
  width: 300,
  child: Column(
  children: [
	  Text('这是一个宽度300px的Container'),
		  Wrap(
			  children: [
		      ...List.generate(
		          5,
		          (_) => const Text('123456456'),
		          )
		        ],
		      )
		    ],
		  ),
	decoration: BoxDecoration(color: Colors.black26),
),
    

https://ithelp.ithome.com.tw/upload/images/20210930/20112906e5norVmztW.png


我个人是觉得会使用 ContainerRowColumn 就能完成一些很基础的排版需求了,剩下的等实际开发时遇到再来说明。

明天就来做我们第一个小专案也顺便开始真正的运用状态管理。


<<:  【Day15】:STM32辗压Arduino的功能—TIM(下)

>>:  使用 State Hook (Day16)

【左京淳的JAVA WEB学习笔记】第五章 过滤器与监听器

过滤器可以用来做权限校验或是编码转换等功能。 多个过滤器可以串联在一起,做多重过滤。 自定义的过滤器...

[Day4] Jetpack Compose: 要如何让元件和我们来点互动?

知道怎麽构建UI後,我们来学学怎麽跟UI的互动 clickable 最简单的方式就是新增Modifi...

[D19] DL 深度学习(2)

可能经过一些介绍後,大家还是多多少少会觉得"深度学习"听起来是深奥难懂的概念,不过台大电机系教授李宏...

Day5-D3 资料绑定 Data Binding:data( ) 跟 datum( )

本篇大纲:Joining Data、绑定 DOM 元素跟资料的方法、data 跟 datum 的比...

这个赌场在玩什麽把戏 - 金融商品内容

事前提要: 本 API 系为 永丰金 PYTHON API,尚未申请的朋友们,有两个方法可以申请 洽...