Flutter CustomSingleChildLayout 通用单子布局
  xx2YH4ad7R0N 2023年11月02日 50 0


一、认识组件
1. CustomSingleChildLayout组件介绍
可容纳一个子组件,并指定代理类对子组件进行排布。代理类可获取父容器区域和子组件的区域大小,及区域约束情况。

名称:       CustomSingleChildLayout  通用单子排布
类型:       布局型
重要性:     ☆☆☆
相关组件:   【Align】、【FractionallySizedBox】、【CustomMultiChildLayout】  
家族:       RenderObjectWidget
                |--- SingleChildRenderObjectWidget
                    |--- CustomSingleChildLayout
 

二、组件测试
1. 测试环境:
这里父容器使用11灰的300*200的盒子,子组件为不设宽高的橙色Container。
如下: 默认的约束条件,会使橙色Container伸展占满父容器。

class CustomSingleChildLayoutDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 300,
height: 200,
color: Colors.grey.withAlpha(11),
child: CustomSingleChildLayout(
delegate: _TolySingleChildLayoutDelegate(),
child: Container(
color: Colors.orange,
),
),
);
}
}

2. 认识CustomSingleChildLayout

CustomSingleChildLayout容纳一个child,且需要一个抽象代理类SingleChildLayoutDelegate
Flutter并没有提供可用的实现类,所以只能自定义_TolySingleChildLayoutDelegate。

class CustomSingleChildLayoutDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 300,
height: 200,
color: Colors.grey.withAlpha(11),
child: CustomSingleChildLayout(
delegate: _TolySingleChildLayoutDelegate(),
child: Container(
color: Colors.orange,
),
),
);
}
}
  • SingleChildLayoutDelegate必须实现shouldRelayout方法,可重写:
  • Size getSize(BoxConstraints): 可获取父容器约束条件
  • Offset getPositionForChild(Size, Size) 可获取父子组件的区域,返回子组件偏移量
  • BoxConstraints getConstraintsForChild(BoxConstraints)可获取父容器约束条件,并返回新的约束条件
class _TolySingleChildLayoutDelegate extends SingleChildLayoutDelegate {
@override
bool shouldRelayout(SingleChildLayoutDelegate oldDelegate) {
return true;
}

@override
Size getSize(BoxConstraints constraints) {
print('----getSize:----constraints:$constraints----');
return super.getSize(constraints);
}

@override
Offset getPositionForChild(Size size, Size childSize) {
print('----size:$size----childSize:$childSize----');
return super.getPositionForChild(size, childSize);
}

@override
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
print('----getConstraintsForChild:----constraints:$constraints----');
return super.getConstraintsForChild(constraints);
}
}

看一下运行打印结果:

I/flutter (28366): ----getSize:----constraints:BoxConstraints(w=300.0, h=200.0)----
I/flutter (28366): ----getConstraintsForChild:----constraints:BoxConstraints(w=300.0, h=200.0)----
I/flutter (28366): ----size:Size(300.0, 200.0)----childSize:Size(300.0, 200.0)----

3.使用新的区域约束

getConstraintsForChild可以根据原约束区域返回新的约束区域。如下:

@override
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
print('----getConstraintsForChild:----constraints:$constraints----');
return BoxConstraints(
maxHeight: constraints.maxHeight/2,
maxWidth: constraints.maxWidth/2,
minHeight: constraints.maxHeight/4,
minWidth: constraints.maxWidth/4,
);
}

4. 子组件的偏移

Offset getPositionForChild(Size, Size) 可获取父、子组件的区域,返回子组件偏移量
如下,在刚才的基础上,可以通过偏移让子组件到容器的右上角。

@override
Offset getPositionForChild(Size size, Size childSize) {
print('----size:$size----childSize:$childSize----');
return Offset(size.width/2,0 );
}

三、CustomSingleChildLayout能干嘛?

从上面可以看出,使用CustomSingleChildLayout可以获取父组件和子组件的布局区域。并可以对子组件进行盒约束及偏移定位。一句话来说用于排布一个组件。

1. 控制偏移的代理类
传入一个Offset对象控制子组件的偏移。

class _OffSetDelegate extends SingleChildLayoutDelegate {
final Offset offset;

_OffSetDelegate({this.offset = Offset.zero});

@override
bool shouldRelayout(_OffSetDelegate oldDelegate) =>
offset != oldDelegate.offset;

@override
Offset getPositionForChild(Size size, Size childSize) {
return offset;
}
}

2. 封装类OffSetWidget

你可以直接用CustomSingleChildLayout组件,但为了方便使用,一般都会进行封装使用
下面的OffSetWidget组件可以实现子组件相对于父组件的偏移。

class OffSetWidget extends StatelessWidget {
final Offset offset;
final Widget child;

OffSetWidget({this.offset = Offset.zero, this.child});

@override
Widget build(BuildContext context) {
return CustomSingleChildLayout(
delegate: _OffSetDelegate(offset: offset),
child: child,
);
}
}

3. OffSetWidget使用

这样就可以让子组件在父组件中发生相对偏移。
简约派代表:"等等...老子看了半天,你给我个简易版的Padding?还花里胡哨的。"
兄台莫急,且往下看。

class OffSetWidgetDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 300,
height: 100,
alignment: Alignment.topRight,
color: Colors.grey.withAlpha(11),
child: OffSetWidget(
offset: Offset(20, 20),
child: Icon(Icons.android, size: 30,color: Colors.green,),
));
}
}

这里最大的优势是: Offset支持负偏移

child: OffSetWidget(
offset: Offset(-20, 20),
child: Icon(Icons.android, size: 30,color: Colors.green,),
));

4. 方向版

通过动态计算,可以锁定访问进行偏移,如下:
这样就像Position的能力,但Position有必须用于Stack的现在。
而OffSetWidget随意 并且支持负偏移

class OffSetWidget extends StatelessWidget {
final Offset offset;
final Widget child;
final Direction direction;

OffSetWidget({this.offset = Offset.zero,
this.child,
this.direction = Direction.topLeft});

@override
Widget build(BuildContext context) {
return CustomSingleChildLayout(
delegate: _OffSetDelegate(offset: offset, direction: direction),
child: child,
);
}
}

enum Direction { topLeft, topRight, bottomLeft, bottomRight }

class _OffSetDelegate extends SingleChildLayoutDelegate {
final Offset offset;
final Direction direction;

_OffSetDelegate(
{this.offset = Offset.zero, this.direction = Direction.topLeft});

@override
bool shouldRelayout(_OffSetDelegate oldDelegate) =>
offset != oldDelegate.offset;

@override
Offset getPositionForChild(Size size, Size childSize) {
var w = size.width;
var h = size.height;
var wc = childSize.width;
var hc = childSize.height;

switch (direction) {
case Direction.topLeft:
return offset;
case Direction.topRight:
return offset.translate(w - wc - offset.dx * 2, 0);
case Direction.bottomLeft:
return offset.translate(0, h - hc - offset.dy * 2);
case Direction.bottomRight:
return offset.translate(w - wc - offset.dx * 2, h - hc - offset.dy * 2);
}
return offset;
}
}

CustomSingleChildLayout组件的用法还是比较简单的。上面代码只是简单演示一下使用方式,也许并不是太实用。Positioned组件可以实现定位,SizedOverflowBox组件可以实现溢出。 不过当你遇到对某一个组件约束或定位困难时,CustomSingleChildLayout也许可以帮到你。
 

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  iD7FikcuyaVi   2023年11月30日   23   0   0 MacWindowsandroid
  b1UHV4WKBb2S   2023年11月13日   33   0   0 裁剪ideflutter
  b1UHV4WKBb2S   2023年11月13日   26   0   0 flutterDart
  zSWNgACtCQuP   2023年11月13日   29   0   0 ide
xx2YH4ad7R0N