Flutter CustomSingleChildLayout 通用单子布局

Posted 一叶飘舟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter CustomSingleChildLayout 通用单子布局相关的知识,希望对你有一定的参考价值。

一、认识组件
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也许可以帮到你。
 

以上是关于Flutter CustomSingleChildLayout 通用单子布局的主要内容,如果未能解决你的问题,请参考以下文章

[Flutter] flutter项目一直卡在 Running Gradle task 'assembleDebug'...

flutter 日志输出,Flutter打印日志,flutter log,flutter 真机日志

Flutter开发 Flutter 包和插件 ( Flutter 包和插件简介 | 创建 Flutter 插件 | 创建 Dart 包 )

flutter与原生混编(iOS)

Flutter-布局

如何解决flutter gradle build error?C:\flutter\packages\flutter_tools\gradle\flutter.gradle' line: 991