Flutter的设计思路是一切皆是 “Widget” 。Widget就是我们开发所使用的组件,开发者使用它来完成页面开发、UI显示、动作交互等等。
Widget总览
Flutter的Widget可以分为三大类:组合型、渲染型、代理型。
组合型就是我们常见的StatelessWidget、StatefulWidget。渲染型大多数是系统提供的直接使用的组件,比如Padding、RichText等等。代理型就是为子节点提供代理功能,比如InheritedWidget等等。
StatelessWidget
StatelessWidget是无状态组件,2.2版本的SDK系统为我们提供了大约109的该类型组件。如下图:
图很大,大家可以点开查看,我们常用的组件有Container、ListView、Visibility、SafeArea等等。
StatefulWidget
StatefulWidget是状态组件,2.2版本的SDK为我们提供了170+的该类型组件,如下图:
图很长,大家可以点开查看。我们常用的该类型的组件有:回退拦截的WillPopScope、输入框的TextField、浮窗组件Overlay、页面容器Scaffold等等。
RenderObjectWidget
RenderObjectWidget是渲染类型的组件,提供了一个用于渲染的对象。
我们常用的该类型的组件有:弹性布局的Row、Column,处理间距的Padding,布局约束的SizedBox,Sliver家族的List等等。
ProxyWidget
ProxyWidget是代理组件,并不参与绘制。 相对来说,Flutter中这个类型的Widget要少很多。
我们常见的该类型的组件大多数和主题、配置等相关,比如尺寸相关的MediaQuery,主题相关的SliderTheme,配置相关的KeepAlive等等。
小结
上面就是Widget的总览,很多也很杂,看见就头疼🙄。在我们的日常开发中,我们常用的可能就那么几个:页面脚手架Scaffold、万金油Container、横向布局Row、纵向布局Column、流式布局Wrap、层叠布局Stack、列表滚动ListView、网格滚动GridView、嵌套滑动CustomScrollView、单一滚动SingleChildScrollView、手势点击GestureDetector、图文相关的组件、再加上状态管理相关组件ValueListenableBuilder等等。
这些差不多分为五类:图文组件、容器组件、布局组件、滚动组件和功能组件。下面我们依次看其详细用法。
图文组件
图文组件顾名思义就是显示文本和图片的,常用的控件有:纯文本的Text、富文本的RichText、简单图片的Image、过度效果的FadeInImage。
纯文本Text
A run of text with a single style. 显示单一样式的文本组件。
| 属性 | 类型 | 作用 |
|---|---|---|
| data | String | 必填,待显示的文本内容。 |
| key | Key? | 组件的key |
| style | TextStyle? | 文本显示的样式,字体颜色、文字背景色和前景色、文字大小、字体的权重等等,和文字显示相关的一切属性都在Style中。 |
| strutStyle | StrutStyle? | 和文字的基线相关 |
| textAlign | TextAlign? | 文字的对齐方式 |
| textDirection | TextDirection? | 文字的方向,是从左到右还是从右到左 |
| locale | Locale? | 语言和格式首选项的标识符。 |
| softWrap | bool? | 文字是否在换行时折断 |
| overflow | TextOverflow? | 文字溢出时的处理效果 |
| textScaleFactor | double? | 文字的缩放因子 |
| maxLines | int? | 文字的最大行数 |
| semanticsLabel | String? | 文字的语义标签(辅助功能) |
| textWidthBasis | TextWidthBasis? | 文字的测量方法 |
| textHeightBehavior | TextHeightBehavior? | 处理文字上下的高度 |
下面我们看实际的小栗子
class _TextTestPageState extends State<TextTestPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Text组件'),
),
body: Center(child: Text('Text文本组件演示')),
);
}
}
屏幕中间就是最简单的文本组件,运行效果如下:
修改文字的颜色、字体等只需要增加TextStyle属性
除了简单的字号、权重和颜色之外。TextStyle还支持修改文字的间距、文字的背景色以及文字的装饰等等。
letterSpacing属性是单个字母或者汉字之间的间距,wordSpacing是单词之间的间距,单词是空格隔开的两部分。
从上图可以明显看出来,字符之间有了间距。
从上图可以看到 单词之间的间隙变大了。
除了简单的backgroundColor之外,还可以使用画笔Paint类型的background,来作为文字的背景。
不同与backgroundColor让整个文字的背景设置为颜色,使用Paint可以支持边框、抗锯齿等效果,上面就是设置了黑色的边框。🧐注意的是由于中英文的差异,英文字母的整体行高要矮一些。
除了上面的文字样式的修改,Text还有一些处理 文本组件的修改。
比如maxLine是最大的行数,overflow处理文字的溢出效果。
enum TextOverflow {
/// 直接截断
clip,
/// 文字渐隐
fade,
/// 结尾...展示
ellipsis,
/// 超出部分依然渲染 虽然看不见
visible,
}
这就是行数和溢出效果的控制。这里注意一下,TextOverflow.fade需要配合softWrap属性使用,softWrap设置为false的时候,才可以渐隐消失。缺陷就是 只能显示 一行。
以上就是纯文本组件常用的一些属性。
富文本RichText
Text是简单的文本组件,富文本可以通过RichText来实现。
| 属性 | 类型 | 作用 |
|---|---|---|
| text | InlineSpan | 必填,待显示的富文本内容 |
| key | Key? | 组件的key |
| strutStyle | StrutStyle? | 和文字的基线相关 |
| textAlign | TextAlign? | 文字的对齐方式 |
| textDirection | TextDirection? | 文字的方向,是从左到右还是从右到左 |
| locale | Locale? | 语言和格式首选项的标识符。 |
| softWrap | bool? | 文字是否在换行时折断 |
| overflow | TextOverflow? | 文字溢出时的处理效果 |
| textScaleFactor | double? | 文字的缩放因子 |
| maxLines | int? | 文字的最大行数 |
| textWidthBasis | TextWidthBasis? | 文字的测量方法 |
| textHeightBehavior | TextHeightBehavior? | 处理文字上下的高度 |
除了text之外,其他的属性和Text的作用是一样的。InlineSpan有两个子类:显示文本的TextSpan和显示自定义Widget的WidgetSpan。
TextSpan:
String? text 显示的文本内容
List? children 除了文本之外的其他内容
TextStyle? style span的文本样式
GestureRecognizer? recognizer 文本的手势事件
MouseCursor? mouseCursor 手指或者鼠标悬停在span上的光标
WidgetSpan:
Widget child 显示的widget
ui.PlaceholderAlignment alignment widget的对齐方式
TextBaseline? baseline widget的文字基线
TextStyle? style widget的文本样式
除了显示内容的区别之外,TextSpan还支持Span的拼接,而WidgetSpan就已经是终点了。
上述的代码如下:
RichText(
text: TextSpan(
text: "灰色的片段",
style: TextStyle(color: Colors.black12, fontSize: 16),
children: <InlineSpan>[
TextSpan(
text: '红色可点击的片段',
recognizer: TapGestureRecognizer()
..onTap = () {
print('onTap');
},
style: TextStyle(color: Colors.red, fontSize: 16),
),
WidgetSpan(child: Icon(Icons.ac_unit))
]),
)
recognizer除了点击手势之外,还有长按等手势,可以通过RichText实现解析css、xml等工具组件。
图片组件Image
最简单的图片展示组件就是Image。
| 属性 | 类型 | 作用 |
|---|---|---|
| key | Key? | 组件的Key |
| image | ImageProvider | 图片组件显示的内容,网络、文件、Asset、内存等 |
| frameBuilder | ImageFrameBuilder? | 构造出代表Image的Widget, 一般使用这个属性来添加一个图片的效果比如渐入 或者 占位等等 |
| loadingBuilder | ImageLoadingBuilder? | 图片的夹在builder,显示加载过程中的Widget |
| errorBuilder | ImageErrorWidgetBuilder? | 图片加载失败展示的Widget |
| semanticLabel | String? | 图片控件的辅助语义 |
| excludeFromSemantics | bool | 是否排除辅助语义 |
| width | double? | 图片空间的宽度 |
| height | double? | 图片空间的高度 |
| color | Color? | 图片的颜色 |
| colorBlendMode | BlendMode? | 图像的混合模式juejin.cn/post/684490… |
| fit | BoxFit? | 图像的缩放、自适应等参数 |
| alignment | AlignmentGeometry | 图像在控件内部的对齐方式 |
| repeat | ImageRepeat | 图像在控件内部的重复方式 |
| centerSlice | Rect? | .9图片的区域 |
| matchTextDirection | bool | 是否沿着TextDirection进行绘制 |
| gaplessPlayback | bool | 是否展示旧图片 |
| isAntiAlias | bool | 是否抗锯齿来绘制图像。 |
| filterQuality | FilterQuality | 图像的质量 |
上面就是图片的基本属性。我们一般显示图片的时候,使用指定的命名构造。
[Image], for obtaining an image from an [ImageProvider]. 自己指定显示图片的ImageProvider
[Image.asset], for obtaining an image from an [AssetBundle]using a key. 从AssetBundle中加载asset图片
[Image.network], for obtaining an image from a URL. 从网络加载图片
[Image.file], for obtaining an image from a [File]. 从文件系统中加载图片
[Image.memory], for obtaining an image from a [Uint8List]. 从内存中加载图片
我们下面以网络图片为例,查看属性的使用含义。
class _ImageTestPageState extends State<ImageTestPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Image组件'),
),
body: Image.network(
'https://flutterchina.club/images/homepage/header-illustration.png',
),
);
}
}
展示效果如下:
我们可以通过height和width来设置宽高
通过frameBuilder可以实现渐显等效果,frameBuilder的含义如下:
构造出代表Image的widget
如果属性为null,那么只要图片可用了,widget就会被绘制。
一般使用这个属性来添加一个图片的效果比如渐入 或者 占位等等。
可以通过loadingBuilder实现更精细的控制。
如果loadingBuilder不为null,那么两个属性就链式处理了。frame的结果会被传递到loadingBuilder中。
方法的签名如下:
typedef ImageFrameBuilder = Widget Function(
BuildContext context,
Widget child,
int? frame,
bool wasSynchronouslyLoaded,
)
context:就是上下文。
child:是图片组件,并且保证是非空的。这样我们就可以对原始的图片组件进行包装,比如包装一个动画效果等等。
frame: 代表当前图片的绘制帧,null的时候图片还没被绘制。
wasSynchronouslyLoaded:图片是否已经加载完成了,如果是false,那么图片还不可被绘制
通过loadingBuilder可以实现加载中的效果,loadingBuilder的含义如下:
加载过程中展示的Widget,如果设置为null,那么图片就会递增的加载,用户也不会收到加载的过程。
方法的签名如下:
typedef ImageLoadingBuilder = Widget Function(
BuildContext context,
Widget child,
ImageChunkEvent? loadingProgress,
);
context:就是上下文
child:是图片组件,并且保证是非空的。如果frameBuilder非空的话,会将结果传递进来
loadingProgress: 代表当前的加载阶段,cumulativeBytesLoaded是已经加载的字节数
expectedTotalBytes是总字节数
我们可以通过color来设置图片的颜色,这里的设置规则是 将图片中的有颜色的像素 都设置为设置的颜色。
有了这个属性,我们就可以给同一个Icon设置不同的渠道颜色。
图片也可以进行缩放和重复,就是scale和repeat属性。
scale属性类似于我们说的几x,比如1x、2x、3x。3x的意思是 一个逻辑像素 对应着 9个图片像素,也就是说图片的宽是实际绘制的宽的3倍。
不限制图片大小的话,图片控件的范围就是 它本身的大小。比如一个200*200的实际像素的图片,我们scale是2的情况下,图片控件占据的大小还是200*200。但是图像绘制的大小是100*100。这样就剩下了很多空白空间。repeat属性就定义了怎么填充空白的空间。
repeat, x、y轴填充
repeatX, 只在x轴填充
repeatY, 只在y轴填充
noRepeat, 不填充
除了使用重复来填满空间之外,还可以通过拉伸来填满剩余空间,就是fit属性。效果如下:
占位组件FadeInImage
Image组件提供了最基本你的图像展示,FadeInImage组件提供了加载中显示占位图的功能。
| 属性 | 类型 | 作用 |
|---|---|---|
| key | Key? | 组件的Key |
| image | ImageProvider | 图片组件显示的内容,网络、文件、Asset、内存等 |
| placeholder | ImageProvide | 占位的图片提供器 |
| placeholderErrorBuilder | ImageErrorWidgetBuilder? | 占位图加载错误的 |
| imageErrorBuilder | ImageErrorWidgetBuilder? | 图片加载失败展示的Widget |
| bundle | AssetBundle? | 图片的来源 |
| placeholderScale | double? | 占位图片的缩放因子 |
| imageScale | double? | 图片的缩放因子 |
| excludeFromSemantics | bool | 是否排除辅助语义 |
| imageSemanticLabel | String? | 空间的辅助语义 |
| fadeOutDuration | Duration | 占位图片的淡出时长 |
| fadeOutCurve | Curve | 占位图片的淡出的淡出效果 |
| fadeInDuration | Duration | 待展示的图片的淡入时长 |
| fadeInCurve | Curve | 待展示的图片的淡入效果 |
| width | double? | 图片的宽度 |
| height | double? | 图片的高度 |
| fit | BoxFit? | 图片的拉伸效果 |
| alignment | AlignmentGeometry | 图片的对齐方式 |
| repeat | ImageRepeat | 图片的重复方式 |
| matchTextDirection | bool | 是否和文字方向相一致 |
| placeholderCacheWidth | int? | 用于展位图编解码的宽度 |
| placeholderCacheHeight | int? | 用于展位图编解码的高度 |
| imageCacheWidth | int? | 用于图片编解码的宽度 |
| imageCacheHeight | int? | 用于图片编解码的高度 |
上面的大多数属性其实和Image是相似的,多的大多数和占位图相关,从placeholder可以看出来,展位图可以来自于任何地方:网络、文件等等。
和Image相似FadeInImage也提供了命名构造来帮助我们不同来源快速实现组件的方法。
FadeInImage.assetNetwork 占位图来自于asset目录,图片是网络图。
FadeInImage.memoryNetwork 占位图来自于内存,图片是网络图。
最简单的使用效果如下:
我们可以使用参数中的builder来实现更多的效果,比如展位图是错误的时候,展示一段文本。我们把占位图的路径写错。
同理imageErrorBuilder就可以实现待加载的图片,加载出错时显示的Widget了。我们把网络路径写错。
总结
从Widget的总览中,看到Widget大概三四百个🙄,我们常用的基本就五大类:图文组件、容器组件、布局组件、滚动组件和功能组件。介绍了图文组件的一些基本用法。鉴于篇幅后面在依次介绍剩下类型组件的基本用法。