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

Posted

技术标签:

【中文标题】是否可以强制对齐 Wrap 小部件中的特定子小部件?【英文标题】:Is it possible to force the alignment of a specific child widget in a Wrap widget? 【发布时间】:2019-08-01 05:49:51 【问题描述】:

我正在尝试在 Flutter 中创建一个布局,该布局由具有两个子小部件的行组成,第一个向左对齐,第二个向右对齐,如果容器太窄,它也会包裹小部件。

这类似于Flutter align two items on extremes - one on the left and one on the right 此处提出的问题,可以使用带有alignment: WrapAlignment.spaceBetweenWrap 小部件来解决。但是,当使用此方法包装小部件时,包装到新运行的右侧小部件不再右对齐。 (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 以控制布局的整体大小。布局的大小不能依赖于子级的布局属性。

那么是否可以创建一个布局,使右侧小部件在必须换行时保持与右边缘对齐?

【问题讨论】:

如果CustomMultiChildLayout 部分解决了您的问题,您可能需要创建一个自定义RenderBox,它允许更高级的高度控制。 【参考方案1】:

感谢 Rémi Rousselet 的建议,使用自定义 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'),
      ])
    );
  


【讨论】:

以上是关于是否可以强制对齐 Wrap 小部件中的特定子小部件?的主要内容,如果未能解决你的问题,请参考以下文章

Flutter Wrap 小部件对齐:在 ExpansionPanel 内部

PySide QScrollArea 以编程方式滚动到特定的子小部件

遍历框架中的所有子小部件并检查小部件的类型

如何从 Flutter 中的子小部件调用父小部件功能

创建一个函数来放置子小部件

隐藏子小部件时,QGridLayout 未调整大小或重新绘制