欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

flutter布局約束原理深入解析

 更新時間:2023年01月13日 09:14:23   作者:半點橘色  
這篇文章主要為大家介紹了flutter布局約束原理深入解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

剛開始接觸flutter的時候,Container組件是用得最多的。它就像HTML中的div一樣普遍,專門用來布局頁面的。

但是使用Container嵌套布局的時候,經(jīng)常出現(xiàn)一些令人無法理解的問題。就如下面代碼,在一個固定的容器中,子組件卻鋪滿了全屏。

/// 例一
@override
Widget build(BuildContext context) {
   return Container(
     width: 300,
     height: 300,
     color: Colors.amber,
     child: Container(width: 50, height: 50, color: Colors.red,),
  );
}

然后要加上alignment屬性,子組件正常顯示了,但容器還是鋪滿全屏。

/// 例二
@override
Widget build(BuildContext context) {
   return Container(
     width: 300,
     height: 300,
     color: Colors.amber,
     alignment: Alignment.center,
     child: Container(width: 50, height: 50, color: Colors.red,),
  );
}

而在容器外層添加一個Scaffold組件,它就正常顯示了。

/// 例三
@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Container(
      width: 300,
      height: 300,
      color: Colors.amber,
      alignment: Alignment.center,
      child: Container(width: 50, height: 50, color: Colors.red,),
    ),
  );
}

這一切的怪異行為困擾了我很久,直到我深入了flutter布局的學(xué)習(xí),才漸漸解開這些疑惑。

1、flutter的widget類型

flutter的widget可以分為三類,組合類ComponentWidget代理類ProxyWidget繪制類RenderObjectWidget

組合類:如Container、Scaffold、MaterialApp還有一系列通過繼承StatelessWidgetStatefulWidget的類。組合類是我們開發(fā)過程中用得最多的組件。

代理類InheritedWidget,功能型組件,它可以高效快捷的實現(xiàn)共享數(shù)據(jù)的跨組件傳遞。如常見的ThemeMediaQuery就是InheritedWidget的應(yīng)用。

繪制類:屏幕上看到的UI幾乎都會通過RenderObjectWidget實現(xiàn)。通過繼承它,可以進(jìn)行界面的布局和繪制。如Align、Padding、ConstrainedBox等都是通過繼承RenderObjectWidget,并通過重寫createRenderObject方法來創(chuàng)建RenderObject對象,實現(xiàn)最終的布局(layout)和繪制(paint)。

2、Container是個組合類

顯而易見Container繼承StatelessWidget,它是一個組合類,同時也是一個由DecoratedBoxConstrainedBox、TransformPadding、Align等組件組合的多功能容器??梢酝ㄟ^查看Container類,看出它實際就是通過不同的參數(shù)判斷,再進(jìn)行組件的層層嵌套來實現(xiàn)的。

@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()),
    );
  } else 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!;
}

組合類基本不參與ui的繪制,都是通過繪制類的組合來實現(xiàn)功能。

3、flutter布局約束

flutter中有兩種布局約束BoxConstraints盒約束和SliverConstraints線性約束,如Align、Padding、ConstrainedBox使用的是盒約束。

BoxConstraints盒約束是指flutter框架在運(yùn)行時遍歷整個組件樹,在這過程中 「向下傳遞約束,向上傳遞尺寸」,以此來確定每個組件的尺寸和大小。

BoxConstraints類由4個屬性組成,最小寬度minWidth、最大寬度maxWidth、最小高度minHeight、最大高度maxHeight

BoxConstraints({
    this.minWidth,
    this.maxWidth,
    this.minHeight,
    this.maxHeight,
});

根據(jù)這4個屬性的變化,可以分為“緊約束(tight)”、“松約束(loose)”、“無界約束”、“有界約束”。

緊約束:最小寬(高)度和最大寬(高)度值相等,此時它是一個固定寬高的約束。

BoxConstraints.tight(Size size)
    : minWidth = size.width,
      maxWidth = size.width,
      minHeight = size.height,
      maxHeight = size.height;

松約束:最小寬(高)值為0,最大寬(高)大于0,此時它是一個約束范圍。

BoxConstraints.loose(Size size)
    : minWidth = 0.0,
      maxWidth = size.width,
      minHeight = 0.0,
      maxHeight = size.height;

無界約束:最小寬(高)和最大寬(高)值存在double.infinity(無限)。

BoxConstraints.expand({double? width, double? height}) 
: minWidth = width ?? double.infinity,
     maxWidth = width ?? double.infinity,
     minHeight = height ?? double.infinity,
     maxHeight = height ?? double.infinity;

有界約束:最小寬(高)和最大寬(高)值均為固定值。

BoxConstraints(100, 300, 100, 300)

4、Container布局行為解惑

了解了BoxConstraints布局約束,回到本文最開始的問題。

/// 例一
@override
Widget build(BuildContext context) {
   return Container(
     width: 300,
     height: 300,
     color: Colors.amber,
     child: Container(width: 50, height: 50, color: Colors.red,),
  );
}

例一中,兩個固定寬高的Container,為什么子容器鋪滿了全屏?

根據(jù)BoxConstraints布局約束,遍歷整個組件樹,最開始的root是樹的起點,它向下傳遞的是一個緊約束。因為是移動設(shè)備,root即是屏幕的大小,假設(shè)屏幕寬414、高896。于是整個布局約束如下:

這里有個問題,就是Container分明已經(jīng)設(shè)置了固定寬高,為什么無效?

因為父級向下傳遞的約束,子組件必須嚴(yán)格遵守。這里Container容器設(shè)置的寬高超出了父級的約束范圍,就會自動被忽略,采用符合約束的值。

例一兩上Container都被鋪滿屏幕,而最底下的紅色Container疊到了最上層,所以最終顯示紅色。

/// 例二
@override
Widget build(BuildContext context) {
   return Container(
     width: 300,
     height: 300,
     color: Colors.amber,
     alignment: Alignment.center,
     child: Container(width: 50, height: 50, color: Colors.red,),
  );
}

例二也同樣可以根據(jù)布局約束求證,如下圖:

這里Container為什么是ConstrainedBoxAlign組件?前面說過Container是一個組合組件,它是由多個原子組件組成的。根據(jù)例二,它是由ConstrainedBox和Align嵌套而成。

Align提供給子組件的是一個松約束,所以容器自身設(shè)置50寬高值是在合理范圍的,因此生效,屏幕上顯示的就是50像素的紅色方塊。ConstrainedBox受到的是緊約束,所以自身的300寬高被忽略,顯示的是鋪滿屏幕的黃色塊。

/// 例三
@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Container(
      width: 300,
      height: 300,
      color: Colors.amber,
      alignment: Alignment.center,
      child: Container(width: 50, height: 50, color: Colors.red,),
    ),
  );
}

例三中Scaffold向下傳遞的是一個松約束,所以黃色Container的寬高根據(jù)自身設(shè)置的300,在合理的范圍內(nèi),有效。Container再向下傳遞的也是松約束,最終紅色Container寬高為50。

這里還有個問題,怎么確定組件向下傳遞的是緊約束還是松約束?

這就涉及到組件的內(nèi)部實現(xiàn)了,這里通過Align舉個例。

Align是一個繪制組件,它能夠進(jìn)行界面的布局和繪制,這是因為Align的繼承鏈為:

Align -> SingleChildRenderObjectWidget -> RenderObjectWidget

Align需要重寫createRenderObject方法,返回RenderObject的實現(xiàn),這里Align返回的是RenderPositionedBox,所以核心內(nèi)容就在這個類中

class Align extends SingleChildRenderObjectWidget {
    /// ...
    @override
    RenderPositionedBox createRenderObject(BuildContext context) {
      return RenderPositionedBox(
        alignment: alignment,
        widthFactor: widthFactor,
        heightFactor: heightFactor,
        textDirection: Directionality.maybeOf(context),
      );
    }
    /// ...
}

而RenderPositionedBox類中,重寫performLayout方法,該方法用于根據(jù)自身約束條件,計算出子組件的布局,再根據(jù)子組件的尺寸設(shè)置自身的尺寸,形成一個至下而上,由上到下的閉環(huán),最終實現(xiàn)界面的整個繪制。

RenderPositionedBox -> RenderAligningShiftedBox -> RenderShiftedBox -> RenderBox

class RenderPositionedBox extends RenderAligningShiftedBox {
    /// ...
    @override
    void performLayout() {
      final BoxConstraints constraints = this.constraints; // 自身的約束大小
      final bool shrinkWrapWidth = _widthFactor != null || constraints.maxWidth == double.infinity;
      final bool shrinkWrapHeight = _heightFactor != null || constraints.maxHeight == double.infinity;
      /// 存在子組件
      if (child != null) {
        /// 開始布局子組件
        child!.layout(constraints.loosen(), parentUsesSize: true);
        /// 根據(jù)子組件的尺寸設(shè)置自身尺寸
        size = constraints.constrain(Size(
          shrinkWrapWidth ? child!.size.width * (_widthFactor ?? 1.0) : double.infinity,
          shrinkWrapHeight ? child!.size.height * (_heightFactor ?? 1.0) : double.infinity,
        ));
        /// 計算子組件的位置
        alignChild();
      } else {
        /// 不存在子組件
        size = constraints.constrain(Size(
          shrinkWrapWidth ? 0.0 : double.infinity,
          shrinkWrapHeight ? 0.0 : double.infinity,
        ));
      }
    }
    /// ...
}

根據(jù)Align中performLayout方法的實現(xiàn),可以確定該組件最終會給子組件傳遞一個怎么樣的約束。

/// constraints.loosen提供的是一個松約束
child!.layout(constraints.loosen(), parentUsesSize: true);
/// loosen方法
BoxConstraints loosen() {
  assert(debugAssertIsValid());
  /// BoxConstraints({double minWidth = 0.0, double maxWidth = double.infinity, double minHeight = 0.0, double maxHeight = double.infinity})
  return BoxConstraints(
    maxWidth: maxWidth,
    maxHeight: maxHeight,
  );
}

其它繪制類的組件基本跟Align大同小異,只要重點看performLayout方法的實現(xiàn),即可判斷出組件提供的約束條件。

總結(jié)

1、flutter的widget分為,組合類、代理類和繪制類。

2、Container是一個組合類,由DecoratedBox、ConstrainedBox、Transform、Padding、Align等繪制組件組合而成。

3、flutter中有兩種布局約束BoxConstraints盒約束和SliverConstraints線性約束。

4、BoxConstraints的約束原理是: 「向下傳遞約束,向上傳遞尺寸」。

5、BoxConstraints的約束類型為:緊約束、松約束、無界約束、有界約束。

6、判斷一個繪制組件的約束行為可以通過查看performLayout方法中l(wèi)ayout傳入的約束值。

以上就是flutter布局約束原理深入解析的詳細(xì)內(nèi)容,更多關(guān)于flutter布局約束原理的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Navigation?Bundle實現(xiàn)兩個Fragment參數(shù)傳遞

    Navigation?Bundle實現(xiàn)兩個Fragment參數(shù)傳遞

    這篇文章主要為大家介紹了Navigation?Bundle實現(xiàn)兩個Fragment參數(shù)傳遞,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • Android應(yīng)用開發(fā)的版本更新檢測升級功能實現(xiàn)示例

    Android應(yīng)用開發(fā)的版本更新檢測升級功能實現(xiàn)示例

    本文對Android版本更新的知識做全面的總結(jié),主要包括開發(fā)中版本的設(shè)置,如何檢測本程序的版本,版本的更新判斷和顯示,新版本程序的安裝
    2022-04-04
  • Android通過自定義View實現(xiàn)隨機(jī)驗證碼

    Android通過自定義View實現(xiàn)隨機(jī)驗證碼

    這篇文章主要介紹了Android通過自定義View實現(xiàn)隨機(jī)驗證碼的相關(guān)資料,需要的朋友可以參考下
    2016-03-03
  • FragmentTabHost使用方法詳解

    FragmentTabHost使用方法詳解

    這篇文章主要為大家詳細(xì)介紹了Android中FragmentTabHost的使用方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • Android自定義view實現(xiàn)仿抖音點贊效果

    Android自定義view實現(xiàn)仿抖音點贊效果

    這篇文章主要介紹了Android自定義view實現(xiàn)仿抖音點贊效果,代碼簡單易懂非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2018-05-05
  • android RecyclerView的一些優(yōu)化點介紹

    android RecyclerView的一些優(yōu)化點介紹

    大家好,本篇文章主要講的是android RecyclerView的一些優(yōu)化點介紹,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽
    2021-12-12
  • Android開發(fā)之AppWidget詳解

    Android開發(fā)之AppWidget詳解

    這篇文章主要介紹了Android開發(fā)之AppWidget詳解,想了解桌面程序AppWidget的同學(xué)可以參考下
    2021-04-04
  • Kotlin開發(fā)中open關(guān)鍵字與類名函數(shù)名和變量名的使用方法淺析

    Kotlin開發(fā)中open關(guān)鍵字與類名函數(shù)名和變量名的使用方法淺析

    這篇文檔中,我們將解釋如何以及為什么將 open 關(guān)鍵字與類名、函數(shù)名和變量名一起使用,了解內(nèi)部原理是為了幫助我們做擴(kuò)展,同時也是驗證了一個人的學(xué)習(xí)能力,如果你想讓自己的職業(yè)道路更上一層樓,這些底層的東西你是必須要會的
    2023-02-02
  • Android仿360市場下載按鈕的實現(xiàn)方法

    Android仿360市場下載按鈕的實現(xiàn)方法

    這篇文章主要給大家介紹了關(guān)于利用Android實現(xiàn)360市場下載按鈕效果的方法,文中給出了詳細(xì)的示例代碼供大家參考學(xué)習(xí),并在文末給出了源碼供大家下載,需要的朋友們下面跟著小編一起來學(xué)習(xí)學(xué)習(xí)吧。
    2017-05-05
  • Android使用硬件加速的方式

    Android使用硬件加速的方式

    硬件加速是指利用設(shè)備的硬件資源來加速圖形渲染和圖像處理等操作,以提高應(yīng)用程序的性能和用戶體驗,Android使用硬件加速的目的是為了提高圖形渲染的性能和效果,本文給大家詳細(xì)介紹了Android如何使用硬件加速,需要的朋友可以參考下
    2023-10-10

最新評論