Flutter常用组件——图文组件

2,520 阅读11分钟

Flutter的设计思路是一切皆是 “Widget” 。Widget就是我们开发所使用的组件,开发者使用它来完成页面开发、UI显示、动作交互等等。

Widget总览

Flutter的Widget可以分为三大类:组合型、渲染型、代理型
组合型就是我们常见的StatelessWidget、StatefulWidget。渲染型大多数是系统提供的直接使用的组件,比如Padding、RichText等等。代理型就是为子节点提供代理功能,比如InheritedWidget等等。

Widget总览

StatelessWidget

StatelessWidget是无状态组件,2.2版本的SDK系统为我们提供了大约109的该类型组件。如下图:

StatelessWidget1 StatelessWidget2 StatelessWidget2 StatelessWidget2

图很大,大家可以点开查看,我们常用的组件有Container、ListView、Visibility、SafeArea等等。

StatefulWidget

StatefulWidget是状态组件,2.2版本的SDK为我们提供了170+的该类型组件,如下图:

StatefulWidget3 StatefulWidget2 StatefulWidget4 StatefulWidget1

图很长,大家可以点开查看。我们常用的该类型的组件有:回退拦截的WillPopScope、输入框的TextField、浮窗组件Overlay、页面容器Scaffold等等

RenderObjectWidget

RenderObjectWidget是渲染类型的组件,提供了一个用于渲染的对象。

RenderObjectWidget3 RenderObjectWidget4 RenderObjectWidget1 RenderObjectWidget2

我们常用的该类型的组件有:弹性布局的Row、Column,处理间距的Padding,布局约束的SizedBox,Sliver家族的List等等。

ProxyWidget

ProxyWidget是代理组件,并不参与绘制。 相对来说,Flutter中这个类型的Widget要少很多。

ProxyWidget2 ProxyWidget1

我们常见的该类型的组件大多数和主题、配置等相关,比如尺寸相关的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. 显示单一样式的文本组件。

属性类型作用
dataString必填,待显示的文本内容。
keyKey?组件的key
styleTextStyle?文本显示的样式,字体颜色、文字背景色和前景色、文字大小、字体的权重等等,和文字显示相关的一切属性都在Style中。
strutStyleStrutStyle?和文字的基线相关
textAlignTextAlign?文字的对齐方式
textDirectionTextDirection?文字的方向,是从左到右还是从右到左
localeLocale?语言和格式首选项的标识符。
softWrapbool?文字是否在换行时折断
overflowTextOverflow?文字溢出时的处理效果
textScaleFactordouble?文字的缩放因子
maxLinesint?文字的最大行数
semanticsLabelString?文字的语义标签(辅助功能)
textWidthBasisTextWidthBasis?文字的测量方法
textHeightBehaviorTextHeightBehavior?处理文字上下的高度

下面我们看实际的小栗子

class _TextTestPageState extends State<TextTestPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Text组件'),
      ),
      body: Center(child: Text('Text文本组件演示')),
    );
  }
}

屏幕中间就是最简单的文本组件,运行效果如下:

ProxyWidget1

修改文字的颜色、字体等只需要增加TextStyle属性

textStyle.gif

除了简单的字号、权重和颜色之外。TextStyle还支持修改文字的间距、文字的背景色以及文字的装饰等等。

letterSpacing属性是单个字母或者汉字之间的间距,wordSpacing是单词之间的间距,单词是空格隔开的两部分。

letterspace.gif

从上图可以明显看出来,字符之间有了间距。

wordspace.gif

从上图可以看到 单词之间的间隙变大了。

除了简单的backgroundColor之外,还可以使用画笔Paint类型的background,来作为文字的背景。

background.gif

不同与backgroundColor让整个文字的背景设置为颜色,使用Paint可以支持边框、抗锯齿等效果,上面就是设置了黑色的边框。🧐注意的是由于中英文的差异,英文字母的整体行高要矮一些。

除了上面的文字样式的修改,Text还有一些处理 文本组件的修改。

比如maxLine是最大的行数,overflow处理文字的溢出效果。

enum TextOverflow {
  /// 直接截断
  clip,

  /// 文字渐隐
  fade,

  /// 结尾...展示
  ellipsis,

  /// 超出部分依然渲染 虽然看不见
  visible,
}
行数.gif

这就是行数和溢出效果的控制。这里注意一下,TextOverflow.fade需要配合softWrap属性使用,softWrap设置为false的时候,才可以渐隐消失。缺陷就是 只能显示 一行

以上就是纯文本组件常用的一些属性。

富文本RichText

Text是简单的文本组件,富文本可以通过RichText来实现。

属性类型作用
textInlineSpan必填,待显示的富文本内容
keyKey?组件的key
strutStyleStrutStyle?和文字的基线相关
textAlignTextAlign?文字的对齐方式
textDirectionTextDirection?文字的方向,是从左到右还是从右到左
localeLocale?语言和格式首选项的标识符。
softWrapbool?文字是否在换行时折断
overflowTextOverflow?文字溢出时的处理效果
textScaleFactordouble?文字的缩放因子
maxLinesint?文字的最大行数
textWidthBasisTextWidthBasis?文字的测量方法
textHeightBehaviorTextHeightBehavior?处理文字上下的高度

除了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就已经是终点了。

富文本.gif

上述的代码如下:

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。

属性类型作用
keyKey?组件的Key
imageImageProvider图片组件显示的内容,网络、文件、Asset、内存等
frameBuilderImageFrameBuilder?构造出代表Image的Widget, 一般使用这个属性来添加一个图片的效果比如渐入 或者 占位等等
loadingBuilderImageLoadingBuilder?图片的夹在builder,显示加载过程中的Widget
errorBuilderImageErrorWidgetBuilder?图片加载失败展示的Widget
semanticLabelString?图片控件的辅助语义
excludeFromSemanticsbool是否排除辅助语义
widthdouble?图片空间的宽度
heightdouble?图片空间的高度
colorColor?图片的颜色
colorBlendModeBlendMode?图像的混合模式juejin.cn/post/684490…
fitBoxFit?图像的缩放、自适应等参数
alignmentAlignmentGeometry图像在控件内部的对齐方式
repeatImageRepeat图像在控件内部的重复方式
centerSliceRect?.9图片的区域
matchTextDirectionbool是否沿着TextDirection进行绘制
gaplessPlaybackbool是否展示旧图片
isAntiAliasbool是否抗锯齿来绘制图像。
filterQualityFilterQuality图像的质量

上面就是图片的基本属性。我们一般显示图片的时候,使用指定的命名构造

[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',
      ),
    );
  }
}

展示效果如下:

image.png

我们可以通过height和width来设置宽高

Image宽高组件.gif

通过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,那么图片还不可被绘制

fadecode fadeImage

通过loadingBuilder可以实现加载中的效果,loadingBuilder的含义如下:

加载过程中展示的Widget,如果设置为null,那么图片就会递增的加载,用户也不会收到加载的过程。

方法的签名如下:

typedef ImageLoadingBuilder = Widget Function(
  BuildContext context,
  Widget child,
  ImageChunkEvent? loadingProgress,
);

context:就是上下文
child:是图片组件,并且保证是非空的。如果frameBuilder非空的话,会将结果传递进来
loadingProgress: 代表当前的加载阶段,cumulativeBytesLoaded是已经加载的字节数
expectedTotalBytes是总字节数

loadbuilder loadImage

我们可以通过color来设置图片的颜色,这里的设置规则是 将图片中的有颜色的像素 都设置为设置的颜色

有了这个属性,我们就可以给同一个Icon设置不同的渠道颜色。

loadImage

图片也可以进行缩放和重复,就是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, 不填充

repeat.gif

除了使用重复来填满空间之外,还可以通过拉伸来填满剩余空间,就是fit属性。效果如下:

box_fit_contain.pngcontain:尽可能大伸展到边缘box_fit_cover.pngcover:尽可能小,超出部分会裁剪box_fit_fill.pngfill:按比例拉伸
box_fit_fitHeight.pngfitHeight:保持原图比例,拉伸高度box_fit_fitWidth.pngfitWidth:保持原图比例,拉伸宽度box_fit_none.pngnone:正常展示
box_fit_scaleDown.pngscaleDown:居中展示,大的话就按比例缩小

占位组件FadeInImage

Image组件提供了最基本你的图像展示,FadeInImage组件提供了加载中显示占位图的功能。

属性类型作用
keyKey?组件的Key
imageImageProvider图片组件显示的内容,网络、文件、Asset、内存等
placeholderImageProvide占位的图片提供器
placeholderErrorBuilderImageErrorWidgetBuilder?占位图加载错误的
imageErrorBuilderImageErrorWidgetBuilder?图片加载失败展示的Widget
bundleAssetBundle?图片的来源
placeholderScaledouble?占位图片的缩放因子
imageScaledouble?图片的缩放因子
excludeFromSemanticsbool是否排除辅助语义
imageSemanticLabelString?空间的辅助语义
fadeOutDurationDuration占位图片的淡出时长
fadeOutCurveCurve占位图片的淡出的淡出效果
fadeInDurationDuration待展示的图片的淡入时长
fadeInCurveCurve待展示的图片的淡入效果
widthdouble?图片的宽度
heightdouble?图片的高度
fitBoxFit?图片的拉伸效果
alignmentAlignmentGeometry图片的对齐方式
repeatImageRepeat图片的重复方式
matchTextDirectionbool是否和文字方向相一致
placeholderCacheWidthint?用于展位图编解码的宽度
placeholderCacheHeightint?用于展位图编解码的高度
imageCacheWidthint?用于图片编解码的宽度
imageCacheHeightint?用于图片编解码的高度

上面的大多数属性其实和Image是相似的,多的大多数和占位图相关,从placeholder可以看出来,展位图可以来自于任何地方:网络、文件等等。

和Image相似FadeInImage也提供了命名构造来帮助我们不同来源快速实现组件的方法。

FadeInImage.assetNetwork 占位图来自于asset目录,图片是网络图。

FadeInImage.memoryNetwork 占位图来自于内存,图片是网络图。

最简单的使用效果如下:

repeat.gif

我们可以使用参数中的builder来实现更多的效果,比如展位图是错误的时候,展示一段文本。我们把占位图的路径写错。

fadeinerror.gif

同理imageErrorBuilder就可以实现待加载的图片,加载出错时显示的Widget了。我们把网络路径写错。

fadeinerror.gif
其他的属性和Image控件就差不多一样了,就不详细列举。

总结

从Widget的总览中,看到Widget大概三四百个🙄,我们常用的基本就五大类:图文组件、容器组件、布局组件、滚动组件和功能组件。介绍了图文组件的一些基本用法。鉴于篇幅后面在依次介绍剩下类型组件的基本用法。