Flutter - 常用 Widget

基础 Widget

文本:Text Widget

文本是视图系统中的常见控件,用来显示一段特定样式的字符串,就比如 Android 里的 TextView、iOS 中的 UILabel。而在 Flutter 中,文本展示是通过 Text 控件实现的。

Text 支持两种类型的文本展示,一个是默认的展示单一样式的文本 Text,另一个是支持多种混合样式的富文本 Text.rich。

单一样式的文本Text 的初始化,是要传入需要展示的字符串。而这个字符串的具体展示效果,受构造函数中的其他参数控制。这些参数大致可以分为两类:

  • 控制整体文本布局的参数:如文本对齐方式 textAlign、文本排版方向 textDirection,文本显示最大行数 maxLines、文本截断规则 overflow 等等,这些都是构造函数中的参数;
  • 控制文本展示样式的参数:如字体名称 fontFamily、字体大小 fontSize、文本颜色 color、文本阴影 shadows 等等,这些参数被统一封装到了构造函数中的参数 style 中。

混合展示样式与单一样式的关键区别在于分片,即如何把一段字符串分为几个片段来管理,给每个片段单独设置样式。面对这样的需求,在 Android 中,我们使用 SpannableString 来实现;在 iOS 中,我们使用 NSAttributedString 来实现;而在 Flutter 中也有类似的概念,即 TextSpan。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
TextStyle blackStyle = TextStyle(fontWeight: FontWeight.normal, fontSize: 20, color: Colors.black); // 黑色样式

TextStyle redStyle = TextStyle(fontWeight: FontWeight.bold, fontSize: 20, color: Colors.red); // 红色样式

Text.rich(
TextSpan(
children: <TextSpan>[
TextSpan(text:'文本是视图系统中常见的控件,它用来显示一段特定样式的字符串,类似', style: redStyle), // 第 1 个片段,红色样式
TextSpan(text:'Android', style: blackStyle), // 第 1 个片段,黑色样式
TextSpan(text:'中的', style:redStyle), // 第 1 个片段,红色样式
TextSpan(text:'TextView', style: blackStyle) // 第 1 个片段,黑色样式
]),
textAlign: TextAlign.center,
);

图片:Image Widget

Image 控件需要根据图片资源异步加载的情况,决定自身的显示效果,因此是一个 StatefulWidget。图片加载过程由 ImageProvider 触发,而 ImageProvider 表示异步获取图片数据的操作,可以从资源、文件和网络等不同的渠道获取图片。
首先,ImageProvider 根据 _ImageState 中传递的图片配置生成对应的图片缓存 key;然后,去 ImageCache 中查找是否有对应的图片缓存,如果有,则通知 _ImageState 刷新 UI;如果没有,则启动 ImageStream 开始异步加载,加载完毕后,更新缓存;最后,通知 _ImageState 刷新 UI。

ImageProvider

ImageProvider是一个抽象类,主要定义了图片数据获取的接口load(),从不同的数据源获取图片需要实现不同的ImageProvider,如AssetImage是实现了从Asset中加载图片的ImageProvider,而NetworkImage实现了从网络加载图片的ImageProvider。

Image

new Image:从 ImageProvider 获取图片
new Image.asset:加载资源图片
new Image.file:加载本地图片
new Image.network:加载网络图片
new Image.memory:加载 Uint8List 资源图片

  • 从asset中加载图片
  1. 在工程根目录下创建一个 images 目录 ,并将图片 avatar.png 拷贝到该目录。
  2. 在 pubspec.yaml 中的 flutter 部分添加如下内容:
    1
    2
    assets:
    - images/avatar.png
  3. 加载该图片
    1
    2
    3
    4
     Image(
    image: AssetImage("images/avatar.png"),
    width: 100.0
    );
  • 从网络加载图片
1
2
3
4
5
Image(
image: NetworkImage(
"https://oss-materials.ifable.cn/avatar/25bbdcd06c32d477f7fa1c3e4a91b032.jpg"),
width: 100.0,
)
  • Fit

fill:会拉伸填充满显示空间,图片本身长宽比会发生变化,图片会变形。
cover:会按图片的长宽比放大后居中填满显示空间,图片不会变形,超出显示空间部分会被剪裁。
contain:这是图片的默认适应规则,图片会在保证图片本身长宽比不变的情况下缩放以适应当前显示空间,图片不会变形。
fitWidth:图片的宽度会缩放到显示空间的宽度,高度会按比例缩放,然后居中显示,图片不会变形,超出显示空间部分会被剪裁。
fitHeight:图片的高度会缩放到显示空间的高度,宽度会按比例缩放,然后居中显示,图片不会变形,超出显示空间部分会被剪裁。
none:图片没有适应策略,会在显示空间内显示图片,如果图片比显示空间大,则显示空间只会显示图片中间部分。

FadeInImage
FadeInImage 控件提供了图片占位的功能,并且支持在图片加载完成时淡入淡出的视觉效果。此外,由于 Image 支持 gif 格式,我们甚至还可以将一些炫酷的加载动画作为占位图。

图标:Icon Widget(Material Design的字体图标)

按钮:

RaisedButton、FlatButton、OutlineButton
CupertinoButton

容器 Widget

Container

Container是DecoratedBox、ConstrainedBox、Transform、Padding、Align等widget的一个组合widget。所以我们只需通过一个Container可以实现同时需要装饰、变换、限制的场景

1
2
3
4
5
6
7
8
9
10
11
12
13
Container({
this.alignment,
this.padding, //容器内补白,属于decoration的装饰范围
Color color, // 背景色
Decoration decoration, // 背景装饰
Decoration foregroundDecoration, //前景装饰
double width,//容器的宽度
double height, //容器的高度
BoxConstraints constraints, //容器大小的限制条件
this.margin,//容器外补白,不属于decoration的装饰范围
this.transform, //变换
this.child,
})

Padding

ConstrainedBox和SizedBox

ConstrainedBox用于对子widget添加额外的约束
SizedBox用于给子widget指定固定的宽高

DecoratedBox

DecoratedBox可以在其子widget绘制前(或后)绘制一个装饰Decoration(如背景、边框、渐变等)。DecoratedBox定义如下:

1
2
3
4
5
const DecoratedBox({
Decoration decoration,
DecorationPosition position = DecorationPosition.background,
Widget child
})
  • decoration:代表将要绘制的装饰,它类型为Decoration,Decoration是一个抽象类,它定义了一个接口createBoxPainter(),子类的主要职责是需要通过实现它来创建一个画笔,该画笔用于绘制装饰。
  • position:此属性决定在哪里绘制Decoration,它接收DecorationPosition的枚举类型,该枚举类两个值:
    • background:在子widget之后绘制,即背景装饰。
    • foreground:在子widget之上绘制,即前景。

BoxDecoration
通常会直接使用BoxDecoration,它是一个Decoration的子类,实现了常用的装饰元素的绘制。

1
2
3
4
5
6
7
8
9
10
BoxDecoration({
Color color, //颜色
DecorationImage image,//图片
BoxBorder border, //边框
BorderRadiusGeometry borderRadius, //圆角
List<BoxShadow> boxShadow, //阴影,可以指定多个
Gradient gradient, //渐变
BlendMode backgroundBlendMode, //背景混合模式
BoxShape shape = BoxShape.rectangle, //形状
})

Transform

Transform可以在其子Widget绘制时对其应用一个矩阵变换

布局 Widget

线性布局 Row 和 Column

所谓线性布局,即指沿水平或垂直方向排布子Widget。Flutter中通过Row和Column来实现线性布局。

对于线性布局,有主轴和纵轴之分,如果布局是沿水平方向,那么主轴就是指水平方向,而纵轴即垂直方向;如果布局沿垂直方向,那么主轴就是指垂直方向,而纵轴就是水平方向。在线性布局中,有两个定义对齐方式的枚举类MainAxisAlignment和CrossAxisAlignment,分别代表主轴对齐和纵轴对齐。

弹性布局 Flex、Expanded

弹性布局允许子widget按照一定比例来分配父容器空间

Flex
Flex可以沿着水平或垂直方向排列子widget,如果你知道主轴方向,使用Row或Column会方便一些,因为Row和Column都继承自Flex,参数基本相同,所以能使用Flex的地方一定可以使用Row或Column。Flex本身功能是很强大的,它也可以和Expanded配合实现弹性布局。

Expanded
可以按比例“扩伸”Row、Column和Flex子widget所占用的空间。

流式布局 Wrap、Flow

层叠布局 Stack、Positioned

层叠布局和Web中的绝对定位、Android中的Frame布局是相似的,子widget可以根据到父容器四个角的位置来确定本身的位置。绝对定位允许子widget堆叠(按照代码中声明的顺序)。Flutter中使用Stack和Positioned来实现绝对定位,Stack允许子widget堆叠,而Positioned可以给子widget定位(根据Stack的四个角)。

Stack

1
2
3
4
5
6
7
Stack({
this.alignment = AlignmentDirectional.topStart,
this.textDirection,
this.fit = StackFit.loose,
this.overflow = Overflow.clip,
List<Widget> children = const <Widget>[],
})
  • alignment:此参数决定如何去对齐没有定位(没有使用Positioned)或部分定位的子widget。所谓部分定位,在这里特指没有在某一个轴上定位:left、right为横轴,top、bottom为纵轴,只要包含某个轴上的一个定位属性就算在该轴上有定位。
  • textDirection:和Row、Wrap的textDirection功能一样,都用于决定alignment对齐的参考系即:textDirection的值为TextDirection.ltr,则alignment的start代表左,end代表右,即从左往右的顺序;textDirection的值为TextDirection.rtl,则alignment的start代表右,end代表左,即从右往左的顺序。
  • fit:此参数用于决定没有定位的子widget如何去适应Stack的大小。StackFit.loose表示使用子widget的大小,StackFit.expand表示扩伸到Stack的大小。
  • overflow:此属性决定如何显示超出Stack显示空间的子widget,值为Overflow.clip时,超出部分会被剪裁(隐藏),值为Overflow.visible时则不会。

Positioned

1
2
3
4
5
6
7
8
9
10
const Positioned({
Key key,
this.left,
this.top,
this.right,
this.bottom,
this.width,
this.height,
@required Widget child,
})

Left、top 、right、 bottom分别代表离Stack左、上、右、底四边的距离。width和height用于指定定位元素的宽度和高度,注意,此处的width、height 和其它地方的意义稍微有点区别,此处用于配合left、top 、right、 bottom来定位widget,举个例子,在水平方向时,你只能指定left、right、width三个属性中的两个,如指定left和width后,right会自动算出(left+width),如果同时指定三个属性则会报错,垂直方向同理。

可滚动 Widget

SingleChildScrollView

ListView

GridView

CustomScrollView

表单 Widget

Switch、CupertinoSwitch

Checkbox

TextField、CupertinoTextField

Form

FormField

TextFormField

RawKeyboardListener