Flutter IntrinsicHeight 和 IntrinsicWidth 不考虑 Transform.rotate

问题描述 投票:0回答:1

我有一个

Container
和一个孩子,我想轮换。旋转时,我希望
Container
的大小与子项的新边框相匹配。

假设我有以下设置:

  Container(
    margin: EdgeInsets.all(200),
    child: Stack(
      children: [
        IntrinsicHeight(
          child: Container(
            color: Colors.green,
            child: Transform.rotate(
              angle: 45 * 3.14 / 180,
              child: Container(
                color: Colors.amber,
                child: Text("Whatever"),
              )
            ),
          ),
        ),
      ],
    ),
  ),

这给出了以下输出:

enter image description here

我想要的输出是:

enter image description here

以便

Container
与旋转子项的实际尺寸相匹配。我怎样才能实现这个目标?

我认为

IntrinsicHeight
可以解决问题,但它似乎并不关心旋转。我也尝试用
RotationTransition
旋转,但这也不起作用

flutter dart resize transform
1个回答
0
投票

真是一个测验!我认为这是可能的,但我不知道如何使用任何预定义的小部件来实现这一点。相反,我尝试使用自定义

RenderObject
RenderBox
来实现此逻辑。这是我得到的。 简而言之:我们在布局模式期间对内部容器应用转换,并根据转换后的容器的大小计算外部小部件的大小。

enter image description here

首先,不得不说这将包括一些繁重的数学计算,所以要考虑到这一点。

现在,我们需要一些导入来计算旋转(我们将自己完成,因为不幸的是

Transform.rotate
不会为我们提供有关旋转角度的必要信息):

import 'dart:math' as math;
import 'package:vector_math/vector_math_64.dart' show Vector3;

接下来,创建类似容器的小部件,它将采用 one 子级(独占)和旋转。我还添加了颜色参数,但如果需要,您可以添加更多属性。

class FittedRotationContainer extends StatelessWidget {
  const FittedRotationContainer({
    Key? key,
    required this.child,
    required this.transform,
    this.color,
  }) : super(key: key);

  final Widget child;
  final Matrix4 transform;
  final Color? color;

  @override
  Widget build(BuildContext context) {
    return FittedTransformBox(
      transform: transform,
      color: color,
      child: child,
    );
  }
}

现在,事情变得有点沉重了。我们必须实现实际的绘画逻辑。我们将在其中使用

SingleChildRenderObjectWidget
RenderProxyBox
。他们一起工作有点像
StatefulWidget
State

class FittedTransformBox extends SingleChildRenderObjectWidget {
  const FittedTransformBox({
    super.key,
    required this.transform,
    super.child,
    this.color,
  });

  final Matrix4 transform;
  final Color? color;

  @override
  RenderObject createRenderObject(BuildContext context) {
    return _RenderFittedTransformBox(transform: transform, color: color);
  }

  @override
  void updateRenderObject(
      BuildContext context, _RenderFittedTransformBox renderObject) {
    renderObject
      ..transform = transform
      ..color = color;
  }
}

class _RenderFittedTransformBox extends RenderProxyBox {
  _RenderFittedTransformBox({
    required Matrix4 transform,
    Color? color,
  })  : _transform = transform,
        _color = color;

  Matrix4 get transform => _transform;
  Matrix4 _transform;
  set transform(Matrix4 value) {
    if (_transform != value) {
      _transform = value;
      markNeedsLayout();
    }
  }

  Color? get color => _color;
  Color? _color;
  set color(Color? value) {
    if (_color != value) {
      _color = value;
      markNeedsPaint();
    }
  }

  @override
  void setupParentData(RenderObject child) {
    if (child.parentData is! BoxParentData) {
      child.parentData = BoxParentData();
    }
  }

  @override
  void performLayout() {
    if (child != null) {
      child!.layout(constraints.loosen(), parentUsesSize: true);
      final childSize = child!.size;
      final childParentData = child!.parentData as BoxParentData;

      final transformedCorners = [
        _transform.transform3(Vector3(0, 0, 0)),
        _transform.transform3(Vector3(childSize.width, 0, 0)),
        _transform.transform3(Vector3(0, childSize.height, 0)),
        _transform.transform3(Vector3(childSize.width, childSize.height, 0)),
      ];

      double minX = double.infinity, minY = double.infinity;
      double maxX = double.negativeInfinity, maxY = double.negativeInfinity;

      for (final corner in transformedCorners) {
        minX = math.min(minX, corner.x);
        minY = math.min(minY, corner.y);
        maxX = math.max(maxX, corner.x);
        maxY = math.max(maxY, corner.y);
      }

      final fittedSize = Size(maxX - minX, maxY - minY);
      size = constraints.constrain(fittedSize);
      childParentData.offset = Offset(
        (size.width - fittedSize.width) / 2 - minX,
        (size.height - fittedSize.height) / 2 - minY,
      );
    } else {
      size = constraints.smallest;
    }
  }

  @override
  void paint(PaintingContext context, Offset offset) {
    if (_color != null) {
      context.canvas.drawRect(offset & size, Paint()..color = _color!);
    }
    if (child != null) {
      final childParentData = child!.parentData as BoxParentData;
      context.pushTransform(
        needsCompositing,
        offset + childParentData.offset,
        _transform,
        super.paint,
      );
    }
  }
}

这就是您在应用程序中使用它的方式:

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Container(
        margin: EdgeInsets.all(200),
        child: Stack(
          children: [
            Center(
              child: FittedRotationContainer(
                color: Colors.green,
                transform: Matrix4.rotationZ(90 * math.pi / 180),
                child: Container(
                  color: Colors.amber,
                  child: const Padding(
                    padding: EdgeInsets.all(20.0),
                    child: Text("Whatever"),
                  ),
                ),
              ),
            )
          ],
        ),
      ),
    );
  }
© www.soinside.com 2019 - 2024. All rights reserved.