<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>布局 on 首页 | Widegram</title>
    <link>https://widegram.com/categories/%E5%B8%83%E5%B1%80/</link>
    <description>Recent content in 布局 on 首页 | Widegram</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Wed, 03 Nov 2021 16:15:00 +0000</lastBuildDate><atom:link href="https://widegram.com/categories/%E5%B8%83%E5%B1%80/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Flutter 布局全解</title>
      <link>https://widegram.com/2021/11/flutter-layout/</link>
      <pubDate>Wed, 03 Nov 2021 16:15:00 +0000</pubDate>
      
      <guid>https://widegram.com/2021/11/flutter-layout/</guid>
      <description>一、初识布局 1.1 约束（Terminology）的向下传递 ​	Flutter从一个runApp函数开始运行，例如：
void main() { runApp(//1.向下Container传递紧约束：屏幕尺寸 Container(//2.向下Center传递约束，由于自身的w和h无效，因此原封不动地传递 width: 0.04,//1.来自App的约束使width失效，w=页面宽度 height: 0.08,//1.来自App的约束使height失效，h=页面高度 color: Colors.red[200], child: Center(//3.向下Container传递松约束，下限设为0 child: Container(//4.向下FlutterLogo传递紧约束：200&amp;lt;=h,w&amp;lt;=200 color: Colors.white, width: 200,//3.满足Center的约束，w=200 height: 200,//3.满足Center的约束，h=200 child: FlutterLogo( size: 10//不满足约束条件，失效，size=200 ), ), ) )); } 上面这段代码的运行结果如下图：
​	通过读取LayoutBuilder的constrants参数，我们可以得到来自父组件的约束区间：
LayoutBuilder(builder: (context, constrants) { print(&amp;#34;constrants: $constrants&amp;#34;); return FlutterLogo(size: 10); }), ​	我们可以通过Align系列控件来放松约束，例如使用Center。
1.2 SizedBox和FractionallySizedBox ​	我们可以使用SizedBox和FractionallySizedBox来确定组件的大小，其中后者使用一个比例关系来约束。
​	但如果SizedBox本身受到一个紧约束，那么它也无法突破。
​	目前我们只能使用Align系列Widgets（比如Center）来将紧约束转换为松约束，例如将200&amp;lt;=h,w&amp;lt;=200转化为0&amp;lt;=h,w&amp;lt;=200。
Container( //向下SizedBox传递紧约束：200&amp;lt;=h,w&amp;lt;=200 width: 200, height: 200, child: SizedBox( //收到Container的紧约束 height: 100,//失效 width: 100,//失效 child: FlutterLogo(size: 10), //不满足约束条件，失效，size=200 ) 1.</description>
      <content:encoded><![CDATA[<h3 id="一初识布局">一、初识布局</h3>
<h4 id="11-约束terminology的向下传递">1.1 约束（Terminology）的向下传递</h4>
<p>​		Flutter从一个runApp函数开始运行，例如：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() {
</span></span><span style="display:flex;"><span>  runApp(<span style="color:#75715e">//1.向下Container传递紧约束：屏幕尺寸
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    Container(<span style="color:#75715e">//2.向下Center传递约束，由于自身的w和h无效，因此原封不动地传递
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      width: <span style="color:#ae81ff">0.04</span>,<span style="color:#75715e">//1.来自App的约束使width失效，w=页面宽度
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      height: <span style="color:#ae81ff">0.08</span>,<span style="color:#75715e">//1.来自App的约束使height失效，h=页面高度
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      color: Colors.red[<span style="color:#ae81ff">200</span>],
</span></span><span style="display:flex;"><span>      child: Center(<span style="color:#75715e">//3.向下Container传递松约束，下限设为0
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        child: Container(<span style="color:#75715e">//4.向下FlutterLogo传递紧约束：200&lt;=h,w&lt;=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          color: Colors.white,
</span></span><span style="display:flex;"><span>          width: <span style="color:#ae81ff">200</span>,<span style="color:#75715e">//3.满足Center的约束，w=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          height: <span style="color:#ae81ff">200</span>,<span style="color:#75715e">//3.满足Center的约束，h=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          child: FlutterLogo(
</span></span><span style="display:flex;"><span>            size: <span style="color:#ae81ff">10</span><span style="color:#75715e">//不满足约束条件，失效，size=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          ),
</span></span><span style="display:flex;"><span>        ),
</span></span><span style="display:flex;"><span>      )
</span></span><span style="display:flex;"><span>    ));
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>上面这段代码的运行结果如下图：</p>
<img src="https://i.loli.net/2021/11/03/pEhd8VH92mYLa6B.png" alt="截屏2021-11-03 上午10.33.40" style="zoom:25%;" />
<p>​		通过读取<code>LayoutBuilder</code>的constrants参数，我们可以得到来自父组件的约束区间：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>LayoutBuilder(builder: (context, constrants) {
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;constrants: </span><span style="color:#e6db74">$</span>constrants<span style="color:#e6db74">&#34;</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> FlutterLogo(size: <span style="color:#ae81ff">10</span>);
</span></span><span style="display:flex;"><span>}),
</span></span></code></pre></div><p>​		我们可以通过<code>Align</code>系列控件来放松约束，例如使用<code>Center</code>。</p>
<h4 id="12-sizedbox和fractionallysizedbox">1.2 SizedBox和FractionallySizedBox</h4>
<p>​		我们可以使用<code>SizedBox</code>和<code>FractionallySizedBox</code>来确定组件的大小，其中后者使用一个比例关系来约束。</p>
<p>​		但如果<code>SizedBox</code>本身受到一个紧约束，那么它也无法突破。</p>
<p>​		目前我们只能使用<code>Align</code>系列Widgets（比如<code>Center</code>）来将紧约束转换为松约束，例如将<code>200&lt;=h,w&lt;=200</code>转化为<code>0&lt;=h,w&lt;=200</code>。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container(
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//向下SizedBox传递紧约束：200&lt;=h,w&lt;=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  width: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>  height: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>  child: SizedBox(  <span style="color:#75715e">//收到Container的紧约束
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    height: <span style="color:#ae81ff">100</span>,<span style="color:#75715e">//失效
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    width: <span style="color:#ae81ff">100</span>,<span style="color:#75715e">//失效
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    child: FlutterLogo(size: <span style="color:#ae81ff">10</span>), <span style="color:#75715e">//不满足约束条件，失效，size=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  )
</span></span></code></pre></div><h4 id="13-constrainedbox">1.3 ConstrainedBox</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container( <span style="color:#75715e">//向下传递紧约束：h,w=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  color: Colors.black,
</span></span><span style="display:flex;"><span>  width: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>  height: <span style="color:#ae81ff">500</span>,
</span></span><span style="display:flex;"><span>  child: Center(<span style="color:#75715e">//向下传递松约束：0&lt;=h,w&lt;=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    child: ConstrainedBox(<span style="color:#75715e">//重新定义约束区间（同样，收到上级约束的限制）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      constraints: <span style="color:#66d9ef">const</span> BoxConstraints(
</span></span><span style="display:flex;"><span>        minWidth: <span style="color:#ae81ff">60</span>,
</span></span><span style="display:flex;"><span>        maxWidth: <span style="color:#66d9ef">double</span>.infinity,<span style="color:#75715e">//无限大，被上级限制为200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        minHeight: <span style="color:#ae81ff">60</span>,
</span></span><span style="display:flex;"><span>        maxHeight: <span style="color:#ae81ff">120</span>,
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>      child: LayoutBuilder(builder: (context, constraints) {
</span></span><span style="display:flex;"><span>        print(<span style="color:#e6db74">&#34;</span><span style="color:#e6db74">$</span>constraints<span style="color:#e6db74">&#34;</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//flutter: BoxConstraints(60.0&lt;=w&lt;=120.0, 60.0&lt;=h&lt;=120.0)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">const</span> FlutterLogo(size: <span style="color:#ae81ff">150</span>);<span style="color:#75715e">//150被限制回120
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      }),
</span></span><span style="display:flex;"><span>    ),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><p>​		<code>ConstrainedBox</code>可以重新定义约束区间，但新定义的约束区间必须在父约束之内。我们可以使用<code>loosen()</code>函数取消<code>minWidth</code>和<code>minHeight</code>，也就是将它们设置为0.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container( <span style="color:#75715e">//向下传递紧约束：h,w=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  color: Colors.black,
</span></span><span style="display:flex;"><span>  width: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>  height: <span style="color:#ae81ff">500</span>,
</span></span><span style="display:flex;"><span>  child: Center(<span style="color:#75715e">//向下传递松约束：0&lt;=h,w&lt;=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    child: ConstrainedBox(<span style="color:#75715e">//重新定义约束区间（同样，收到上级约束的限制）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      constraints: <span style="color:#66d9ef">const</span> BoxConstraints(
</span></span><span style="display:flex;"><span>        minWidth: <span style="color:#ae81ff">60</span>,
</span></span><span style="display:flex;"><span>        maxWidth: <span style="color:#66d9ef">double</span>.infinity,<span style="color:#75715e">//无限大，被上级限制为200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        minHeight: <span style="color:#ae81ff">60</span>,
</span></span><span style="display:flex;"><span>        maxHeight: <span style="color:#ae81ff">120</span>,
</span></span><span style="display:flex;"><span>      ).loosen(),	<span style="color:#75715e">//变为松约束（下限为0）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      child: LayoutBuilder(builder: (context, constraints) {
</span></span><span style="display:flex;"><span>        print(<span style="color:#e6db74">&#34;</span><span style="color:#e6db74">$</span>constraints<span style="color:#e6db74">&#34;</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//flutter: BoxConstraints(0.0&lt;=w&lt;=120.0, 0.0&lt;=h&lt;=120.0)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">const</span> FlutterLogo(size: <span style="color:#ae81ff">150</span>);<span style="color:#75715e">//150被限制回120
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      }),
</span></span><span style="display:flex;"><span>    ),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><ul>
<li>紧约束：最大值等于最小值的约束；</li>
<li>松约束：最小值等于0的约束；</li>
<li>既是松约束，又是紧约束的约束：最大值与最小值都是0的约束。</li>
</ul>
<p>同时，</p>
<ul>
<li>有界约束：有设置最大值的约束；</li>
<li>无界约束：最大值是无限大的约束，例如无限长的ListView。</li>
</ul>
<h4 id="14-padding">1.4 Padding</h4>
<p>Padding组件可以收缩Container的紧约束，但仍向下传递一个紧约束。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container(
</span></span><span style="display:flex;"><span>  color: Colors.black,
</span></span><span style="display:flex;"><span>  width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>  height: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>  child: Padding(
</span></span><span style="display:flex;"><span>    padding: <span style="color:#66d9ef">const</span> EdgeInsets.all(<span style="color:#ae81ff">20.0</span>),
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//上下同时设置20的边框，收缩了Container的紧约束
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    child: LayoutBuilder(builder: (context, constraints) {
</span></span><span style="display:flex;"><span>      print(<span style="color:#e6db74">&#34;</span><span style="color:#e6db74">$</span>constraints<span style="color:#e6db74">&#34;</span>);
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">//BoxConstraints(w=260.0, h=260.0)，这里仍然是一个紧约束
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">const</span> FlutterLogo(size: <span style="color:#ae81ff">150</span>);<span style="color:#75715e">//150仍然失效
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    }),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><h3 id="二弹性布局column和flexible">二、弹性布局——Column和Flexible</h3>
<h4 id="21-column和directionality">2.1 Column和Directionality</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Scaffold(
</span></span><span style="display:flex;"><span>  body: Center(
</span></span><span style="display:flex;"><span>    child: Container(
</span></span><span style="display:flex;"><span>      color: Colors.red[<span style="color:#ae81ff">200</span>],
</span></span><span style="display:flex;"><span>      width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>      child: Column(
</span></span><span style="display:flex;"><span>        mainAxisAlignment: MainAxisAlignment.spaceAround,
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//主轴环绕留白
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        crossAxisAlignment: CrossAxisAlignment.start,
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//交叉轴：从起始处（一般从左到右）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        <span style="color:#75715e">//某些语言的阅读顺序是从右向左，可以使用Directionality调整
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        children: <span style="color:#66d9ef">const</span> [
</span></span><span style="display:flex;"><span>          FlutterLogo(size:<span style="color:#ae81ff">150</span>),
</span></span><span style="display:flex;"><span>          FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>          FlutterLogo(size: <span style="color:#ae81ff">150</span>),
</span></span><span style="display:flex;"><span>        ],)),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/Gh1aBpITdjw5lvA.png" alt="截屏2021-11-03 上午11.34.26" style="zoom:25%;" />
<p>Column还有一些其他属性，如<code>mainAxisSize</code>和<code>CrossAxisAlignment.stretch</code>：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Scaffold(
</span></span><span style="display:flex;"><span>  body: Center(
</span></span><span style="display:flex;"><span>    child: Container(
</span></span><span style="display:flex;"><span>      color: Colors.red[<span style="color:#ae81ff">200</span>],
</span></span><span style="display:flex;"><span>      child: Column(
</span></span><span style="display:flex;"><span>        mainAxisSize: MainAxisSize.min,<span style="color:#75715e">//设置主轴方向的长度，默认max
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        mainAxisAlignment: MainAxisAlignment.spaceAround,
</span></span><span style="display:flex;"><span>        crossAxisAlignment: CrossAxisAlignment.stretch,
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//交叉轴：拉伸至与父限制一致（类型交叉轴的expanded）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        children: <span style="color:#66d9ef">const</span> [
</span></span><span style="display:flex;"><span>          FlutterLogo(size:<span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>          FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>          FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>        ],)),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/RFAs3YZqIyvbn7g.png" alt="截屏2021-11-03 上午11.38.30" style="zoom:25%;" />
<h4 id="22-expanded">2.2 Expanded</h4>
<p>使用<code>Expanded</code>组件尽可能多地填满剩余空间：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Center(
</span></span><span style="display:flex;"><span>  child: Container(
</span></span><span style="display:flex;"><span>    color: Colors.red[<span style="color:#ae81ff">200</span>],
</span></span><span style="display:flex;"><span>    child: Column(
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">//交叉轴：拉伸至与父限制一致（类型交叉轴的expand）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      children: [
</span></span><span style="display:flex;"><span>        FlutterLogo(size:<span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>        FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>        Expanded(child: Container(color: Colors.black)),
</span></span><span style="display:flex;"><span>        FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>      ],)),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/eksSJ6NKMBdh4oL.png" style="zoom:25%;" />
<p>通过指定<code>flex</code>参数设置两个<code>Expanded</code>之间的比例：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Center(
</span></span><span style="display:flex;"><span>  child: Container(
</span></span><span style="display:flex;"><span>    color: Colors.red[<span style="color:#ae81ff">200</span>],
</span></span><span style="display:flex;"><span>    child: Column(
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">//交叉轴：拉伸至与父限制一致（类型交叉轴的expand）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      children: [
</span></span><span style="display:flex;"><span>        FlutterLogo(size:<span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>        Expanded(flex:<span style="color:#ae81ff">1</span>, child: Container(color: Colors.green)),
</span></span><span style="display:flex;"><span>        FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>        Expanded(flex:<span style="color:#ae81ff">2</span>, child: Container(color: Colors.green)),
</span></span><span style="display:flex;"><span>        FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>      ],)),
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/JMw7WFI8U2kabcB.png" alt="截屏2021-11-03 上午11.46.01" style="zoom:25%;" />
<p><code>Expanded</code>组件的本质是<code>Flexible</code>组件，例如：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Expanded(
</span></span><span style="display:flex;"><span>  child: Container(color: Colors.green)
</span></span><span style="display:flex;"><span>),
</span></span><span style="display:flex;"><span><span style="color:#75715e">//等价于
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>Flexible(
</span></span><span style="display:flex;"><span>  flex:<span style="color:#ae81ff">1</span>,
</span></span><span style="display:flex;"><span>  child: Container(color: Colors.green)
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><ul>
<li>
<p>使用Flexible时，Flutter首先会渲染Column中固定的项目，再用剩余的空间分比例占取。</p>
</li>
<li>
<p>Flutter在布局其中的非弹性控件时（例如FlutterLogo），会先假装自己是一个无界约束，例如</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Column(
</span></span><span style="display:flex;"><span>  children: [
</span></span><span style="display:flex;"><span>    LayoutBuilder(builder: (_, con) {
</span></span><span style="display:flex;"><span>      print(<span style="color:#e6db74">&#39;</span><span style="color:#e6db74">$</span>con<span style="color:#e6db74">&#39;</span>);
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">//flutter: BoxConstraints(0.0&lt;=w&lt;=375.0, 0.0&lt;=h&lt;=Infinity)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      <span style="color:#66d9ef">return</span> FlutterLogo(size: <span style="color:#ae81ff">50</span>);
</span></span><span style="display:flex;"><span>    }),
</span></span><span style="display:flex;"><span>    Expanded(flex: <span style="color:#ae81ff">1</span>, child: Container(color: Colors.green)),
</span></span><span style="display:flex;"><span>    FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>    Expanded(flex: <span style="color:#ae81ff">2</span>, child: Container(color: Colors.green)),
</span></span><span style="display:flex;"><span>    FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>这是由于Column没有办法将自己受到的限制提前告知FlutterLogo，为了尽可能地不限制FlutterLogo，Column会传入一个Infinity“假装”自己是一个无界控件，但如果真的超出了限制，则会提示错误。</p>
</li>
</ul>
<h4 id="23-column中的listview">2.3 Column中的ListView</h4>
<p>倘若我们直接在Column中插入ListView，则会提示错误：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Column(
</span></span><span style="display:flex;"><span>  children: [
</span></span><span style="display:flex;"><span>    FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>    ListView(children: [	<span style="color:#75715e">//报错！
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      <span style="color:#66d9ef">for</span>(<span style="color:#66d9ef">int</span> i<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span>;i<span style="color:#f92672">&lt;</span><span style="color:#ae81ff">10</span>;i<span style="color:#f92672">++</span>) Text(<span style="color:#e6db74">&#34;ListView item </span><span style="color:#e6db74">$</span>i<span style="color:#e6db74">&#34;</span>)
</span></span><span style="display:flex;"><span>    ]),
</span></span><span style="display:flex;"><span>    FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>正如我们上面提到的那样，Column在渲染时会假装自己是一个无界控件。Column的渲染分成两个阶段：</p>
<ol>
<li>
<p>渲染固定组件（非<code>Flexible</code>组件）</p>
</li>
<li>
<p>将剩余的空间分配给children中的各个弹性（也就是<code>Flexible</code>）组件</p>
</li>
</ol>
<p>​		而在上面的代码中，<code>ListView</code>不是一个Flexible组件，所以它参与第一个阶段的渲染，它发现自己受到的约束是Infinity（请参考2.2节的最后一段代码）。而ListView本身也是一个Infinity组件，于是它就真的使用Infinity的空间，超出了Column实际受到的限制（比如：屏幕尺寸），于是就会产生内容溢出的错误。</p>
<p>​		因此，如果我们需要在Column中使用ListView，就需要给予ListView限制，可以用Expanded包裹ListView：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Column(
</span></span><span style="display:flex;"><span>  children: [
</span></span><span style="display:flex;"><span>    FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>    Expanded(	<span style="color:#75715e">//使用Expanded包裹ListView
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      child: ListView(children: [
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span>(<span style="color:#66d9ef">int</span> i<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span>;i<span style="color:#f92672">&lt;</span><span style="color:#ae81ff">100</span>;i<span style="color:#f92672">++</span>) Text(<span style="color:#e6db74">&#34;ListView item </span><span style="color:#e6db74">$</span>i<span style="color:#e6db74">&#34;</span>)
</span></span><span style="display:flex;"><span>      ]),
</span></span><span style="display:flex;"><span>    ),
</span></span><span style="display:flex;"><span>    FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/X4p1wmiqUKI62yh.png" alt="截屏2021-11-03 下午12.12.23" style="zoom:25%;" />
<h3 id="三层叠组件stack">三、层叠组件——Stack</h3>
<h4 id="31-stack组件">3.1 Stack组件</h4>
<p>用下面一段代码展示Stack的作用：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Center(
</span></span><span style="display:flex;"><span>  child: Container(
</span></span><span style="display:flex;"><span>    color: Colors.grey, <span style="color:#75715e">//可以看到Stack本身的大小，受到children的影响
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    child: Stack(
</span></span><span style="display:flex;"><span>      alignment: Alignment(<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">0</span>),<span style="color:#75715e">//等价于Alignment.center
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      children: [
</span></span><span style="display:flex;"><span>        FlutterLogo(size: <span style="color:#ae81ff">100</span>),
</span></span><span style="display:flex;"><span>        Text(<span style="color:#e6db74">&#34;111&#34;</span>,style: TextStyle(fontSize: <span style="color:#ae81ff">150</span>)),
</span></span><span style="display:flex;"><span>        Positioned(
</span></span><span style="display:flex;"><span>          left: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>          top: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>          child: Container(color: Colors.red,width: <span style="color:#ae81ff">30</span>,height: <span style="color:#ae81ff">30</span>)
</span></span><span style="display:flex;"><span>        ),
</span></span><span style="display:flex;"><span>      ],),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/y8FeoVPAhQ2UYjT.png" style="zoom:25%;" />
<h4 id="32-stack的渲染过程">3.2 Stack的渲染过程</h4>
<p>​		与Column组件类似，Stack中的成员分成两类：有位置的（也就是<code>Positioned</code>）和没有位置的。</p>
<p>​		在渲染的过程中Stack如何确定其自身的大小呢？</p>
<ol>
<li>Stack会首先将没有位置的组件全部渲染出来，并以此确定其自身的大小；</li>
<li>然后再按照位置渲染<code>Positioned</code>组件。</li>
</ol>
<p>​		我们再看一个例子：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container(
</span></span><span style="display:flex;"><span>  color: Colors.grey,
</span></span><span style="display:flex;"><span>  child: Stack(
</span></span><span style="display:flex;"><span>    alignment: Alignment(<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">0</span>),
</span></span><span style="display:flex;"><span>    children: [
</span></span><span style="display:flex;"><span>      Positioned(	<span style="color:#75715e">//Stack本身的大小与Positioned组件无关！
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        left: <span style="color:#ae81ff">0</span>, bottom: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>        child: FlutterLogo(size: <span style="color:#ae81ff">300</span>)
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>      Text(<span style="color:#e6db74">&#34;111&#34;</span>,style: TextStyle(fontSize: <span style="color:#ae81ff">150</span>)),
</span></span><span style="display:flex;"><span>      Positioned(
</span></span><span style="display:flex;"><span>        left: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>        top: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>        child: Container(color: Colors.red,width: <span style="color:#ae81ff">30</span>,height: <span style="color:#ae81ff">30</span>)
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    ],),
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>也因此，FlutterLogo没有被完整渲染出来：</p>
<p>​	<img src="https://i.loli.net/2021/11/03/zSFwXPbdurvoVOB.png" style="zoom:25%;" /></p>
<p>​		我们可以指定<code>fit属性</code>来将父组件的约束传给下级组件。不是让<code>Stack</code>本身扩展，而是让其中的<code>children</code>受到<code>Stack</code>上级组件的约束，<code>Stack</code>一般也会受到影响，但那只是一个副作用：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Stack(
</span></span><span style="display:flex;"><span>  fit: StackFit.expand,
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container(
</span></span><span style="display:flex;"><span>	constraints: BoxConstraints(
</span></span><span style="display:flex;"><span>  	minWidth:<span style="color:#ae81ff">30</span>,
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>  child: Stack(
</span></span><span style="display:flex;"><span>  	fit: StackFit.passthrough,
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//直接将上级的非紧约束传给children
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  )
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><h4 id="33-positioned组件">3.3 Positioned组件</h4>
<p>首先，Positioned会强行要求其中的位置信息达成，也就是间接地确定了其中控件的大小：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container(
</span></span><span style="display:flex;"><span>  color: Colors.grey,
</span></span><span style="display:flex;"><span>  child: Stack(
</span></span><span style="display:flex;"><span>    alignment: Alignment(<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">0</span>),
</span></span><span style="display:flex;"><span>    children: [
</span></span><span style="display:flex;"><span>      Positioned(
</span></span><span style="display:flex;"><span>        left: <span style="color:#ae81ff">0</span>,	<span style="color:#75715e">//假如我们既设置了left，又设置了right，会怎么样呢？
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        right: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>        child: Container(color: Colors.blue, width: <span style="color:#ae81ff">30</span>, height: <span style="color:#ae81ff">30</span>)
</span></span><span style="display:flex;"><span>        		<span style="color:#75715e">//答案是：Container本身的w和h将会失效！
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      ),
</span></span><span style="display:flex;"><span>      Text(<span style="color:#e6db74">&#34;111&#34;</span>,style: TextStyle(fontSize: <span style="color:#ae81ff">150</span>)),
</span></span><span style="display:flex;"><span>      Positioned(
</span></span><span style="display:flex;"><span>        left: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>        top: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>        child: Container(color: Colors.red,width: <span style="color:#ae81ff">30</span>,height: <span style="color:#ae81ff">30</span>)
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    ],),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/3EeR86ctANnlgHp.png" alt="截屏2021-11-03 下午12.43.16" style="zoom:25%;" />
<p>​		另外，如果我们定义了Positioned，但却没有给它传递任何信息，那么Stack就会将Positioned忽略，此时Positioned里面的组件将会参与第一阶段的渲染，也就是会影响Stack本身的大小。</p>
<p>​		我们可以通过指定<code>clipBehavior</code>属性来确定超出部分是否会被裁减：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container(
</span></span><span style="display:flex;"><span>  color: Colors.grey, <span style="color:#75715e">//可以看到Stack本身的大小，受到children的影响
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  child: Stack(
</span></span><span style="display:flex;"><span>    clipBehavior: Clip.none,<span style="color:#75715e">//默认Clip.hardEdge
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//等价于旧版本的overflow: Overflow.visible,
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    alignment: Alignment(<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">0</span>),<span style="color:#75715e">//等价于Alignment.center
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    children: [
</span></span><span style="display:flex;"><span>      Positioned(
</span></span><span style="display:flex;"><span>        left: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>        width: <span style="color:#ae81ff">300</span>, <span style="color:#75715e">//溢出边框啦！
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        child: Container(color: Colors.blue, width: <span style="color:#ae81ff">30</span>, height: <span style="color:#ae81ff">30</span>)
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>      Text(<span style="color:#e6db74">&#34;111&#34;</span>,style: TextStyle(fontSize: <span style="color:#ae81ff">150</span>)),
</span></span><span style="display:flex;"><span>      Positioned(
</span></span><span style="display:flex;"><span>        left: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>        top: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>        child: Container(color: Colors.red,width: <span style="color:#ae81ff">30</span>,height: <span style="color:#ae81ff">30</span>)
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    ],),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/VTzH7MPkRSw8csg.png" alt="截屏2021-11-03 下午12.52.04" style="zoom:25%;" />
<p>​		需要注意的是，如果控件的尺寸超出了Stack本身的尺寸，那么在Stack外的点击事件将不会生效。因为那里的点击事件不会发送给Stack，那么也就不会传送给Stack的children。</p>
<p>​		另外，即使设置了<code>Clip.hardEdge</code>，对于Transform指定的变换位移，超出部分依然不会被裁减，这也是因为Flutter的优化机制导致的。</p>
<h3 id="四再谈container">四、再谈Container</h3>
<h4 id="41-container语法糖">4.1 Container语法糖</h4>
<p>​		一般来说，Container会传递一个紧约束，我们可以通过Align系列组件放松约束：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//很繁琐
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>Container(
</span></span><span style="display:flex;"><span>  color: Colors.orange,
</span></span><span style="display:flex;"><span>  width: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>  height: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>  child: <span style="color:#66d9ef">const</span> Padding(
</span></span><span style="display:flex;"><span>    padding: EdgeInsets.all(<span style="color:#ae81ff">24.0</span>),
</span></span><span style="display:flex;"><span>    child: Align(
</span></span><span style="display:flex;"><span>      alignment: Alignment.topLeft,
</span></span><span style="display:flex;"><span>      child: FlutterLogo(),
</span></span><span style="display:flex;"><span>    ),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>​		这样做实在太繁琐，我们可以直接使用Container中的<code>padding</code>和<code>alignment</code>属性，这是Flutter提供的语法糖：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//与上面的代码等价
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>Container(
</span></span><span style="display:flex;"><span>  color: Colors.orange,
</span></span><span style="display:flex;"><span>  width: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>  height: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>  padding: <span style="color:#66d9ef">const</span> EdgeInsets.all(<span style="color:#ae81ff">24.0</span>),
</span></span><span style="display:flex;"><span>  alignment: Alignment.topLeft,
</span></span><span style="display:flex;"><span>  child: <span style="color:#66d9ef">const</span> FlutterLogo(),
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>另外，还有一些其他的语法糖，例如<code>color</code>、<code>decoration</code>、<code>foregroundDecoration</code>、<code>constraints</code>对应<code>ColoredBox</code>、<code>DecoratedBox</code>、<code>ConstrainedBox</code>，<code>height/width</code>对应<code>SizedBox</code>。</p>
<ul>
<li>color =&gt; ColoredBox</li>
<li>decoration / foregroundDecoration =&gt; DecoratedBox</li>
<li>constraints =&gt; ConstrainedBox</li>
<li>clipBehavior =&gt; Clip</li>
<li>height/width =&gt; SizedBox</li>
<li>margin =&gt; Padding 添加在Container外</li>
<li>padding =&gt; Padding 添加在child外，Container内</li>
<li>transform =&gt; Transform组件</li>
</ul>
<h4 id="42-container行为">4.2 Container行为</h4>
<p>​		一般来说，如果给Container指定一个child，那么Container的大小就会与其child一致，除非您设置了alignment属性。否则就会直接占满父组件。</p>
<p>​		而如果Container在诸如Column这样主轴无界的组件内，那么它的该轴上的长度会被调整为0。</p>
<p>​		使用margin属性时也会影响Container的大小。</p>
<p>​		<code>Container</code>如何知道自己所在的组件是否有界呢？这里可以参考<code>LimitedBox</code>组件，当出现某个尺寸无穷大的情况，会自动缩减成<code>maxHeight</code>，而如果不是infinity的情况，该轴的尺寸则可以超过<code>maxHeight</code>，也就是<code>LimitedBox</code>此时无任何效果，因此，这里的<code>maxHeight</code>其实可以等同于<code>Placeholder</code>中的<code>fallbackHeight</code>属性。</p>
<p>​		更多信息，可以参考Container的源代码。</p>
<p>​</p>
<h3 id="五custommultichildlayout">五、CustomMultiChildLayout</h3>
<h4 id="51-制作一个自己的stack">5.1 制作一个自己的Stack</h4>
<p>我们可以使用<code>CustomMultiChildLayout</code>组件自制一个简易的Stack：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>CustomMultiChildLayout(
</span></span><span style="display:flex;"><span>  delegate: MyDelegate(),
</span></span><span style="display:flex;"><span>  children: [
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//标记其中的组件，使得MyDelegate中可以找到这些组件
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//LayoutID与Positioned类似，只能在这里使用
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//* 这里的Z-Order由children顺序决定，与MyDelegate无关！
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    LayoutId(id: <span style="color:#ae81ff">1</span>, child: FlutterLogo()),
</span></span><span style="display:flex;"><span>    LayoutId(id: <span style="color:#ae81ff">2</span>, child: FlutterLogo()),
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>自定义一个MyDelegate类作为代理：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyDelegate</span> <span style="color:#66d9ef">extends</span> MultiChildLayoutDelegate {
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> performLayout(Size size) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (hasChild(<span style="color:#ae81ff">1</span>)) {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">final</span> size1 <span style="color:#f92672">=</span> layoutChild(
</span></span><span style="display:flex;"><span>          <span style="color:#ae81ff">1</span>, <span style="color:#75715e">//1号组件layout，并得到它的size（会想：向下传递限制，向上传递大小）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          BoxConstraints.tight(<span style="color:#66d9ef">const</span> Size(<span style="color:#ae81ff">100</span>, <span style="color:#ae81ff">100</span>)) <span style="color:#75715e">//向下传递限制
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          );
</span></span><span style="display:flex;"><span>      positionChild(<span style="color:#ae81ff">1</span>, Offset.zero);  <span style="color:#75715e">//将1号组件放在左上角
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (hasChild(<span style="color:#ae81ff">2</span>)) {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">final</span> size2 <span style="color:#f92672">=</span> layoutChild(
</span></span><span style="display:flex;"><span>          <span style="color:#ae81ff">2</span>, <span style="color:#75715e">//2号组件layout，并得到它的size（会想：向下传递限制，向上传递大小）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          BoxConstraints.tight(<span style="color:#66d9ef">const</span> Size(<span style="color:#ae81ff">200</span>, <span style="color:#ae81ff">200</span>)) <span style="color:#75715e">//向下传递限制
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          );
</span></span><span style="display:flex;"><span>      positionChild(<span style="color:#ae81ff">2</span>, Offset.zero); <span style="color:#75715e">//将2号组件放在左上角
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//返回true表示永远需要重新更新，这个函数用作优化，这里暂不考虑
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">bool</span> shouldRelayout(_) <span style="color:#f92672">=&gt;</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/6nw7UWOLHYAGJR2.png" alt="截屏2021-11-03 下午3.28.34" style="zoom:25%;" />
#### 5.2 简易Column
我们可以通过类似方法自制一个简易的Column：
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>positionChild(<span style="color:#ae81ff">2</span>, Offset(<span style="color:#ae81ff">0</span>,size1.height)); <span style="color:#75715e">//将2号组件放在1号组件下面：类似Column
</span></span></span></code></pre></div><p>同样，我们可以设置<code>BoxConstraints.loost(size)</code>来传递松约束：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> performLayout(Size size) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> (hasChild(<span style="color:#ae81ff">1</span>)) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">final</span> size1 <span style="color:#f92672">=</span> layoutChild(
</span></span><span style="display:flex;"><span>      <span style="color:#ae81ff">1</span>, <span style="color:#75715e">//1号组件layout，并得到它的size（会想：向下传递限制，向上传递大小）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      BoxConstraints.loost(size) <span style="color:#75715e">//向下传递限制
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    );
</span></span><span style="display:flex;"><span>    positionChild(<span style="color:#ae81ff">1</span>, Offset.zero);  <span style="color:#75715e">//将1号组件放在左上角
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> (hasChild(<span style="color:#ae81ff">2</span>)) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">final</span> size2 <span style="color:#f92672">=</span> layoutChild(
</span></span><span style="display:flex;"><span>      <span style="color:#ae81ff">2</span>, <span style="color:#75715e">//2号组件layout，并得到它的size（会想：向下传递限制，向上传递大小）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      BoxConstraints.tight(<span style="color:#66d9ef">const</span> Size(<span style="color:#ae81ff">200</span>, <span style="color:#ae81ff">200</span>)) <span style="color:#75715e">//向下传递限制
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    );
</span></span><span style="display:flex;"><span>    positionChild(<span style="color:#ae81ff">2</span>, Offset.zero); <span style="color:#75715e">//将2号组件放在左上角
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h4 id="53-mydelegate的局限性">5.3 MyDelegate的局限性</h4>
<ol>
<li>首先，MyDelegate无法在绘制时获得其子组件的大小。通过继承<code>MultiChildLayoutDelegate</code>的<code>Size getSize(BoxConstraints c)</code>方法，我们可以得到上级组件给我们的限制，也可以向上传递我们需要的Size大小，但我们并不能通过这个函数获得子组件的大小，也就是说我们无法使得MyDelegate自适应子组件的大小。</li>
<li>我们无法在<code>performLayout</code>函数中“无中生有”，我们只能在其中画出children数组中已经存在的组件，不能在这里创造出一个新的组件。</li>
</ol>
<p>为了解决上述两个问题，我们可以使用RenderObject</p>
<h3 id="六布局原理renderobject">六、布局原理——RenderObject</h3>
<h4 id="61-简述">6.1 简述</h4>
<p>​		我们知道，平常我们在写的Stateless、Stateful组件，其实并没有将自己画在屏幕上的能力，它们只是定义了一个build方法而已。真正将内容写在屏幕上的，其实是<code>RenderObject</code>。</p>
<p>​		我们举一个例子来说明：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container(
</span></span><span style="display:flex;"><span>  color: Colors.grey,
</span></span><span style="display:flex;"><span>  child: MyRenderBox(
</span></span><span style="display:flex;"><span>    child: <span style="color:#66d9ef">const</span> FlutterLogo(size: <span style="color:#ae81ff">200</span>),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyRenderBox</span> <span style="color:#66d9ef">extends</span> SingleChildRenderObjectWidget {
</span></span><span style="display:flex;"><span>  MyRenderBox({<span style="color:#66d9ef">required</span> Widget child}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(child: child);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  RenderObject createRenderObject(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> RenderMyRenderBox();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">RenderMyRenderBox</span> <span style="color:#66d9ef">extends</span> RenderBox <span style="color:#66d9ef">with</span> RenderObjectWithChildMixin {
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//渲染pipeline：performLayout、paint、composition
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> performLayout() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//处理布局
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    child<span style="color:#f92672">!</span>.layout(constraints, <span style="color:#75715e">//也可以自定义紧约束
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        parentUsesSize: <span style="color:#66d9ef">true</span>); <span style="color:#75715e">//作为父组件需要使用子组件的size，默认是false
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>            <span style="color:#75715e">//优化用：如果不需要，子组件改动时就可以不必继续遍历渲染父组件
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//size = const Size(300, 300);
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    size <span style="color:#f92672">=</span> (child <span style="color:#f92672">as</span> RenderBox).size;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> paint(PaintingContext context, Offset offset) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//处理绘制
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    context.paintChild(child<span style="color:#f92672">!</span>, offset <span style="color:#f92672">+</span> Offset(<span style="color:#ae81ff">120</span>,<span style="color:#ae81ff">120</span>));
</span></span><span style="display:flex;"><span>    	<span style="color:#75715e">//FlutterLogo跑到MyRenderBox外面去了！
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//context.canvas.draw** //可以在这里画任意图形！
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//context.pushOpacity		//Opacity原理：使用push增加一个图层（资源耗费多）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/EnkvuWcwbIVN9Tz.png" alt="截屏2021-11-03 下午4.39.08" style="zoom:25%;" />
<p>​		在上述代码中，performLayout和paint是两次不同的遍历过程（flutter一般是深度优先），因此这里的FlutterLogo可以跑到MyRenderBox外面去！</p>
<p>​		paintChild函数是非常自由的，我们可以选择canvas.draw或者push系列函数做更多想要的事情，也可以干脆不做任何事。</p>
<h4 id="62-hot-reload原理updaterenderobject">6.2 Hot Reload原理——updateRenderObject</h4>
<p>通过自制一个ShadowRenderBox，我们可以实现阴影效果：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container(
</span></span><span style="display:flex;"><span>  color: Colors.grey,
</span></span><span style="display:flex;"><span>  child: ShadowBox(
</span></span><span style="display:flex;"><span>    distance: <span style="color:#ae81ff">20.0</span>,
</span></span><span style="display:flex;"><span>    child: <span style="color:#66d9ef">const</span> Icon(Icons.category, size: <span style="color:#ae81ff">80</span>),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">ShadowBox</span> <span style="color:#66d9ef">extends</span> SingleChildRenderObjectWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">double</span> distance;
</span></span><span style="display:flex;"><span>  ShadowBox({<span style="color:#66d9ef">required</span> Widget child, <span style="color:#66d9ef">required</span> <span style="color:#66d9ef">this</span>.distance})
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(child: child);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  RenderObject createRenderObject(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> RenderShadowBox(distance: distance);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> updateRenderObject(
</span></span><span style="display:flex;"><span>      BuildContext context, covariant RenderShadowBox renderObject) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//如果不定义这个函数，热重载时会根据key和类型判断元素是否改变，如果没有改变，则继续使用原来的renderObject，distance的值不会改变
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    renderObject.distance <span style="color:#f92672">=</span> distance;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">/*
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">	也可以使用 class RenderShadowBox extends RenderProxyBox
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">	此时，不再performLayout()和paint函数不再必须，只需要写我们需要的那个函数即可
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">*/</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">RenderShadowBox</span> <span style="color:#66d9ef">extends</span> RenderBox <span style="color:#66d9ef">with</span> RenderObjectWithChildMixin {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">double</span> distance;
</span></span><span style="display:flex;"><span>  RenderShadowBox({<span style="color:#66d9ef">required</span> <span style="color:#66d9ef">this</span>.distance});
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> performLayout() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//处理布局
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    child<span style="color:#f92672">!</span>.layout(constraints, parentUsesSize: <span style="color:#66d9ef">true</span>);
</span></span><span style="display:flex;"><span>    size <span style="color:#f92672">=</span> (child <span style="color:#f92672">as</span> RenderBox).size;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> paint(PaintingContext context, Offset offset) {
</span></span><span style="display:flex;"><span>    context.paintChild(child<span style="color:#f92672">!</span>, offset);
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//插入一个新的半透明图层
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    context.pushOpacity(offset, <span style="color:#ae81ff">127</span>, (context, offset) {
</span></span><span style="display:flex;"><span>      context.paintChild(child<span style="color:#f92672">!</span>, offset <span style="color:#f92672">+</span> Offset(distance, distance));
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/4K9Utaw3AQTY7fx.png" alt="截屏2021-11-03 下午5.01.29" style="zoom:25%;" />
<p>上面的<code>RenderShadowBox</code>可以简单地写成下面的形式：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">RenderShadowBox</span> <span style="color:#66d9ef">extends</span> RenderProxyBox {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">double</span> distance;
</span></span><span style="display:flex;"><span>  RenderShadowBox({<span style="color:#66d9ef">required</span> <span style="color:#66d9ef">this</span>.distance});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> paint(PaintingContext context, Offset offset) {
</span></span><span style="display:flex;"><span>    context.paintChild(child<span style="color:#f92672">!</span>, offset);
</span></span><span style="display:flex;"><span>    context.pushOpacity(offset, <span style="color:#ae81ff">127</span>, (context, offset) {
</span></span><span style="display:flex;"><span>      context.paintChild(child<span style="color:#f92672">!</span>, offset <span style="color:#f92672">+</span> Offset(distance, distance));
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>​		另外，也可以设置<code>DebugOverflowIndicatorMixin</code>来定义超出界限后的那个黑黄相间的错误框。那个overflow组件也只是通过<code>paintOverflowIndicator()</code>手动绘制的而已。</p>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
