Flutter基础组件
Flutter是一套跨平台方案,使用Dart语言设计,作为移动开发者的我们基本上是必须要掌握这些技能的。既然是一套UI框架,那么我们在学习时也是需要从它内置的一些基础组件开始学习。只有先掌握了基础组件,才能在后续中把握更加复杂的页面布局和自定义布局。
Widget
在Android中,所有的组件都是View,对于复杂的布局也是通过ViewGroup进行组合的。而Flutter也是如此,只是Flutter对于组件更加细化,例如padding在Android中只是View的一个属性,而在Flutter中则被抽象成一个组件,这点和Compose是一样的。
Text
1 | |
通过参数控制文本的行为,参数较多,这里只列出了常用的几个。其中最主要的就是data属性,也是必填参数,这是文本组件的文本内容。其他的是几个可选参数,如style参数,类型是TextStyle,可以通过它来设置文本的颜色,背景色,字体,字重,字体大小等等,它涉及到的都是文本的描述属性。其他属性如textAlign,类型为TextAlign枚举,用于控制文本的对齐方式,softwrap是否自动换行,默认自动化行;overflow文本超过限制后的行为属性,可以设置为剪切、渐变、打点等;maxLines最大行数。
1 | |
Image
1 | |
Image作为图片显示的组件也是非常重要的,这里我们先简单关注下它的一些属性,在其构造方法中,需要注意的就是三个属性,一个是image,另外两个是宽高。其中image是一个ImageProvider类型, 用于提供图片,当然实际上我们很少会直接通过这种方式去创建一个图片显示,而是通过它的命名构造方法去进行创建。如Image.asset、Image.memory、Image.file、Image.network等,这些方法相比于直接通过默认构造方法,就是提供了image属性,也即是提供了对应的ImageProvider的实现类。
1 | |
Icon
Icon作为小图标,实际上和Image一样都是用来显示一个图片的,只是Icon更倾向于作为一个小图标,如点赞的爱心或拇指等这种svg小图标。
1 | |
其实最主要的也就是这几个属性了,首先是icon必传属性,类型是一个IconData类型,也即是显示的那个图标的数据。其次是size属性控制其大小,注意Icon的宽高是一致的,不需要单独设置宽高。color则是图标的颜色,可以通过代码控制颜色。
在Flutter中默认会内置一些material的icon资源,如果我们想用这些资源的话,可以直接通过Icons来使用,例如下面:
1 | |
当然,默认内置的这些Icon肯定是无法满足我们实际的项目需求的,因此我们可以引入自定义的一些Icon,这种引入方式实际上就是通过字体ttf引入,通过将Icon打包到ttf中,然后在使用的时候通过设置IconData来访问。
1 | |
唯一需要注意的就是在IconData的参数中的第一个参数,它代表的是图标的代码,这是在构建ttf文件时就已经确定的。
ElevatedButton
Flutter中内置了好几种的按钮类型,可以根据需要进行选择,它们的用法基本上是一致的,只是样式有所不同。如ElevatedButton,是一个悬浮按钮,即默认看起来是悬浮的,有阴影效果和圆角效果。
1 | |
其中onPressed参数接收一个空参的函数类型,作为点击事件的回调,属于必须添加的属性,在安卓中这种事件一般被称为onClick。另外还有一个长按的回调,onLongPress也是一个空参数的函数类型。onHover鼠标悬停时的回调,是一个bool参数的函数类型。onFocusChange是焦点发生变化时的回调。
然后就是style,它是控制整个按钮样式的属性,类型为ButtonStyle,可以通过它来设置按钮的背景色、前景色、阴影、圆角、高度、间距、形状等等。在我们实际的开发中,按钮的样式肯定是不会和默认保持一致的,因此我们需要重新设置style以适配我们自己项目的按钮样式。
还有一个必填的属性就是child,一般情况下我们会将其设置为Text,即按钮中间的文本。也就是说,按钮实际上是由两部分组成,一部分是外部的按钮样式(边框圆角等),一部分是内部的文案(按钮文案等)。
1 | |
另外还有别的按钮类型,它们都是继承自ButtonStyleButton,参数也都是一样,连用法都是一模一样,区别就是样式不同。而样式都是通过style属性控制的,所以大致了解下就行,毕竟实际项目中我们肯定会自定义按钮样式的。
FilledButton:在ElevatedButton的基础上去除了阴影,背景使用主题色填充。
OutlinedButton:在FilledButton的基础上移除了背景,但是添加了边框。
TextButton:在OutlinedButton的基础上移除了边框。
另外这些按钮组件,还提供了一个带Icon的样式的按钮,通过它的命名构造函数icon来创建。
1 | |
注意,通过.icon创建的按钮,参数的变化。首先多了一个icon参数,但不是必须的。然后就是原本按钮的child属性,改名为label属性,当然实际上类型没变。通过这种方式设置的按钮,会在文本的前面加上一个icon图标。
除了上面的几个同系列的按钮外,还有别的类型的按钮,只是它们不是同一个子类,所以可能参数上会有一些不同。
IconButton:将icon变成一个按钮。
1 | |
而继承自IconButton的按钮,还有BackButton、CloseButton、DrawerButton等,实际上就是传入了不同的Icon而已。
CheckBox
1 | |
选择框实际中我们也应用的挺多的,CheckBox组件就是一个选择框,主要的就是三个属性,其中两个是必选属性。
value是一个可空的布尔值类型bool?,值为false表示未勾选,值为true表示已勾选,值为null表示部分勾选。
tristate:是否支持三态模型,即部分勾选。默认为false。
onChanged:当点击选择框时的回调。
其他属性比较多,可以充分进行自定义,如设置颜色、形状、圆角、水波纹等等,我们可以基于此来设置一些属性以符合项目需求,但大部分情况下是无法符合的,通常需要我们去自定义。
和它类似的还有一个Switch组件,即开关组件,当然大部分情况下也无法满足我们的项目样式需求,也是需要进行自定义的。
TextField
输入框也是我们用的非常多的一个组件,用于文本的输入。它的构造方法参数也是非常多的,用于控制各种行为属性,但是它并没有必选参数,下面简单看下它的常用的参数。
controller属性的类型是TextEditingController,用于控制文本的编辑的,通过controller可以拿到输入框的文案,以及向输入框中设置文案内容。也可以拿到输入框的选中内容,选中的位置信息等。
1 | |
controller除了能过设置和获取到文本外,还可以添加监听addListener,当输入框发生任何变化时(文本变化,光标变化等等)都会触发它的回调,非常灵活,但同时调用也会非常频繁。
decoration参数用于控制输入框的样式,可以设置icon、label、hint、border、以及各种颜色和前缀后缀等,通常情况下我们都需要设置这个属性,以适配项目的UI需求。
textInputAction文本输入内容,通常对应的是键盘的右下角,会显示成搜索图标或者完成等信息,对应安卓中是imeOptions。参数类型是TextInputAction枚举类,可以选择多种情况,常见的有search、done、next等等。
textCapitalization参数表示文本输入模式,也是一个枚举类,针对的是键盘的行为属性。设置为none(默认)唤起键盘后会设置成全小写输入,设置为characters唤起的键盘是全大写输入,设置为words则是每个单词的首字母大写,设置为sentences是每个句子的首字母大写。当然这些只针对英文输入法,切换为中文时无效。
style设置的输入的文本的样式,和Text的style属性一模一样。
obscureText设置为密码模式,设为true后文本默认会用.代替,也可以通过obscuringCharacter将替换文本修改为别的。
其他还有很多属性,常用的不多了,还有一个textAlign表示文本的对其方式,一个onChanged回调方法,当文本变化时会被调用。
Row
基础组件中其实也就Text和Image了,其他的组件基本上都是各种组合组件,用于对其他组件进行组合的。如Row就是提供了一个列,允许存放多个子组件,组件水平进行排列。
1 | |
构造方法也是比较简单,只有几个简单的属性。其中mainAxisAlignment表示的是主方向上的布局方式,是一个枚举类,可选的有start、end、center、spaceBetween等多种值,该组件为Row组件,所以主方向就是横向。
然后就是mainAxisSize,主方向上的尺寸,也是一个枚举值,可选为max(默认)或者min,当选择最大时,Row会在横向上填充父布局的宽度,类似于安卓中的match_parent,而min则是对应的wrap_content。
crossAxisAlignment表示的是交叉轴的对其方式,对于Row而言就是垂直方向上的对其方式,取值为枚举值,有start、end、baseline等,其中如果Row中的子组件为Text的时候,最好选择baseline的对其方式。
spacing间隔,表示的是子组件之间的间隔,注意这个间隔不包括子组件对最左侧和最右侧的间距。
children子组件数组,Row会对这个属性中的组件按照上面的规则进行排列。
1 | |
Column
Column组件的构造参数和前面的Row一模一样,它们的表现形式也是一样,只是Row是横向进行排列,而Column是纵向排列。
Expanded
对于Column和Row而言,实际上就类似于安卓中的LinearLayout的横竖两种排列方式,而LinearLayout还支持weight属性,子View会按照weight的权重来瓜分LinearLayout的空间。而在Flutter中,则是将weight的行为也进行抽象,变成了一个Expanded组件。
1 | |
其构造方法中,flex对应的就是weight属性,也即是权重。接下来我们就可以在Row或者Column中通过Expanded来使其子组件瓜分父组件的宽或高了。
1 | |
Spacer
除了Expanded外,还有一个组件也是用比例来瓜分父布局宽或高的,它就是Spacer。
1 | |
flex参数同样是比例参数,Expanded参数是将子布局包裹起来作为一个整体,然后通过比例值进行划分。而Spacer则是单独自己,作为一个空间,单独按照比例进行区分。
1 | |
例如上面代码,就是在三个文本显示的情况下,将Row的横向剩余空间分成三份,在hello1和hello2之间占一份,而hello2和hello3之间占两份。
Container
Container是一个容器组件,它只接受一个子组件,用来对子组件进行布局控制的。
1 | |
参数比较多,首先就是alignment参数,控制的是子组件在它内部的布局位置,类型为AlignmentGeometry,实际中我们可以通过AlignmentDirectional的一些内置静态成员来进行设置,如可以设置为AlignmentDirectional.topEnd让子组件显示在右上角。
padding用于设置内边距(margin用于设置外边距),类型都为EdgeInsetsGeometry,实际设置中我们可以通过EdgeInsets.all或者EdgeInsets.only来进行设置。
color设置容器的颜色,或者说是背景色。width和height设置宽高。
decoration装饰,用于装饰子组件,类型为Decoration,实际使用中可以使用BoxDecoration或者ShapeDecoration。注意添加了decoration后不能在设置color。
1 | |
上面是设置的BoxDecoration可以设置颜色,边框等,如果是ShapeDecoration的话,则无法设置边框了,只能设置颜色和形状。
Padding
前面在Container中,可以通过它的padding属性来设置内边距,实际上有一个单独的组件就叫做Padding,专门用于设置边距。
1 | |
注意这里的Padding组件虽然是设置内边距,但是相对于它的子组件而言它就是设置外边距的,具体看怎么理解。例如下面给Text设置边距。
1 | |
对于Column而言,实际上是设置了一个10的内边距,而对于Text而言,则是设置了一个10的外边距,所以加外边距还是加内边距,需要加到合适的位置。因此,只需要一个Padding组件即可,而不需要Margin组件。
Divider/VerticalDivider
对于一些需要边距的,我们可以通过Padding来设置,但是用Padding的话必须将子布局包起来,这对于一些边框需要比较多的场景就太麻烦了。例如在一个Row中,有三个子组件,现在想要子组件之间加上20的间隔。
1 | |
可以通过spacing属性完成,如果不用这个属性的话,需要用Padding将三个Text包裹起来,然后设置对应的左右边距,无疑非常麻烦。而通过VerticalDivider既可轻松实现。
1 | |
Divider是水平方向的分割线,而VerticalDivider则是垂直方向上的分割线。下面看下构造方法:
1 | |
接下来看下它的参数,首先就是和height,表示的是Divider的高度。thickness则是分割线的粗细,如果我们的Divider的高度比thickness大的话,分割线会居中显示在Divider的中间。
然后就是indent和endIndent,表示的是分割线的起始位置和终止位置的间隔,例如在Divider中,就表示的是分割线的最左边和最右边会留出一部分的空白,即分割线不会延伸到最边缘上。
color设置分割线的颜色,而radius设置分割线的圆角。
Card
Card
相比于Container而言,Card更关注于卡片布局,即圆角和阴影。如果使用Container的话,则需要通过BoxDecoration来设置阴影,而使用Card则可以更加关注这些。
1 | |
color设置的是卡片的背景色,shadowColor设置阴影色,通常情况下我们不需要去设置阴影颜色,保持默认的就行。elevation视觉高度,设置这个高度后,可以使得卡片布局的阴影更大,就好像就卡片拉高了一样。
shape形状,类型为ShapeBorder,可以设置不同的形状。通过shape属性,可以设置卡片布局的边框和圆角等。例如可以设置为CircleBorder将卡片布局变成一个圆形的布局,也可以通过RoundedRectangleBorder设置为一个圆角的布局。
1 | |
用法比较简单,没有什么需要注意的,主要突出的就是一个卡片效果和阴影效果
Stack
类似于安卓中的FrameLayout,将所有的子布局直接重叠放置,后面的子布局会放置在前面的子布局的上面,从而可能会遮挡住前面的子布局。
1 | |
Center/Align
放置子组件的位置,Center将子组件放置在内部的居中位置,Align默认也是放置在中间位置,但是允许设置成别的位置。
1 | |
默认情况下,alignment取值为center,会将子组件放置在居中位置。其中widthFactor和heightFactor表示宽高的缩放比, 例如widthFactor为2的时候,Align组件的宽度会是子组件的宽度的2倍。
1 | |
Center是继承自Align的,只是去除了alignment参数,即不允许设置对其方法,只使用默认的居中对齐。
ListView
1 | |
ListView也是非常常用的组件,用于构建列表,其中属性也比较多。重要的有scrollDirection滚动方向,可以设置为横向或者纵向。controller用来控制列表的滚动,可以获取到滚动的位置和偏移量,也可以主动控制列表滚动到某个位置等。然后就是padding,可以增加边距,当然不设置的话也可以用Padding组将将其包裹一下来实现相同的效果。最后就是children属性,列表的每一项,当item个数超过组件高度后,列表可以进行滚动。
当然实际上并不会直接通过ListView的这种方式去创建列表,因为一般情况下列表的条目都不是固定的,基本上都是根据请求的数据来进行动态构建,这就需要用到ListView.builder来动态创建。参数基本上是一样的,只是多了一个必选参数,itemBuilder参数,这个参数是一个函数类型参数,用于构建每一个item。
1 | |
需要提供两个参数,一个是itemCount表示需要生成多少个条目,一个是itemBuilder用来实际进行构建条目。
PageView
PageView是多界面的组件,类似于安卓中的ViewPager,用的地方也是挺多的。例如banner布局通常会用到这个组件,首页也会用这个组件做多个tab的分界面。
1 | |
属性比较多,首先是scrollDirection滚动方向,默认是横向滚动的,可以设置为纵向滚动,可以做成类似于抖音的视频滑动那样。
controller控制器,控制组件的页面切换,可以通过控制器切换到下一页、上一页或者指定页等。
physics物理引擎,控制组件的滑动情况,可以设置为正常滑动、回弹滑动、不允许滑动等。
pageSnapping是否自动归位,当滑动距离小于一半的时候,界面会自动划回来,超过一半的时候会自动划到下一页,默认为true。
onPageChanged界面切换时的回调,children子布局的集合。
总结
实际上Flutter中的默认Widget远不止这些,但是用法大同小异,通过参数名基本上就能猜个差不多,而且方法的注释也是非常多的,直接点击进入源码查看相应的方法注释也可以理解这些组件。
