是否可以在Wrap小部件中强制对齐特定的子窗口小部件?

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

我正在尝试创建一个由一个带有两个子窗口小部件的行组成的布局,第一个对齐到左边,第二个对齐到右边,如果容器太窄,它也将包装小部件。

这类似于Flutter align two items on extremes - one on the left and one on the right这里提出的问题,可以用Wrapalignment: WrapAlignment.spaceBetween小部件解决。但是,当窗口小部件使用此方法换行时,包装到新运行的正确窗口小部件不再右对齐。 (screenshots

我想要发生的是正确的小部件在它包装时保持与右边对齐。在使用flexbox的css中,这可以通过flex-grow:1或margin-left:auto在右侧小部件上实现,如此codepen中所示(调整页面宽度以查看我想要发生的布局)。

到目前为止,我已经尝试过:

  • Flexible小部件中包装正确的小部件以尝试使其在包装时占用剩余宽度,因此我可以在其中对齐,但这会引发错误:
   Incorrect use of ParentDataWidget.
   Flexible widgets must be placed inside Flex widgets.
  • 将正确的小部件包装在Align小部件中,但是这总是会扩展到整个宽度并导致包装,即使屏幕足够宽,两个小部件都在同一行上
  • 使用带有CustomMultiChildLayoutMultiChildLayoutDelegate可以创建正确的布局(screenshot),但是根据子窗口小部件高度设置窗口小部件的高度似乎不可能,这会强制您使用任意高度值。 docs for MultiChildLayoutDelegate

覆盖getSize以控制布局的整体大小。布局的大小不能取决于子项的布局属性。

那么是否有可能创建一个布局,其中右侧窗口小部件在必须包装时保持与右边缘对齐?

flutter flutter-layout
1个回答
0
投票

感谢RémiRousselet建议使用自定义RenderBox这里是一个解决布局问题的基本实现(基于Wrap小部件RenderBox实现1 2

import 'package:flutter/widgets.dart';
import 'package:flutter/rendering.dart';
import 'dart:math' as math;

class LeftRightAlign extends MultiChildRenderObjectWidget {
  LeftRightAlign({
    Key key,
    @required Widget left,
    @required Widget right,
  }) : super(key: key, children: [left, right]);

  @override
  RenderLeftRightAlign createRenderObject(BuildContext context) {
    return RenderLeftRightAlign();
  }
}

class LeftRightAlignParentData extends ContainerBoxParentData<RenderBox> {}

class RenderLeftRightAlign extends RenderBox
    with
        ContainerRenderObjectMixin<RenderBox, LeftRightAlignParentData>,
        RenderBoxContainerDefaultsMixin<RenderBox, LeftRightAlignParentData> {

  RenderLeftRightAlign({
    List<RenderBox> children,
  }) {
    addAll(children);
  }

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

  @override
  void performLayout() {
    final BoxConstraints childConstraints = constraints.loosen();

    final RenderBox leftChild = firstChild;
    final RenderBox rightChild = lastChild;

    leftChild.layout(childConstraints, parentUsesSize: true);
    rightChild.layout(childConstraints, parentUsesSize: true);

    final LeftRightAlignParentData leftParentData = leftChild.parentData;
    final LeftRightAlignParentData rightParentData = rightChild.parentData;

    final bool wrapped =
        leftChild.size.width + rightChild.size.width > constraints.maxWidth;

    leftParentData.offset = Offset.zero;
    rightParentData.offset = Offset(
        constraints.maxWidth - rightChild.size.width,
        wrapped ? leftChild.size.height : 0);

    size = Size(
        constraints.maxWidth,
        wrapped
            ? leftChild.size.height + rightChild.size.height
            : math.max(leftChild.size.height, rightChild.size.height));
  }

  @override
  bool hitTestChildren(HitTestResult result, {Offset position}) {
    return defaultHitTestChildren(result, position: position);
  }

  @override
  void paint(PaintingContext context, Offset offset) {
    defaultPaint(context, offset);
  }
}

...

class HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      navigationBar: CupertinoNavigationBar(middle: Text('App')),
      child: ListView(children: <Widget>[
        Container(
          margin: EdgeInsets.symmetric(horizontal: 32, vertical: 16),
          child: LeftRightAlign(
            left: Text(
              'Left Widget',
              style: TextStyle(fontSize: 40),
            ),
            right: Text(
              'Right Widget',
              style: TextStyle(fontSize: 40),
            ),
          ),
        ),
        Text('Next Line'),
      ])
    );
  }
}

© www.soinside.com 2019 - 2024. All rights reserved.