zoukankan      html  css  js  c++  java
  • 【Flutter学习】页面布局之宽高尺寸处理

    一,概述  

      Flutter中拥有30多种预定义的布局widget,常用的有ContainerPaddingCenterFlexRowColumListViewGridView。按照《Flutter技术入门与实战》上面来说的话,大概分为四类

    • 基础布局组件Container(容器布局),Center(居中布局),Padding(填充布局),Align(对齐布局),Colum(垂直布局),Row(水平布局),Expanded(配合Colum,Row使用),FittedBox(缩放布局),Stack(堆叠布局),overflowBox(溢出父视图容器)。
    • 宽高尺寸处理SizedBox(设置具体尺寸),ConstrainedBox(限定最大最小宽高布局),LimitedBox(限定最大宽高布局),AspectRatio(调整宽高比),FractionallySizedBox(百分比布局)
    • 列表和表格处理ListView(列表),GridView(网格),Table(表格)
    • 其它布局处理:Transform(矩阵转换),Baseline(基准线布局),Offstage(控制是否显示组件),Wrap(按宽高自动换行布局)

    二,宽高尺寸处理

      • SizedBox(设置具体尺寸)
        • 介绍  
          比较常用的一个控件,设置具体尺寸。SizeBox组件是一个特定大小的盒子,这个组件强制它的chird有特定的宽度和高度,如果宽度和高度为null,则此组件将调整自身大小匹配该纬度中child的大小。
        • 布局行为

               SizedBox布局行为相对较简单:

          • child不为null时,如果设置了宽高,则会强制把child尺寸调到此宽高;如果没有设置宽高,则会根据child尺寸进行调整;
          • child为null时,如果设置了宽高,则自身尺寸调整到此宽高值,如果没设置,则尺寸为0;
        • 继承关系
          Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > SizedBox
        • 构造方法
          const SizedBox({ 
          Key key,
          this.width, //宽
          this.height, //高
          Widget child //子组件
          })
        • 参数解析

          width:宽度值,如果具体设置了,则强制child宽度为此值,如果没设置,则根据child宽度调整自身宽度。

          height:同上。

      • ConstrainedBox(限定最大最小宽高布局)
        • 介绍
          这个控件的作用是添加额外的限制条件(constraints)到child上,本身挺简单的,可以被一些控件替换使用。Flutter的布局控件体系,梳理着发现确实有点乱,感觉总体思想是缺啥就造啥
        • 布局行为
          ConstrainedBox的布局行为非常简单,取决于设置的限制条件,而关于父子节点的限制因素生效优先级,可以查看之前的文章,在这里就不做具体叙述了。
        • 继承关系
          Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > ConstrainedBox
          ConstrainedBox也是一个基础的布局控件。
        • 构造函数
           包含了一个constraints属性,且不能为null。
          ConstrainedBox({
           Key key,
           @required this.constraints,
           Widget child
          })
        • 参数解析
          constraints
              添加到child上的额外限制条件,其类型为BoxConstraints。BoxConstraints的作用是干啥的呢?其实很简单,就是限制各种最大最小宽高。说到这里插一句,double.infinity在widget布局的时候是合法的,也就说,例如. 想最大的扩展宽度,可以将宽度值设为double.infinity。  

             LimitedBox

        (限定最大宽高布局)
                    介绍LimitedBox,通过字面意思,也可以猜测出这个控件的作用,是限制类型的控件。这种类型的控件前面也介绍了不少了,这个是对最大宽高进行限制的控件。
                 布局行为从布局的角度讲,LimitedBox是将child限制在其设定的最大宽高中的,但这个限定是有条件的。当LimitedBox最大宽度不受限制时,child的宽度就会受到这个最大宽度的限制,高度同理。
      • 继承关系
        Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > LimitedBox
      • 构造函数
        const LimitedBox({
          Key key,
          this.maxWidth = double.infinity,
          this.maxHeight = double.infinity,
          Widget child,
        })
      • 参数解析 

        maxWidth:限定的最大宽度,默认值是double.infinity,不能为负数。

        maxHeight:同上。

    • AspectRatio(调整宽高比)
      • 介绍
        AspectRatio的作用是调整child到设置的宽高比,这种控件在其他移动端平台上一般都不会提供,Flutter之所以提供,我想最大的原因,可能就是自定义起来特别麻烦吧。
      • 布局行为

        AspectRatio的布局行为分为两种情况:

        • AspectRatio首先会在布局限制条件允许的范围内尽可能的扩展,widget的高度是由宽度和比率决定的,类似于BoxFit中的contain,按照固定比率去尽量占满区域。
        • 如果在满足所有限制条件过后无法找到一个可行的尺寸,AspectRatio最终将会去优先适应布局限制条件,而忽略所设置的比率。
      • 继承关系
        Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > AspectRatio

        从继承关系看,AspectRatio是基础的布局控件。

      • 构造函数
        const AspectRatio({
        Key key,
        @required this.aspectRatio,
        Widget child
        }) 

        构造函数只包含了一个aspectRatio属性,其中aspectRatio不能为null。

      • 参数解析 
        aspectRatio:aspectRatio是宽高比,最终可能不会根据这个值去布局,具体则要看综合因素,外层是否允许按照这种比率进行布局,只是一个参考值。
        
    • FractionallySizedBox(百分比布局)
        • 介绍
          FractionallySizedBox控件会根据现有空间,来调整child的尺寸,所以说child就算设置了具体的尺寸数值,也不起作用。
        • 布局行为

      FractionallySizedBox的布局行为主要跟它的宽高因子两个参数有关,当参数为null或者有具体数值的时候,布局表现不一样。当然,还有一个辅助参数alignment,作为对齐方式进行布局。

        • 当设置了具体的宽高因子,具体的宽高则根据现有空间宽高 * 因子,有可能会超出父控件的范围,当宽高因子大于1的时候;
        • 当没有设置宽高因子,则填满可用区域;
      • 继承关系
        Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > FractionallySizedBox
      • 构造函数
        const FractionallySizedBox({
          Key key,
          this.alignment = Alignment.center,
          this.widthFactor,
          this.heightFactor,
          Widget child,
        })
      • 参数解析 

        alignment:对齐方式,不能为null。

        widthFactor:宽度因子,跟之前介绍的控件类似,宽度乘以这个值,就是最后的宽度。

        heightFactor:高度因子,用作计算最后实际高度的。

        其中widthFactorheightFactor都有一个规则

        • 如果不为null,那么实际的最大宽高度则为child的宽高乘以这个因子;
        • 如果为null,那么child的宽高则会尽量充满整个区域。

    三,常用方法

    • SizeBox(设置具体尺寸)
      /**
       * MySizeBox
       */
      class MySizeBox extends StatelessWidget {
        @override
        Widget build(BuildContext context) {
          // TODO: implement build
          return new Container(
            color: Colors.green,
            padding: const EdgeInsets.all(5.0),
            child: SizedBox(
               200.0,
              height: 200.0,
              child: new Container(
                color: Colors.red,
                 100.0,
                height: 300.0,
              ),
            ),
          );
        }
      }

      效果图



      源码解析
      SizedBox内部是通过RenderConstrainedBox来实现的。具体的源码就不解析了,总体思路是,根据宽高值算好一个constraints,然后强制应用到child上。

    • ConstrainedBox(限定最大最小宽高布局)
      /**
       * ConstrainedBox
       * 在一个宽高200.0的Container上添加一个约束最大最小宽高的ConstrainedBox,实际的显示中,则是一个宽高为150.0的区域。
       */
      class MyConstrainedBox extends StatelessWidget {
        @override
        Widget build(BuildContext context) {
          // TODO: implement build
          return new ConstrainedBox(
            constraints: const BoxConstraints(
              minWidth: 100.0,
              minHeight: 100.0,
              maxWidth: 150.0,
              maxHeight: 150.0,
            ),
            child: new Container(
               200.0,
              height: 200.0,
              color: Colors.red,
            ),
          );
        }
      }

      效果图


      源码解析
      @override
       RenderConstrainedBox createRenderObject(BuildContext context) {
       return new RenderConstrainedBox(additionalConstraints: constraints);
      }

      RenderConstrainedBox实现其绘制。其具体的布局表现分两种情况:

      如果child不为null,则将限制条件加在child上;
      如果child为null,则会尽可能的缩小尺寸。
      具体代码如下:

      @override
      void performLayout() {
      if (child != null) {
        child.layout(_additionalConstraints.enforce(constraints), parentUsesSize: true);
        size = child.size;
       } else {
        size = _additionalConstraints.enforce(constraints).constrain(Size.zero);
       }
      }

      使用场景

      需要添加额外的约束条件可以使用此控件,例如设置最小宽高,尽可能的占用区域等等。笔者在实际开发中使用的倒不是很多,倒不是说这个控件不好使用,而是好多约束因素是综合的,例如需要额外的设置margin、padding属性能,因此单独再套个这个就显得很繁琐了。
    • LimitedBox(限定最大宽高布局)
      class MyLimitedBox extends StatelessWidget {
        @override
        Widget build(BuildContext context) {
          // TODO: implement build
          return new Row(
            children: <Widget>[
              new Container(
                color: Colors.red,
                 100.0,
              ),
              LimitedBox(
                maxWidth: 150.0,
                child: new Container(
                  color: Colors.blue,
                   250.0,
                ),
              )
            ],
          );
        }
      }

      效果图:


      源码解析

      先不说其源码,单纯从其作用,前面介绍的SizedBox、ConstrainedBox都类似,都是通过强加到child的constraint,来达到相应的效果。

      我们直接看其计算constraint的代码

      minWidth: constraints.minWidth,
      maxWidth: constraints.hasBoundedWidth ? constraints.maxWidth : constraints.constrainWidth(maxWidth),
      minHeight: constraints.minHeight,
      maxHeight: constraints.hasBoundedHeight ? constraints.maxHeight : constraints.constrainHeight(maxHeight)

      LimitedBox只是改变最大宽高的限定。具体的布局代码如下:

      if (child != null) {
        child.layout(_limitConstraints(constraints), parentUsesSize: true);
        size = constraints.constrain(child.size);
      } else {
        size = _limitConstraints(constraints).constrain(Size.zero);
      }

      根据最大尺寸,限制child的布局,然后将自身调节到child的尺寸。

      使用场景
         是不可能清楚了,光是找例子,就花了不少时间。Flutter的一些冷门控件,真的是除了官方的文档,啥材料都木有。
         谷歌说这个很有用,还是一脸懵逼。这种控件,也有其他的替代解决方案,LimitedBox可以达到的效果,ConstrainedBox都可以实现。

    • AspectRatio(调整宽高比)
      /**
       * AspectRatio
       * 定义了一个高度为200的区域,内部AspectRatio比率设置为1.5,最终AspectRatio的是宽300高200的一个区域。
       */
      class MyAspectRatio extends StatelessWidget {
        @override
        Widget build(BuildContext context) {
          // TODO: implement build
          return new Container(
            height: 200.0,
            child: new AspectRatio(
              aspectRatio: 1.5,
              child: new Container(
                color: Colors.red,
              ),
            ),
          );
        }
      }

      效果图

      源码解析
      @override
      RenderAspectRatio createRenderObject(BuildContext context) => new RenderAspectRatio(aspectRatio: aspectRatio);

      经过前面一些控件的解析,我想大家对这种构造应该不会再陌生了,绘制都是交由RenderObject去完成的,这里则是由RenderAspectRatio去完成具体的绘制工作。

      RenderAspectRatio的构造函数中会对aspectRatio做一些检测(assert)

      aspectRatio不能为null;
      aspectRatio必须大于0;
      aspectRatio必须是有限的。

      接下来我们来看一下RenderAspectRatio的具体尺寸计算函数:

      (1)如果限制条件为isTight,则返回最小的尺寸(constraints.smallest);

      if (constraints.isTight)
      return constraints.smallest;

      (2)如果宽度为有限的值,则将高度设置为width / _aspectRatio。 如果宽度为无限,则将高度设为最大高度,宽度设为height * _aspectRatio;

      if (width.isFinite) {
        height = width / _aspectRatio;
      } else {
        height = constraints.maxHeight;
        width = height * _aspectRatio;
      }

      (3)接下来则是在限制范围内调整宽高,总体思想则是宽度优先,大于最大值则设为最大值,小于最小值,则设为最小值。

      如果宽度大于最大宽度,则将其设为最大宽度,高度设为width / _aspectRatio;

      if (width > constraints.maxWidth) {
       width = constraints.maxWidth;
       height = width / _aspectRatio;
      }

      如果高度大于最大高度,则将其设为最大高度,宽度设为height * _aspectRatio;

      if (height > constraints.maxHeight) {
       height = constraints.maxHeight;
       width = height * _aspectRatio;
      }

      如果宽度小于最小宽度,则将其设为最小宽度,高度设为width / _aspectRatio;

      if (width < constraints.minWidth) {
       width = constraints.minWidth;
       height = width / _aspectRatio;
      }

      如果高度小于最小高度,则将其设为最小高度,宽度设为height * _aspectRatio。

      if (height < constraints.minHeight) {
       height = constraints.minHeight;
       width = height * _aspectRatio;
      }
    • FractionallySizedBox(百分比布局)
      /**
       * MyFractionallySizeBox
       */
      
      class  MyFractionallySizeBox extends StatelessWidget {
        @override
        Widget build(BuildContext context) {
          // TODO: implement build
          return new Container(
            color: Colors.blue,
            height: 150.0,
             150.0,
            padding: const EdgeInsets.all(10.0),
            child: new FractionallySizedBox(
              alignment: Alignment.topLeft,
              widthFactor: 1.5,
              heightFactor: 0.5,
              child: new Container(
                color: Colors.red,
              ),
            ),
          );
        }
      }

      效果图

      源码解析
      FractionallySizedBox内部具体渲染是由RenderFractionallySizedOverflowBox来实现的,通过命名就可以看出,这个控件可能会Overflow。

      我们直接看实际计算尺寸的代码

      double minWidth = constraints.minWidth;
      double maxWidth = constraints.maxWidth;
      if (_widthFactor != null) {
       final double width = maxWidth * _widthFactor;
       minWidth = width;
       maxWidth = width;
      }
      double minHeight = constraints.minHeight;
      double maxHeight = constraints.maxHeight;
      
      if (_heightFactor != null) {
       final double height = maxHeight * _heightFactor;
       minHeight = height;
       maxHeight = height;
      }

      源代码中,根据宽高因子是否存在,来进行相对应的尺寸计算。

      使用场景
      当需要在一个区域里面取百分比尺寸的时候,可以使用这个,比方说,高度40%宽度70%的区域。当然,AspectRatio也可以达到近似的效果。

    四,参考  

    Flutter学习之认知基础组件
    Flutter布局

  • 相关阅读:
    《应用Yii1.1和PHP5进行敏捷Web开发》学习笔记(转)
    YII 小模块功能
    Netbeans代码配色主题大搜集
    opensuse 启动巨慢 解决方法 90s多
    opensuse 安装 网易云音乐 rpm netease music
    linux qq rpm deb opensuse
    openSUSE 安装 alien
    第一行代码 Android 第2版
    Android Studio AVD 虚拟机 联网 失败
    docker error during connect: Get http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.29/containers/json: open //./pipe/docker_engine: The system cannot find the file specified. In the default daemon configuratio
  • 原文地址:https://www.cnblogs.com/lxlx1798/p/11090220.html
Copyright © 2011-2022 走看看