likes
comments
collection
share

Flutter-Container详解

作者站长头像
站长
· 阅读数 14

概述

Container是一个拥有绘制、定位、调整大小的 widget,是开发中最常用、最基础的组件。虽然最基础但不可小觑,熟悉每一个属性可以帮助我们更好更快的实现想要的效果,避免走弯路,也能避免代码冗余。本文主要针对其属性进行讲解。

属性

  Container({
    Key? key,
    this.alignment,
    this.padding,
    this.color,
    this.decoration,
    this.foregroundDecoration,
    double? width,
    double? height,
    BoxConstraints? constraints,
    this.margin,
    this.transform,
    this.transformAlignment,
    this.child,
    this.clipBehavior = Clip.none,
  })

key

key用于控制控件如何取代树中的另一个控件,即若widget指定了相同的key,则这些widget可以复用。 如果widget的key值不为空,会判断key._currentElement值所指向的widget,和当前widget的类型key都相同,那么就从旧的父节点上移除,作为当前的节点的子widget之一, 否则将进行真实的创建新的Element。若开发中对组建的使用没有较高要求,一般不设置该属性。

alignment

alignment可以理解为Container内容的锚点位置或重力方向,锚点在哪,内容就从哪里开始。alignment的类型为AlignmentGeometry类型,通常我们会使用其实现类Alignment来进行设置。AlignmentGeometry属于抽象类:

@immutable
abstract class AlignmentGeometry {...}

一般不直接使用AlignmentGeometry ,而是使用其实现类。Flutter中实现或继承了AlignmentGeometry的公共可直接调用的类有两个:Alignment,AlignmentDirectional。这两个实现类的使用方法和相似,可以直接调用其内部属性:

  static const Alignment topLeft = Alignment(-1.0, -1.0);
  static const Alignment topCenter = Alignment(0.0, -1.0);
  static const Alignment topRight = Alignment(1.0, -1.0);
  static const Alignment centerLeft = Alignment(-1.0, 0.0);
  static const Alignment center = Alignment(0.0, 0.0);
  static const Alignment centerRight = Alignment(1.0, 0.0);
  static const Alignment bottomLeft = Alignment(-1.0, 1.0);
  static const Alignment bottomCenter = Alignment(0.0, 1.0);
  static const Alignment bottomRight = Alignment(1.0, 1.0);

使用的时候直接用Alignment.topLeft调用即可,也可直接设置参数数值比如Alignment(-1.0, -1.0)即可。根据设置不同的alignment属性值,视图效果也是不一样的: Flutter-Container详解 若在其他组件中需要设置TextDirection,可以考虑使用AlignmentDirectional。AlignmentDirectional和Alignment差不多:

  static const AlignmentDirectional topStart = AlignmentDirectional(-1.0, -1.0);
  static const AlignmentDirectional topCenter = AlignmentDirectional(0.0, -1.0);
  static const AlignmentDirectional topEnd = AlignmentDirectional(1.0, -1.0);
  static const AlignmentDirectional centerStart = AlignmentDirectional(-1.0, 0.0);
  static const AlignmentDirectional center = AlignmentDirectional(0.0, 0.0);
  static const AlignmentDirectional centerEnd = AlignmentDirectional(1.0, 0.0);
  static const AlignmentDirectional bottomStart = AlignmentDirectional(-1.0, 1.0);
  static const AlignmentDirectional bottomCenter = AlignmentDirectional(0.0, 1.0);
  static const AlignmentDirectional bottomEnd = AlignmentDirectional(1.0, 1.0);

使用方法和Alignment一样,不再叙述。

padding、margin

padding为内边距,margin为外边距。 Flutter-Container详解

针对于上图中Container2,Container1与Container2之间的边框距离称之为margin,Container2与内容之间距离为padding。通常margin和padding使用EdgeInsets,EdgeInsets使用方法如下:

方法使用
EdgeInsets.fromLTRB(this.left, this.top, this.right, this.bottom)左上右下依次填写
EdgeInsets.all(double value)所有边距一样
EdgeInsets.only({this.left = 0.0,this.top = 0.0,this.right = 0.0,this.bottom = 0.0, })左上右下可选择设置
EdgeInsets zero左上右下都为0
EdgeInsets.fromWindowPadding(ui.WindowPadding padding, double devicePixelRatio)左上右下距离窗口的边距,和设备像素比,而不是距离父组件的边距

EdgeInsets继承于EdgeInsetsGeometry,和之类似的还有EdgeInsetsDirectional,同样继承自EdgeInsetsGeometry,暴露出的方法有:

class EdgeInsetsDirectional extends EdgeInsetsGeometry {
  const EdgeInsetsDirectional.fromSTEB(this.start, this.top, this.end, this.bottom);
  const EdgeInsetsDirectional.only({
    this.start = 0.0,
    this.top = 0.0,
    this.end = 0.0,
    this.bottom = 0.0,
  });
  static const EdgeInsetsDirectional zero = EdgeInsetsDirectional.only();
  ...
}

EdgeInsetsDirectional使用方法和EdgeInsets类似,多用于TextDirection。

color

color为Container颜色,设置颜色通常可以调用Colors.white,Colors.red等Flutter定义好的颜色,如没有适合的颜色,可以使用Color(0xFFFFFFFF),自定义颜色,0x代表16进制,前面两个FF代表透明度(Android中可以不写,但Flutter中不可省略),后面6个F代表颜色数值。以Color(0xFFFFFFFF)为例,以下表格对Color的使用进行说明

方法含义
Color(0xFFFFFFFF).value获取颜色数值(0-255)
Color(0xFFFFFFFF).red获取颜色中红色
Color(0xFFFFFFFF).blue获取颜色中蓝色
Color(0xFFFFFFFF).opacity获取颜色不透明度(0.0-1.0)
Color(0xFFFFFFFF).alpha获取颜色透明度
Color(0xFFFFFFFF).green获取颜色中绿色
Color(0xFFFFFFFF).withOpacity(opacity)设置颜色不透明度
Color(0xFFFFFFFF).computeLuminance()计算颜色亮度(0-1)
Color(0xFFFFFFFF).withAlpha(a)设置颜色透明度 (0-255)
Color(0xFFFFFFFF).withBlue(b)设置颜色中蓝色值(0-255)
Color(0xFFFFFFFF).withGreen(g)设置颜色中绿值
Color(0xFFFFFFFF).withRed(r)设置颜色中红色值

关于Color大多有这几个常用的方法,若Container设置了decoration,Container的color就不要设置了,两者冲突会报错,以decoration中的color为准。

decoration,foregroundDecoration

decoration为背景装饰,foregroundDecoration为前景装饰。简单理解就是设置样式,不仅仅是设置颜色,还包括形状、图片、渐变、阴影、模糊等。 decoration指定类型为Decoration,同样Decoration为抽象类,没有具体的实现,需要使用其实现类或子类,其实现类主要有BoxDecoration、FlutterLogoDecoration、UnderlineTabIndicator、ShapeDecoration。Container中通常使用BoxDecoration,BoxDecoration有如下几个参数:

  const BoxDecoration({
    this.color,
    this.image,
    this.border,
    this.borderRadius,
    this.boxShadow,
    this.gradient,
    this.backgroundBlendMode,
    this.shape = BoxShape.rectangle,
  })
color

颜色直接在BoxDecoration中设置即可,以下分别是红色和蓝色的效果图 Flutter-Container详解

image

image就是设置装饰图片,图片分为资源图片、本地图片和网络图片,这里只能使用DecorationImage

 const DecorationImage({
    required this.image,
    this.onError,
    this.colorFilter,
    this.fit,
    this.alignment = Alignment.center,
    this.centerSlice,
    this.repeat = ImageRepeat.noRepeat,
    this.matchTextDirection = false,
    this.scale = 1.0
  })
  • image DecorationImage中指定了image的类型必须是ImageProvider,也就是这里使用的是AssetImage()、NetworkImage()、FileImage(),而不是Image.asset()、Image.net()、Image.file()等。 Flutter-Container详解 左图为AssetImage,右图为NetworkImage,FileImage()访问的是手机中的图片,和前两者一样,只要拿到路径都不是问题。
  • onError onError指的是图片错误监听,万一图片格式不对,大小不对,网络出错,加载出错,开发者需要知道错在哪里了,然后做错误处理。
  • colorFilter colorFilter就是相当于给图片加上一层滤镜,以上图中左边图片为例,ColorFilter使用方式有四种:
方法含义
ColorFilter.mode(Color color, BlendMode blendMode)添加指定混合模式指定颜色的滤镜,BlendMode 大概有三十种,用户自由选择
ColorFilter.matrix(List matrix)矩阵混合
ColorFilter.linearToSrgbGamma()将sRGB伽马曲线应用于RGB通道
ColorFilter.srgbToLinearGamma()将sRGB伽马曲线逆应用于RGB通道

效果: Flutter-Container详解 通过上图可以发现,同一图片使用不同的滤镜,效果大不相同。如果需要开发图片滤镜功能,这一块会有大用处。

  • fit fit指的是图片适配模式,使用BoxFit即可,BoxFit也提供了几种模式供选择。
模式含义
fill根据图片比例填充
contain容器范围内尽可能最大
cover覆盖整个容器
fitWidth宽度适应
fitHeight高度适应
none
scaleDown等比例缩放

各种模式依次效果如下: Flutter-Container详解 上图中图片较小,容器固定,有些图片看不出区别,而实际使用过程中不同模式之间差别较大,以实际为准。

  • alignment alignment 同文章开头的alignment一样,不再叙述。

  • centerSlice centerSlice和fit效果有些相似,当两者同时使用的时候,可能没有效果,可能无法看到图片,使用时需要谨慎一些。 centerSlice类型为Rect,centerSlice用于nine-patch image,即可拉伸图片,后续这一块需要仔细研究一下。

方法
Rect.fromLTRB(this.left, this.top, this.right, this.bottom)
Rect.fromLTWH(double left, double top, double width, double height)
Rect.fromCircle({ required Offset center, required double radius })
Rect.fromCenter({ required Offset center, required double width, required double height })
Rect.fromPoints(Offset a, Offset b)
  • repeat repeat 是空白区域图片重复模式。
方法含义
ImageRepeat.repeat全部填充图片
ImageRepeat.repeatX水平重复
ImageRepeat.repeatY竖直重复
ImageRepeat.noRepeat不重复

对于的效果依次如下: Flutter-Container详解 由于图片本身已经占用了容器的水平位置上的全部空间,所以repeatX和noRepeat效果一样,repeat和repeatY效果一样。若图片水平和竖直方向都有剩余空间,则repeat等于repeatX和repeatY叠加。

  • matchTextDirection matchTextDirection默认为false,表示背景图片和文字方向没有关系。当为true的时候,表示图片和文字方向一致。
  • scale scale表示图片缩放,默认为1.0,表示按原图大小显示。小于1.0,则按比例缩小图片;大于1.0,则表示按比例放大图片。
border

border是边框的意思,设置该属性,就可以设置Contaier边框样式。border指定类型为BoxBorder,BoxBorder,InputBorder,OutlinedBorder虽都继承自ShapeBorder ,但这里BoxBorder也是抽象类,所以需要使用其子类Border或BorderDirectional,这里通常使用Border。Border有四种构造方法:

方法含义
Border({BorderSide top: BorderSide.none, BorderSide right: BorderSide.none, BorderSide bottom: BorderSide.none, BorderSide left: BorderSide.none})可分别设置上下左右边框
Border.all({Color color: const Color(0xFF000000), double width: 1.0, BorderStyle style: BorderStyle.solid})设置所有边框
Border.fromBorderSide(BorderSide side)内边框
Border.symmetric({BorderSide vertical: BorderSide.none, BorderSide horizontal: BorderSide.none})水平方向边框,竖直方向边框

通过上表可以发现,不管使用哪一种方法都要使用BorderSide,BorderSide指的是具体边框的样式,而Border指的是在哪个方向可以有边框。BorderSide比较简单:

  const BorderSide({
    this.color = const Color(0xFF000000),
    this.width = 1.0,
    this.style = BorderStyle.solid,
  }) 

color代表边框颜色,width代表边框宽度,style表示边框样式(默认实线-BorderStyle.solid)。具体效果如下图: Flutter-Container详解

borderRadius

边角弧度,类型为BorderRadiusGeometry ,通常使用其实现类BorderRadius或BorderRadiusDirectional,其中BorderRadius最常用,以下是BorderRadius的一些用法:

方法含义
BorderRadius.all(Radius radius)所有角的弧度
BorderRadius.circular(double radius)所有角的弧度
BorderRadius.horizontal({Radius left: Radius.zero, Radius right: Radius.zero})所有角的水平方向弧度
BorderRadius.only({Radius topLeft: Radius.zero, Radius topRight: Radius.zero, Radius bottomLeft: Radius.zero, Radius bottomRight: Radius.zero})分别设置四角的弧度
BorderRadius.vertical({Radius top: Radius.zero, Radius bottom: Radius.zero})所有角的垂直方向弧度

和border一样,BorderRadius中除了 BorderRadius.circular外都是指定哪个角设置弧度,具体实现由Radius实现。Radius中Radius.circular(double radius)指的是圆角的弧度一样,Radius.elliptical(double x,double y)表示水平和垂直方向的弧度自由定义。 Flutter-Container详解 总而言之,borderRadius针对于不同的角,不同方向,不同弧度都可以随意设置,比较灵活。

boxShadow

boxShadow设置Container阴影或投影,List boxShadow说明使用时是以数组的形式,对于稍显复杂的场景一层阴影无法达到要求,所有需要很多层阴影相互叠加来满足要求。BoxShadow使用相对简单

const BoxShadow(
{Color color: const Color(0xFF000000),
Offset offset: Offset.zero,
double blurRadius: 0.0,
double spreadRadius: 0.0}
)

color是阴影的颜色,Offset是投影偏移量,blurRadius投影模糊程度,spreadRadius则是投影的扩散程度。 Flutter-Container详解blurRadius取值不同,效果不同,取值越大,阴影的色彩越淡,但是扩散的范围越大。 Flutter-Container详解 spreadRadius取值不同,阴影范围有明显区别。当取值大于0时,阴影向外扩散;当取值小于0时,阴影向内部聚集,若此时需要显示阴影,需要设置Offset,光线角度不同,阴影的投射方向也不同,所以设置内投影的时候,一定要设置Offset,让投影偏移出来,否则投影被遮挡无法显示。

gradient

gradient为设置渐变,渐变类型分为LinearGradient、RadialGradient、SweepGradient,分别为线性渐变、辐射渐变、扫描渐变。

  • LinearGradient(线性渐变) Flutter-Container详解 begin线性渐变起点,end为线性渐变终点,可水平,可竖直,可对角,根据需要自由选择。stops里面数值数量要和colors里面的数量保持一致。tileMode为颜色填充模式:
模式含义
TileMode.clamp夹钳模式,颜色与颜色之间有类似窄而明显的过渡
TileMode.repeated重复
TileMode.mirror镜像
TileMode.decal贴花(探索中)

具体效果如下: Flutter-Container详解 没有对TileMode.decal进行效果展示,是因为关于这个模式的解释比较模糊,也没有观察出到底有什么不同,需后续持续探索。 transform表示渐变色变换,一般有GradientRotation和SweepGradient供选择,主要用于SweepGradient和RadialGradient中。

  • SweepGradient(扫描渐变)
SweepGradient({
    this.center = Alignment.center,
    this.startAngle = 0.0,
    this.endAngle = math.pi * 2,
    required List<Color> colors,
    List<double>? stops,
    this.tileMode = TileMode.clamp,
    GradientTransform? transform,
  })

默认中心的从Container中心开始,开始弧度为0.0,结束弧度为pi*2,也就是一周。运行效果如下: Flutter-Container详解 如果设置好颜色,再搭配上旋转动画,像雷达扫描、大转盘这种效果是可以轻松实现的。

  • RadialGradient(辐射渐变)
    RadialGradient({
    this.center = Alignment.center,
    this.radius = 0.5,
    required List<Color> colors,
    List<Color>? stops,
    this.tileMode = TileMode.clamp,
    this.focal,
    this.focalRadius = 0.0,
    GradientTransform? transform,
    })

效果如下: Flutter-Container详解 第一张图片是没有设置焦点。第二张图片是设置了焦点,焦点中心为Container中心,焦点半径为0.1。第三张图同样设置了焦点,但是焦点的中心为centerLeft,且焦点半径为1.0,焦点半径单位不是像素,focalRadius和focal设置的值不同,效果区别较大,有时和想象中的不太一样,所以使用的时候需仔细调试一下。

backgroundBlendMode

backgroundBlendMode为背景混合模式,和前面讲的图片滤镜差不多,大概有将近30种模式,有些差别较大,有些区别不是很明显,需要开发者多多尝试,这里随机选取了四种,效果如下: Flutter-Container详解

shape

shape即为装饰的形状,默认为BoxShape.rectangle,用户也可以选择BoxShape.circle。BoxShape.circle是整个装饰为圆形,而RadialGradient是辐射状也为圆形,不容易区分到底是哪一个决定的,但LinearGradient区分比较开,如下图所示: Flutter-Container详解 所以shape和RadialGradient、SweepGradient有时可以实现相同的效果,可灵活使用。

width,height

Container需要固定宽高,否则会报错。虽有时没有设置也能正常显示,是因为Container包含的组件的宽高固定了,只要子组件宽高固定,Container宽高也固定了,所以显示正常。

constraints

constraints是Container的约束,主要指定的是宽高上面的约束:

BoxConstraints({
    this.minWidth = 0.0,
    this.maxWidth = double.infinity,
    this.minHeight = 0.0,
    this.maxHeight = double.infinity,
  })

constraints可以指定Container的最大宽高和最小宽高,否则有时超出某些范围页面显示异常。和BoxConstraints一样,同样继承自Constraint还有SliverConstraints,SliverConstraints在Sliver相关组件中使用,这里就不多讲了。

transform

矩阵变化,类型为Matrix4,即四阶矩阵。常用的有以下几种用法:

方法含义
Matrix4(...)16个参数, 平移,旋转,缩放,扭曲等
diagonal3Values缩放
rotationX沿x旋转
rotationY沿y旋转
rotationZ沿z旋转
columns设置新矩阵
compose合并平移,旋转,缩放成新矩阵
copy复制矩阵
identity单位矩阵
inverted矩阵逆运算
outer合并
skew扭曲
skewX(x扭曲
skewYy扭曲
zero零矩阵
fromList数组转矩阵

还有其他方法,这里就不一一列举。基本上所有的变换都是在Matrix4(...)基础上实现的,所以只要弄懂Matrix4(...) ,其他的也不是问题。高级变换是一定需要矩阵,复杂的动画也需要矩阵,基础的是四阶,复杂的有五阶、六阶等等,所以矩阵很重要。以下是几种简单的变换效果: Flutter-Container详解 实际中变换后的图片的大小、方位、角度都有不同,效果无法在上图中完全体现出来。

transformAlignment

变换锚点或者是变换重力方向和上文中的alignment是一样的,这里就不再叙述。

clipBehavior

clipBehavior就是组件内容边缘的切割方式,分为四种:

  • none 不做处理。
  • hardEdge 当内容溢出时,hardEdge切割容器边缘最快,但是精准度欠佳,可能会有一些锯齿存在。
  • antiAlias 抗锯齿,速度要比hardEdge慢一些,但是边缘要平滑一些。
  • antiAliasWithSaveLayer 图层抗锯齿,就是容器中每一个图层都做抗锯齿处理,而antiAlias是在容器的轮廓做抗锯齿,antiAliasWithSaveLayer效果肯定会更好更平滑,但是速度最慢,如果没有明确指明,建议使用antiAlias,这样效果和性能能够达到较好的平衡。

Container

查看Container对于各种属性的处理如下:

 @override
  Widget build(BuildContext context) {
    Widget? current = child;

    if (child == null && (constraints == null || !constraints!.isTight)) {
      current = LimitedBox(
        maxWidth: 0.0,
        maxHeight: 0.0,
        child: ConstrainedBox(constraints: const BoxConstraints.expand()),
      );
    }

    if (alignment != null)
      current = Align(alignment: alignment!, child: current);

    final EdgeInsetsGeometry? effectivePadding = _paddingIncludingDecoration;
    if (effectivePadding != null)
      current = Padding(padding: effectivePadding, child: current);

    if (color != null)
      current = ColoredBox(color: color!, child: current);

    if (clipBehavior != Clip.none) {
      assert(decoration != null);
      current = ClipPath(
        clipper: _DecorationClipper(
          textDirection: Directionality.maybeOf(context),
          decoration: decoration!,
        ),
        clipBehavior: clipBehavior,
        child: current,
      );
    }

    if (decoration != null)
      current = DecoratedBox(decoration: decoration!, child: current);

    if (foregroundDecoration != null) {
      current = DecoratedBox(
        decoration: foregroundDecoration!,
        position: DecorationPosition.foreground,
        child: current,
      );
    }

    if (constraints != null)
      current = ConstrainedBox(constraints: constraints!, child: current);

    if (margin != null)
      current = Padding(padding: margin!, child: current);

    if (transform != null)
      current = Transform(transform: transform!, child: current, alignment: transformAlignment);

    return current!;
  }

Container并非是单元组件不可再次拆分,恰恰相反,Container中多数属性都有关联组件,所以当属性被设置的时候,也是调用了该属性关联的组件,然后在此基础上再依次进行嵌套,最后套成Container,所以Container是由其他组件组成的。 本文是对Container的属性进行单独解析,实际使用时,大多都是各种属性相互配合使用,实现的效果也要比文中呈现的效果要丰富得多。

  • 文中有很多遗漏,错误,不准确的,欢迎补充批评指正。
  • 熟悉基础,可以帮助开发者用简单、少量、高效的代码解决复杂问题。