6、Flutter布局原理:自定义布局

上一篇文章我们介绍了Flutter的布局原理的概念知识,主要给大家解释了”向下传递约束,向上传递大小“、“松约束、紧约束”以及Container为什么时而大时而小的问题。还不知道的小伙伴请移步链接: Flutter布局原理 :为什么我的部件不听话

本篇文章我将通过CustomLayout自定义布局,给大家演示布局原理。OK,let`s go.

一、CustomMultiChildLayout基础介绍

CustomMultiChildLayout 允许你自定义多个子部件的布局。可以实现高度定制化的布局,其中每个子部件的位置和大小可以根据自定义算法来确定。

以下是使用 CustomMultiChildLayout 的基础介绍:

  1. CustomMultiChildLayout构造函数

    CustomMultiChildLayout 的构造函数如下:

    CustomMultiChildLayout({
      Key? key,
       LayoutDelegate delegate,
      List<Widget> children = const <Widget>[],
    })
    
    • delegate 参数是一个必需的参数,它接受一个自定义的布局委托对象,该委托对象负责计算子部件的位置和大小。
    • children 参数是一个可选的子部件列表,你可以在其中放置需要进行自定义布局的子部件。
  2. 创建自定义布局委托

    要使用 CustomMultiChildLayout,你需要创建一个继承自 MultiChildLayoutDelegate 的自定义布局委托类。这个委托类必须实现以下方法:

    • performLayout(BuildContext context, BoxConstraints constraints):在这个方法中,你可以计算和指定每个子部件的位置和大小,并将它们传递给 layoutChild 方法来进行布局。这个方法会在布局过程中被调用。

    • getSize(BoxConstraints constraints):在这个方法中,你需要指定CustomMultiChildLayout小部件的大小。通常,你可以根据传递给它的约束来确定大小。

    • shouldRelayout:可选的方法,用于确定是否需要重新布局。你可以根据需要实现此方法。

  3. 使用自定义布局委托

    在创建了自定义布局委托类后,将其实例化,并传递给 CustomMultiChildLayout 小部件的 delegate 参数,如下所示:

    CustomMultiChildLayout(
      delegate: MyCustomLayoutDelegate(), // 使用自定义委托
      children: [
        // 放置子部件
        // ...
      ],
    )
    
  4. 子部件的布局

    在自定义布局委托中,你可以使用 layoutChild 方法来指定每个子部件的位置和大小,如下所示:

    layoutChild(
      'childId', // 子部件的标识符
      BoxConstraints.tight(Size(100.0, 100.0)), // 子部件的约束
    );
    positionChild(
      'childId', // 子部件的标识符
      Offset(50.0, 50.0), // 子部件的位置
    );
    

    上述示例中,layoutChild 用于指定子部件的大小,positionChild 用于指定子部件的位置。

使用 CustomMultiChildLayout 时,你有完全的灵活性来计算和布局子部件,这使得你可以创建各种自定义的布局效果。常见的用例包括创建自定义的流式布局、瀑布流布局和棋盘布局等。

请注意,CustomMultiChildLayout 需要一定的布局知识和编程技巧,因为你需要自己处理子部件的位置和大小计算。但它提供了一种非常灵活的方式来实现复杂的自定义布局需求。

二、举例说明
我们有蓝色和红色两个Widget,要将一个放在父容器中间左边距离10px的位置,另一个放在举例右侧10px的位置。两者中线对齐。
在这里插入图片描述
第一步:创建自定义部件
我们在定义部件的时候,要用LayoutId包裹一下,用作部件的唯一标识(下面会讲),不然Flutter不知道怎样区分是哪个部件。


class CustomLayoutExample extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return CustomMultiChildLayout(
    /// 这里创建代理
      delegate: CustomLayoutDelegate(),
      children: [
      /// 部件的标识,用于识别是哪个部件
        LayoutId(
          id: 'centerLeft',
          child: Container(
            color: Colors.blue,
            width: 100.0,
            height: 100.0,
            child: Center(child: Text('Center Left')),
          ),
        ),
        LayoutId(
          id: 'centerRight',
          child: Container(
            color: Colors.red,
            width: 50.0,
            height: 50.0,
            child: Center(child: Text('Center Right')),
          ),
        ),
      ],
    );
  }
}

第二步:实现代理类
必须实现两个方法:void performLayout(Size size)布局的具体实现写在这个函数里;bool shouldRelayout(MultiChildLayoutDelegateoldDelegate) 用于决策是否要重新布局。代码中直接返回true。请阅读代码中的注释

class CustomLayoutDelegate extends MultiChildLayoutDelegate {
  
  void performLayout(Size size) {
    // 计算第一个子部件的位置和大小
    ///(1)向子部件传递了约束 20 <= width/height <= 100
    // (2)子部件返回宽高
    // 验证“向下传递约束,向上传递大小”
    final leftSize = layoutChild('centerLeft', const BoxConstraints(
      maxWidth: 100,
      minWidth: 20,
      maxHeight: 100,
      minHeight: 20
    ));
    var offsetY = size.height / 2.0 - leftSize.height / 2.0;
    // (3)父部件摆放子部件的位置。
    positionChild('centerLeft', Offset(10.0, offsetY));

    // 计算第二个子部件的位置和大小
    final rightSize = layoutChild('centerRight', const BoxConstraints(
            maxWidth: 100,
            minWidth: 20,
            maxHeight: 100,
            minHeight: 20
        ));
    offsetY = size.height / 2.0 - rightSize.height / 2.0;
    final offsetX = size.width - rightSize.width - 10;
    positionChild('centerRight', Offset(offsetX, offsetY));
  }

  
  bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) {
    return true;
  }
}

三、LayoutId
LayoutId 继承自 SingleChildRenderObjectWidget,这是因为 LayoutId 的主要作用是为子部件分配一个标识符(ID),以便在自定义布局委托中确定子部件的位置和大小。为了在布局过程中传递标识符,LayoutId 中包含了 parentData 属性,用于传递额外的布局信息。通常用于指定子部件在父部件中的布局约束、位置等信息。
如果你了解Stack & Posioned部件,Postioned部件也包含parentData属性,也是由于这个原因。
父部件的size有了,自己的size也有了,具体布局在哪里通过x,y就能确定了。这样还有什么部件不能通过自定义实现呢?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Flutter是一种跨平台的移动应用开发框架,它使用Dart语言编写,并提供了丰富的布局控件来帮助开发者构建灵活和美观的用户界面。下面是对Flutter布局的一些理解: 1. 布局控件:Flutter提供了多种布局控件,包括容器(Container)、行(Row)、列(Column)、层叠(Stack)等等。这些控件可以嵌套使用,通过设置不同的属性和约束条件来实现各种复杂的布局效果。 2. 弹性布局Flutter中的弹性布局使用Flexible和Expanded控件来实现。Flexible控件可以根据可用空间进行伸缩,而Expanded控件则会占据剩余的可用空间。 3. 相对布局Flutter中的相对布局使用Positioned控件来实现。通过设置相对于父容器的位置和大小,可以精确地定位子控件。 4. 约束布局Flutter中的约束布局使用ConstrainedBox和AspectRatio控件来实现。ConstrainedBox可以设置子控件的最小和最大尺寸,而AspectRatio可以设置子控件的宽高比。 5. 响应式布局Flutter中的响应式布局使用MediaQuery和LayoutBuilder控件来实现。MediaQuery可以获取屏幕的尺寸和方向等信息,而LayoutBuilder可以根据父容器的尺寸来调整子控件的布局。 6. 自定义布局:除了内置的布局控件,Flutter还允许开发者自定义布局控件。通过继承RenderObjectWidget类并实现performLayout方法,开发者可以创建自己的布局控件。 总的来说,Flutter提供了丰富而灵活的布局控件,可以满足各种复杂的布局需求。开发者可以根据具体的场景和需求选择合适的布局方式来构建用户界面。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值