flutter 布局原理

flutter 布局原理,第1张

➢ layout 约束 尺寸 位置:

先看一下,下面这段代码:

body: Container(
  width: 400,
  height: 400,
  color: Colors.blue,
  child: Container(
    width: 100,
    height: 100,
    color: Colors.orange,
    child: const FlutterLogo(
      size: 50,
    ),
  ),
),

我们:想要的是 400*400 蓝色底,100*100橘色的底, 50*50 FlutterLoge, 而实际情况确是这样:

究其原因是因为:上面的组件没有满足 Flutter的约束条件会被修正,可以理解为 child  width 和height 只是个建议尺寸,实际的尺寸 是有 父级对象 决定的!

Flutter 向下传递约束 Flutter 向上传递建议尺寸最后父级通过 位置计算得到 每个 child尺寸,在决定把 child 放在那里

经过简单的处理之后 我们得到了应该有的布局

body: Container(
  width: 400,
  height: 400,
  color: Colors.blue,
  child: Align(
    //alignment: Alignment(0.5,0.5)等价于 alignment: Alignment.center,
    // alignment: Alignment.bottomCenter,
    child: Container(
      width: 250,
      height: 250,
      color: Colors.orange,
      //没有规定child 放在那里,所有只能 放满全屏,如果 添加一个Center/Align 让父组件怎么 如何摆放child,这时就能确定 child位置和 尺寸
      //Center 继承自 Align
      child: Center(
        child: const FlutterLogo(
          size: 50,
        ),
      ),
    ),
  ),
),

➢ layout 获取和设置 布局约束:

Flutter 布局约束:

紧约束
constraints:BoxConstraints(w=350.0, h=350.0)
松约束   (最小值为0的约束 )
BoxConstraints(0.0<=w<=350.0, 0.0<=h<=350.0)

下面的代码中 FlutterLoge() 设置的 size: 9999,是没有效果的,会被父层级Container 修正!

body: Center(
  child: Container(
    width: 350,
    height: 350,
    color: Colors.orangeAccent,
    child: FlutterLogo(
      size: 9999,
    ),
  ),
)

可以使用 LayoutBuilder 查看父层级的约束:

child: LayoutBuilder(
  builder: (BuildContext context, BoxConstraints constraints) {
    print("constraints:$constraints");
    return const FlutterLogo(
      size: 9999,
    );
  },
)

  打印结果为一个: 紧约束,所有这里不管我们怎么 设置子组件尺寸都是 没有用的,父组件会强制设置子组件的尺寸为 350*350

I/flutter (13229): constraints:BoxConstraints(w=350.0, h=350.0)

➽ 我们给 LayoutBuilder  包裹一个 Center 组件, 可以改变父组件的 约束,成为一个 松约束,让后告诉父组件我们要摆放的位置;就能实现控制子组件的大小了

constraints:BoxConstraints(0.0<=w<=350.0, 0.0<=h<=350.0)

body: Center(
  child: Container(
    width: 350,
    height: 350,
    color: Colors.orangeAccent,
    child: Center(
      child: LayoutBuilder(
        builder: (BuildContext context, BoxConstraints constraints) {
          print("constraints:$constraints");
          return const FlutterLogo(
            size: 50,
          );
        },
      ),
    ),
  ),
),
➽ ConstrainedBox,修改我们的约束, 前提是 我们当前组件满足父组件的约束:
先将我们的约束设置为 松约束,如果不设置,我么的定义约束还是会被父组件 强制修改的定定义我们的约束
body: Center(
  child: Container(
    width: 350,
    height: 350,
    color: Colors.orangeAccent,
    child: Center( //1 先将我们的约束设置为 松约束
      child: ConstrainedBox(
        constraints: const BoxConstraints( //2 定定义我们的约束
            minWidth: 50, maxWidth: 250, minHeight: 50, maxHeight: 250),
        child: LayoutBuilder(
          builder: (BuildContext context, BoxConstraints constraints) {
            print("constraints:$constraints");
            return const FlutterLogo(
              size: 50,
            );
          },
        ),
      ),
    ),
  ),
),

 上面代码中如果我们不使用,Center组件(不修改 强制约束松约束),那么 ConstrainedBox 是不会起作用的!

下面的代码设置 自定义约束就不会起作用!:

//错误示范
body: Center(
  child: Container(
    width: 350,
    height: 350,
    color: Colors.orangeAccent,
      //1 先将我们的约束设置为 松约束;负责不起作用
      child: ConstrainedBox(
        constraints: const BoxConstraints( //2 定定义我们的约束
            minWidth: 50, maxWidth: 250, minHeight: 50, maxHeight: 250),
        child: LayoutBuilder(
          builder: (BuildContext context, BoxConstraints constraints) {
            print("constraints:$constraints");
            return const FlutterLogo(
              size: 50,
            );
          },
        ),    
    ),
  ),
),

➢ layout d性布局:

   Column 或者 Row 中嵌入 ListView: 报错这个现象:

body: Center(
  child: Container(
    color: Colors.orange,
    width: 250,
    child: Column(
      // mainAxisSize: MainAxisSize.max,
      mainAxisSize: MainAxisSize.min,
      mainAxisAlignment: MainAxisAlignment.spaceAround,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        ElevatedButton(onPressed: () {}, child: Text("")),
        ListView(
          children: [for (int i = 0; i < 10; i++) Text("List view $i")],
        ),
        ElevatedButton(onPressed: () {}, child: Text("")),
      ],
    ),
  ),
),
RenderBox was not laid out: RenderRepaintBoundary#341bb relayoutBoundary=up5 NEEDS-PAINT
'package:flutter/src/rendering/box.dart':
Failed assertion: line 1929 pos 12: 'hasSize'

上面代码当中

Column 元素会首先布局  固定大小(有约束限制)的组件:其中 ElevatedButton组件高度固定,

Listview 组件是一个d性组件高度无限制,而 Column 也是一个高度无限制的组件;所以就会出错!在这里给ListView  包裹一层就能解决这个问题!这里归根结底的原因就是:Column 本身是一个无限制的元素,而子元素 中除过 ListView 也是一个无限制元素;两个无限制元素同时出现时  Flutter就无法确定 元素大小就会报错

I/flutter (11566): constraints:BoxConstraints(0.0<=w<=250.0, 0.0<=h<=Infinity)

所以 当无约束的空间 嵌套无约束的空间,就是出异常,在这里给 ListView 包裹 Expand组件就能决绝 这个问题:Expand占剩余空间 大小确定

body: Center(
  child: Container(
    color: Colors.orange,
    width: 250,
    child: Column(
      // mainAxisSize: MainAxisSize.max,
      mainAxisSize: MainAxisSize.min,
      mainAxisAlignment: MainAxisAlignment.spaceAround,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        ElevatedButton(onPressed: () {}, child: Text("")),
        Expanded(
          child: ListView(
            children: [
              for (int i = 0; i < 100; i++) Text("List view $i")
            ],
          ),
        ),
        ElevatedButton(onPressed: () {}, child: Text("")),
      ],
    ),
  ),
),

➢ layout Stack 层叠:影响 Stack 组件大小的因素

在 Stack 布局中  有一个有意思的现象:

子组件中  有Positioned 组件 和 其他组件的情况下 Stack 组件大小由 最大 非Positioned 组件 决定;如下代码中 则是由 最长 Text 组件决定的                                                                                
body: Center(
  child: Container(
    color: Colors.blue,
    child: Stack(
      // alignment: Alignment.center,
      alignment: Alignment(-1, 0.75),
      children: [
        Text(
          'You have You have You',
          style: TextStyle(fontSize: 20, color: Colors.orange),
        ),
        Text(
          'GoGoGO',
          style: TextStyle(fontSize: 15, color: Colors.red),
        ),
        Positioned(
          left: 0,
          top: 0,
          child: FlutterLogo(
            size: 250,
          ),
        ),
        Positioned(
          left: 0,
          top: 0,
          child: Container(
            width: 24,
            height: 24,
            color: Colors.black,
          ),
        ),
      ],
    ),
  ),
),

2.子组件中 都是 Positioned 组件,那么 Stack 的尺寸则会占满父组件 最大约束

之所以会有这样的现象 主要是应为:本身Stack 组件为 用户实现层叠样式实现的,如果有其他组件(非Positioned)那么State 就有参照!所以可以按照参照进行布局,如果全部都是 Positoned 则没有参照;所以会 占满父组件;

当然也可通过 Satck  fit 属性进行设置:修改默认规则

child: Stack(
  //fit: StackFit.expand,//最大适配子元素
  fit:StackFit.loose,//最小适配元素
  //fit:StackFit.passthrough,//直接把 Stack 父组件的约束传递给子组件们

body: Center(
  child: Container(
    color: Colors.blue,
    child: Stack(
      fit: _isSelect ? StackFit.expand : StackFit.loose, 
      // fit: StackFit.loose, //最小适配
      // fit: StackFit.expand, //最大适配
      // alignment: Alignment.center,
      alignment: Alignment(-1, 0.75),
      children: [
        Text("Stack 影响组件大小的 因素"),
        Positioned(
          left: 0,
          top: 0,
          child: FlutterLogo(
            size: 250,
          ),
        ),
        Positioned(
          left: 0,
          top: 0,
          child: Container(
            width: 24,
            height: 24,
            color: Colors.black,
          ),
        ),
        Switch(
          value: _isSelect,
          onChanged: (value) {
            setState(() {
              _isSelect = value;
            });
            print('Switch--------value:$value');
            print('Switch--------_isSelect:$_isSelect');
          },
        ),
      ],
    ),
  ),
),

 Stack  超出显示和裁切:超出部分 虽然可以显示出来,但是不能响应 用户点击事件

overflow: Overflow.visibleclipBehavior: Clip.none,//超出部分 虽然可以显示出来,但是不能响应 用户点击事件

body: Center(
  child: Container(
    color: Colors.blue,
    constraints: const BoxConstraints(
      maxWidth: 350,
      minWidth: 10,
      maxHeight: 350,
      minHeight: 10,
    ),
    child: Stack(
      //alignment: Alignment.bottomCenter,
      overflow: Overflow.visible,
      clipBehavior: Clip.none,//超出部分 虽然可以显示出来,但是不能响应 用户点击事件
      children: [
        FlutterLogo(size: 250),
        Positioned(
          left: 20,
          top: -20,
          width: 250,
          height: 50,
          child: Container(
            width: 24,
            height: 24,
            color: Colors.orange,
          ),
        ),
      ],
    ),
  ),
),

➢ layout  决定 Container 组件大小的因素
当  Container 没有 子组件 的时候,自身尺寸 占满父组件最大约束当  Contaiber     有子组件 的时候,自身尺寸  就是子组件的尺寸*当 Contaiber 对齐属性的时候Contaiber  自身尺寸 占满父组件最大约束;(对齐属性类似包裹了一层 layout*当 Contaiber  父组件是类似 Column/Row/ListView  这种无边界约束的时候 unborder,因为不能确定约束的最大的值! 这时候 Contaiber 会取约束的最小值

 没有 子组件 的时候:

body: Center(
  child: Container(
    color: Colors.orange,
    // child: FlutterLogo(size: 100),
  ),
),

有子组件 的时候:

body: Center(
  child: Container(
    color: Colors.orange,
    child: FlutterLogo(size: 100),
  ),
),

右对齐属性:

body: Container(
  alignment: Alignment.center,
  color: Colors.orange,
  child: const FlutterLogo(
    size: 50,
  ),
),

 Column/Row/ListView  这种无边界约束的时候:

body: Column(
  children: [
    Container(
      // alignment: Alignment.center,
      color: Colors.orange,
      child: const FlutterLogo(
        size: 50,
      ),
    )
  ],
),

 当然也通过可以修改  Container 的 constraints 改变父组件约束,来修 占满父组件最大约束

body: Center(
  child: Container(
    constraints: const BoxConstraints(
      maxHeight: 250,
      minHeight: 10,
      maxWidth: 250,
      minWidth: 10,
    ),
    color: Colors.orange,
    // child: FlutterLogo(size: 30),
  ),
),

如果有 约束传递/多组件约束叠加  的情况发生,那么取 约束的交集

Center 组件是一个 松约束 范围  width: 0 到 屏幕宽;   height: 0 到 屏幕高SizedBox 强制宽度到 200高度没有 影响Container :受父组件约束 高度200,受子组件影响 宽度 100FlutterLog :  宽100   高100

所以在这里 Container 组件的大小 实际上 受到多个组建的叠加影响

body: Center(
  //松约束 0-屏幕宽高
  child: SizedBox(
    width: 200, // 宽度紧约束
    child: Container(
      //页数叠加: 宽度由SizeBox决定, 由于Container 又子组件高度为 子组件的高度 200*100
      color: Colors.orange,
      child: FlutterLogo(size: 100), //子组件 紧约束 100*100
    ),
  ),
),

本质上:Container组件 类以一个语法糖的写法;里面的很多属性 都是一个一个的 类型去实现的:等等

如果有  alignment 属性则会包裹一层 Align(alignment: alignment!, child: current);如果有  color 属性,则会包裹一层 ColoredBox(color: color!, child: current);

他的部分源码:

   @override
  Widget build(BuildContext context) {
    Widget? current = child;

    if (child == null && (constraints == null || !constraints!.isTight)) {
      current = LimitedBox(
        maxWidth: 0.0,
        maxHeight: 0.0,
        child: ConstrainedBox(constraints: const BoxConstraints.expand()),
      );
    }

    if (alignment != null)
      current = Align(alignment: alignment!, child: current);

    final EdgeInsetsGeometry? effectivePadding = _paddingIncludingDecoration;
    if (effectivePadding != null)
      current = Padding(padding: effectivePadding, child: current);

    if (color != null)
      current = ColoredBox(color: color!, child: current);

    if (clipBehavior != Clip.none) {
      assert(decoration != null);
      current = ClipPath(
        clipper: _DecorationClipper(
          textDirection: Directionality.maybeOf(context),
          decoration: decoration!,
        ),
        clipBehavior: clipBehavior,
        child: current,
      );
    }

    if (decoration != null)
      current = DecoratedBox(decoration: decoration!, child: current);

    if (foregroundDecoration != null) {
      current = DecoratedBox(
        decoration: foregroundDecoration!,
        position: DecorationPosition.foreground,
        child: current,
      );
    }

    if (constraints != null)
      current = ConstrainedBox(constraints: constraints!, child: current);

    if (margin != null)
      current = Padding(padding: margin!, child: current);

    if (transform != null)
      current = Transform(transform: transform!, alignment: transformAlignment, child: current);

    return current!;
  }

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/994478.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存