【- Flutter 組件篇 285 -】 CustomSingleChildLayout 通用單子佈局

1、認識組件

1. CustomSingleChildLayout組件介紹

可容納一個子組件,並指定代理類對子組件進行排布。代理類可獲取父容器區域和子組件的區域大小,及區域約束狀況。android

名稱:       CustomSingleChildLayout  通用單子排布
類型:       佈局型
重要性:     ☆☆☆
相關組件:   【Align】、【FractionallySizedBox】、【CustomMultiChildLayout】  
家族:       RenderObjectWidget
                |--- SingleChildRenderObjectWidget
                    |--- CustomSingleChildLayout
複製代碼

2、組件測試

1. 測試環境:

這裏父容器使用11灰的300*200的盒子,子組件爲不設寬高的橙色Container
以下: 默認的約束條件,會使橙色Container伸展佔滿父容器。git

class CustomSingleChildLayoutDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      width: 300,
      height: 200,
      color: Colors.grey.withAlpha(11),
      child:nContainer(
          color: Colors.orange,
      ),
    );
  }
}
複製代碼

2. 認識CustomSingleChildLayout

CustomSingleChildLayout容納一個child,且須要一個抽象代理類SingleChildLayoutDelegate
Flutter並無提供可用的實現類,因此只能自定義_TolySingleChildLayoutDelegategithub

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能夠根據原約束區域返回新的約束區域。以下:bash

@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 );
}
複製代碼

3、CustomSingleChildLayout能幹嗎?

從上面能夠看出,使用CustomSingleChildLayout能夠獲取父組件和子組件的佈局區域。並能夠對子組件進行盒約束偏移定位。一句話來講用於排佈一個組件。微信

1. 控制偏移的代理類

傳入一個Offset對象控制子組件的偏移。less

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組件能夠實現子組件相對於父組件的偏移。ide

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也許能夠幫到你。


尾聲

歡迎Star和關注FlutterUnit 的發展,讓咱們一塊兒攜手,成爲Unit一員。
另外本人有一個Flutter微信交流羣,歡迎小夥伴加入,共同分享Flutter的知識,期待與你的交流與切磋。

@張風捷特烈 2020.05.26 未允禁轉
個人公衆號:編程之王
聯繫我--郵箱:1981462002@qq.com --微信:zdl1994328
~ END ~

相關文章
相關標籤/搜索