Flutter Advanced - レイアウト (レイアウト) の原則

1. 制約、サイズ、位置  

  @override
  Widget build(BuildContext context) {
    return Scaffold(body: LayoutBuilder(builder: (context, constraints) {
      print("body约束:" + constraints.toString());
      return Container(
          color: Colors.black,
          width: 300,
          height: 300,
          child: LayoutBuilder(builder: (context, constraints) {
            print("Container约束:" + constraints.toString());
            return Container(width: 20, height: 20, color: Colors.orange);
          }));
    }));
  }

ランニング効果図: 

コンソール出力:

フラッター: ボディ制約: BoxConstraints(0.0<=w<=390.0, 0.0<=h<=844.0)
フラッター: コンテナ制約: BoxConstraints(w=300.0, h=300.0)

このことから、次のように結論付けることができます。

  • 本体は子コントロールに対する緩やかな制約です (子コントロールは幅と高さを指定するように設定できます)。
  • コンテナーは子コントロールに対する厳しい制約です (子コントロールがコンテナーの配置方法を指定しない場合、位置が指定されていない場合、システムはズームインまたはズームアウトした後に子コントロールを配置する方法を認識せず、単純に埋め込まれます)親コントロールの幅と高さの設定は自然に失敗します)、本質的に、子コントロールは親コントロールの制約に違反するため、親コントロールの制約に従って実行されます
  • 概要: 子コントロールのサイズを設定するときに、それをリッスンしない場合は、LayoutBuilder(builder: (context,constraints){}) を使用してそれを印刷し、それがきついか緩いかを確認できます。

レイアウトの原則:

レンダリング前の各コンポーネントのレイアウト プロセスは、2 つの直線的なプロセスに分割できます。レイアウト制約はまずコンポーネントの上部から渡され、レイアウト情報は下部から上に渡されます。

簡単に言うと、フラッターはレイアウト時にコンポーネント ツリーを走査し、ルートから開始して (深さ優先の原則)、制約を下に渡します。子コントロールの制約が親コントロールの制約に違反すると、親コントロールの制約が実行されます。 。Flutter のレイアウトでは、onePass は WidgetTree を 1 回トラバースするだけで済み、サイズを上に渡し、最後に親がサイズを取得して子を配置する場所を決定します。

これら 2 つの線形プロセスは、要素ツリーによって参照される RenderObject ツリー内で完了し、最終的なレイアウト情報が RenderObject に保存されます。そのため、コンポーネントを再構築する際、要素やRenderObjectが再利用できる場合には、前回と同じレイアウト情報を利用することも可能です。この情報の受け渡しと保存の一方向の方法は、Flutter のレイアウト パフォーマンスが他のフレームワークよりも優れている重要な理由の 1 つです。

RenderObject ツリーは、RenderObject を 1 つずつ集めて構成されます。Element インスタンスが要素ツリーにマウントされると、コンポーネントの createRenderObject() メソッドが呼び出され、対応する RenderObject が生成されます。RenderObject ツリーは要素ツリーによって参照され、その主なタスクは Element インスタンスによる特定のレンダリング作業を支援することであるため、RenderObject ツリーは要素ツリーのサブツリーとも呼ばれます。

 各 RenderObject は要素によって保持され、コンポーネントの再構築後に可能な限り再利用されます。要素の状態が変化するたびに、コンポーネントの updateRenderObject() メソッドが呼び出され、レンダー オブジェクトとその値が更新されます。画面上の内容は最終的に更新されます。

2. カスタムボックスのレイアウト制約

 @override
  Widget build(BuildContext context) {
    return Scaffold(body: LayoutBuilder(builder: (context, constraints) {
      print("body约束:" + constraints.toString());
      return Container(
        constraints: BoxConstraints(
            minWidth: 60, minHeight: 60, maxWidth: 100, maxHeight: 100),
        child: LayoutBuilder(builder: (context, constraints) {
          print("Container约束:" + constraints.toString());
          return FlutterLogo(size: 500);
        }),
      );
    }));
  }

flutter: body约束:BoxConstraints(0.0<=w<=390.0, 0.0<=h<=844.0)
flutter: Container约束:BoxConstraints(60.0<=w<=100.0, 60.0<=h<=100.0)

3.カスケードコンポーネントをスタックする

  1. (子には通常のウィジェットと位置でラップされたウィジェットの両方があります)、そのサイズは、位置でラップされていない子の中で最大のサブコンポーネントによって決まります。(位置でラップされたサブコンポーネントは、絶対的なものと多少似ています)フロントエンド レイアウト (レイアウトはドキュメント フローから分離されます) は、親コンポーネントを超えて表示することもできます。表示したくない場合は、clipBehavior: Clip.hardEdge を設定して、余分な部分を強制的に表示することもできます。切り取られた。
  2. (子内のすべては通常のウィジェットです)、そのサイズはサブコンポーネントにラップされます。
  3. (子はすべてPosition でラップされた Widget であるため、参照オブジェクトがなく、配置方法がわかりません。) スタックのサイズは、可能な限り大きな親コンポーネントで埋められます。スタック自体は十分に大きく、対応する子は左上の配置または他の配置を設定します。当然のことですが、そうでない場合、それらは一緒に押し込まれ、元の設定の配置の意味が失われます)

4、カスタムMultiChildLayout

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: CustomMultiChildLayout(
      delegate: MyDelegate(4),
      children: [
        LayoutId(
            id: "text",
            child: Text(
                "测试文字下划线测试文字下划线测试文字下划线测试文字下划线测试文字下划线测试文字下划线测试文字下划线测试文字下划线")),
        LayoutId(
            id: "underline",
            child: Container(
              color: Colors.green,
            ))
      ],
    ));
  }

class MyDelegate extends MultiChildLayoutDelegate {

final double thickness;

MyDelegate(this.thickness);



@override

Size getSize(BoxConstraints constraints) {

return super.getSize(constraints);

}

@override

void performLayout(Size size) {

final sizeText = layoutChild("text", BoxConstraints.loose(size));



final sizeUnderline =

layoutChild("underline", BoxConstraints.tight(Size(sizeText.width, thickness)));



final left = (size.width - sizeText.width) / 2;

final top = (size.height - sizeText.height) / 2;

positionChild("text", Offset(left, top));

positionChild("underline", Offset(left, top + sizeText.height));

}



@override

bool shouldRelayout(covariant MultiChildLayoutDelegate oldDelegate) => true;

}

 5、レンダーオブジェクト

 @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Container(
            color: Colors.red,
            child: ShadowBox(child: FlutterLogo(size: 200), distance: 200)));
  }


//SingleChildRenderObjectWidget一种能真正画到屏幕上的widget
class ShadowBox extends SingleChildRenderObjectWidget {
  double distance;
  ShadowBox({Widget child, this.distance}) : super(child: child);

  @override
  RenderObject createRenderObject(BuildContext context) {
    return RenderShadowBox(distance);
  }

  @override
  void updateRenderObject(
      BuildContext context, covariant RenderShadowBox renderObject) {
    renderObject.distance = distance;
  }
}

//RenderObjectWithChildMixin比较方便的设置一个child
class RenderShadowBox extends RenderProxyBox with DebugOverflowIndicatorMixin {
  double distance;

  RenderShadowBox(this.distance);

  //处理布局
  // @override
  // void performLayout() {
  //   child.layout(constraints, parentUsesSize: true); //parentUsesSize: false
  //   // 如果parentUsesSize: false则parent的size跟这个child没关系以后再relayout凡是涉及到这个child的不会再更新,如果是true则代表会使用这个child
  //   size = (child as RenderBox).size; // relayout boundary
  //   // size = Size(300, 100);
  // }

  //处理绘制
  //其实layout和paint各遍历一次
  @override
  void paint(PaintingContext context, Offset offset) {
    context.paintChild(child, offset);
    context.canvas.clipRRect(RRect.fromLTRBAndCorners(105, 5, 5, 5));
    //建立新图层,可以在新图层上做各种操作
    context.pushOpacity(offset, 100, (context, offset) {
      context.paintChild(child, offset + Offset(distance, distance));
    });
    //containerRect: 能画的范围
    //childRect: 想让画的范围
    //警戒线
    paintOverflowIndicator(
        context,
        offset,
        Offset.zero & size, //Rect.fromLTWH(0, 0, size.width, size.height)
        Offset.zero & child.size);
  }
}

おすすめ

転載: blog.csdn.net/RreamigOfGirls/article/details/130989686