Mackit 新品

Mackit — macOS 开发者缓存清理工具:扫描 Xcode / Node / Flutter 等可再生缓存,逐项标示风险,把磁盘空间拿回来

了解详情
温馨提示 2024年 6月我们已经停止开发者板块文章内容更新,谢谢来访。 存档数据
前端

使用Gap为 Flutter 添加间隙

最近,我希望Row或Column小部件有一个spacing属性来配置子小部件之间的间隙。

因为我发现向这些子项添加填充太麻烦了 - 既因为这进一步缩进了小部件树中的小部件,又因为很难决定是否应该仅添加底部填充,或仅添加顶部填充,或同时添加填充子部件的顶部和底部——我最常使用SizedBox部件来引入间隙。还有另一个不便之处:我必须根据是否将间隙添加到or来显式设置widthor属性,并且要使用常量,我必须引入and或 类似的东西。heightRowColumnsmallVerticalGapsmallHorizontalGap

我们可以创建一个Gap自动执行正确操作的小部件吗?

我们可以!

差距

这是我的实现。

因为它的布局取决于小部件direction的Flex子级(Spacer小部件也有同样的问题),所以仅使用小部件无法实现这一点。我需要一个特殊的渲染对象来执行特殊的布局。因此,Gap应是以下子类SingleChildRenderObjectWidget:

class Gap extends SingleChildRenderObjectWidget {
  const Gap({Key key, this.size = 16})
    : assert(size != null),
      super(key: key);  final double size;  …

Gap现在需要实现一个createRenderObject创建渲染对象的方法。我将RenderGap按照 Flutter 的命名约定将此对象称为。

 ……  @override 
  RenderGap createRenderObject(BuildContext context) { 
    return RenderGap(size); 
  }  ……

由于Gap有一个必须传递的属性,我还实现了updateRenderObject确保对该属性的更改也会影响我的渲染对象:

  ……  @override 
  void updateRenderObject(BuildContext context, RenderGap renderObject) { 
    renderObject.gapSize = size; 
  } 
}

它RenderGap作为子类实现RenderBox,提供了所需的大部分功能。因为渲染对象(与小部件相反)是可变的,所以我确保对属性的更改gapSize会触发重新布局,但前提是尺寸确实发生变化。

class RenderGap extends RenderBox {
  RenderGap(this._gapSize);  double _gapSize;
  double get gapSize => _gapSize;
  set gapSize(double gapSize) {
    if (_gapSize != gapSize) {
      _gapSize = gapSize;
      markNeedsLayout();
    }
  }  …

我们已经完成了样板代码。

为了实现布局,我们必须重写performLayout必须设置对象size属性的方法。文档说它也应该尊重对象的constraints,但现在,我忽略它们,因为我有点着急。希望我的假设是正确的,performLayout只有在对象被添加到渲染树后才会被调用,因此总是有一个parent.

我根据其父级的布局方向分配大小,如下所示:

  …  @override
  void performLayout() {
    final parent = this.parent;
    if (parent is RenderFlex) {
      if (parent.direction == Axis.horizontal) {
        size = Size(_gapSize, 0);
      } else {
        size = Size(0, _gapSize);
      }
    } else {
      throw FlutterError(‘Gap must be used inside a Flex widget’);
    }
  }
}

现在,可以向列或行添加间隙:

Column( 
  Children: [ 
    Text('这是一条消息'), 
    Gap.large, 
    Row( 
      Children: [ 
        Spacer(), 
        RaishedButton( 
          onPressed: () => null, 
          child: Text('取消'), 
        ), 
        Gap .small, 
        RaisingButton( 
          onPressed: () => null, 
          child: Text('OK'), 
        ), 
      ], 
    ), 
  ] 
);

为了完整的实现,还必须重写这四种compute{Min,Max}Intrinsic{Width,Height}方法。gapSize如果存在带有正确“ ` 的parent类,则它们应该返回而不是 0 。RenderFlexdirection