【Flutter學習】頁面佈局之寬高尺寸處理

一,概述  

  Flutter中擁有30多種預約義的佈局widget,經常使用的有ContainerPaddingCenterFlexRowColumListViewGridView。按照《Flutter技術入門與實戰》上面來講的話,大概分爲四類git

  • 基礎佈局組件Container(容器佈局),Center(居中佈局),Padding(填充佈局),Align(對齊佈局),Colum(垂直佈局),Row(水平佈局),Expanded(配合Colum,Row使用),FittedBox(縮放佈局),Stack(堆疊佈局),overflowBox(溢出父視圖容器)。
  • 寬高尺寸處理SizedBox(設置具體尺寸),ConstrainedBox(限定最大最小寬高佈局),LimitedBox(限定最大寬高佈局),AspectRatio(調整寬高比),FractionallySizedBox(百分比佈局)
  • 列表和表格處理ListView(列表),GridView(網格),Table(表格)
  • 其它佈局處理:Transform(矩陣轉換),Baseline(基準線佈局),Offstage(控制是否顯示組件),Wrap(按寬高自動換行佈局)

二,寬高尺寸處理github

    • SizedBox(設置具體尺寸)
      • 介紹  
        比較經常使用的一個控件,設置具體尺寸。SizeBox組件是一個特定大小的盒子,這個組件強制它的chird有特定的寬度和高度,若是寬度和高度爲null,則此組件將調整自身大小匹配該緯度中child的大小。
      • 佈局行爲

             SizedBox佈局行爲相對較簡單:less

        • 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寬度調整自身寬度。ide

        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,不能爲負數。post

      maxHeight:同上。性能

  • AspectRatio(調整寬高比)
    • 介紹
      AspectRatio的做用是調整child到設置的寬高比,這種控件在其餘移動端平臺上通常都不會提供,Flutter之因此提供,我想最大的緣由,可能就是自定義起來特別麻煩吧。
    • 佈局行爲

      AspectRatio的佈局行爲分爲兩種狀況:學習

      • AspectRatio首先會在佈局限制條件容許的範圍內儘量的擴展,widget的高度是由寬度和比率決定的,相似於BoxFit中的contain,按照固定比率去儘可能佔滿區域。
      • 若是在知足全部限制條件事後沒法找到一個可行的尺寸,AspectRatio最終將會去優先適應佈局限制條件,而忽略所設置的比率。
    • 繼承關係
      Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > AspectRatio

      從繼承關係看,AspectRatio是基礎的佈局控件。ui

    • 構造函數
      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(
            width: 200.0,
            height: 200.0,
            child: new Container(
              color: Colors.red,
              width: 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(
            width: 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,
              width: 100.0,
            ),
            LimitedBox(
              maxWidth: 150.0,
              child: new Container(
                color: Colors.blue,
                width: 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,
          width: 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佈局

相關文章
相關標籤/搜索