Flutter-實現(xiàn)中間留白,空間不足支持滾動效果

前言

在日常開發(fā)中我們可能會遇到這種設(shè)計,上半部分的內(nèi)容是固定的,然后中間是空白,下面是按鈕,當屏幕空間顯示不下的時候,就壓縮空白區(qū)域,如果空白區(qū)域被壓縮完了,就整體滾動(或者上半部分滾動)。

如圖所示

布局

根據(jù)上面的示意圖,隨意創(chuàng)建幾個Widget出來,效果如下

class AutoResizeSpacer extends StatelessWidget {
  const AutoResizeSpacer({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('自動壓縮空白區(qū)域 '),
      ),
      body: Column(
        children: [
          _textField('請輸入賬號(手機/郵箱)'),
          _textField('請輸入驗證碼'),
          _textField('請輸入密碼'),
          _button(),
        ],
      ),
    );
  }

  Widget _textField(String labelText) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: TextField(
        decoration: InputDecoration(labelText: labelText),
      ),
    );
  }

  Widget _button() {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: ElevatedButton(onPressed: () {}, child: const Text('我是按鈕')),
    );
  }
}
默認布局

上面的代碼就把布局給做出來了,但是是不符合設(shè)計要求的效果的,接下來就是完成設(shè)計的效果。

在Flutter里面,空白可以用Spacer來表示,這個小部件會撐滿剩余的空間,所以我們就可以在Column中新增一個Spacer。效果如下

Column(
    children: [
        ...
        const Spacer(),
        ...
    ]
)
小屏幕
大屏

看起來一切安好,直到遇到特小屏設(shè)備
image-20221224092643703.png

上面這個報錯,對于Flutter開發(fā)來說那不是家常便飯,解決起來也和喝水一樣簡單,你可能馬上就會想到SingleChildScrollView包裹起來,然后。。。你就會發(fā)現(xiàn)啥都沒有了

SingleChildScrollView

這個也很好理解,就是Spacer惹的禍。

方案一:底部固定,上半部分滾動

對于上面那種情況,我們可以在外層在套用一個Column,同時把Button提到外面這個Column中,就可以完成這個設(shè)計

...
body:Column(
        children: [
          Expanded(
            child: SingleChildScrollView(
              child: Column(
                children: [
                  _textField('請輸入賬號(手機/郵箱)'),
                  _textField('請輸入驗證碼'),
                  _textField('請輸入密碼'),
                ],
              ),
            ),
          ),
          _button(),
        ],
     ),
...      

image-20221224093354270.png

這個很好理解,把Column分為兩個部分,一個是擴展區(qū)域,另外一個是按鈕的空間,擴展區(qū)域(上半部分)在用一個SIngleChildScrollView進行包裹,當空間不夠展示完整的child的時候,就可以滾動。

方案二:整體滾動

可以使用CustomScrollView,在配合SliverFillRemaining來完成。


sliver.gif

使用之前先來了解一下SliverFillRemaining這個widget,點進去看到他的官方說明:A sliver that contains a single box child that fills the remaining space in the viewport.,會填充一整屏(一整個viewport)的空間,可以用下面的gif來理解

fillRemaining.gif

SliverFillRemaining里面有一個屬性是:hasScrollBody,意思就是要不要動態(tài)計算最大可用范圍,默認是開啟的。我們把它關(guān)閉,然后再觀察一下下面這個gif


fillRemaining-2.gif

到這里應(yīng)該就知道如何處理了,把button放在SliverFillRemaining中,然后調(diào)整一下他的布局方式就可以完成上面的效果

...
body: CustomScrollView(
      slivers: [
        SliverToBoxAdapter(
          child: Column(
            children: [
              _textField('請輸入賬號(手機/郵箱)'),
              _textField('請輸入驗證碼'),
              _textField('請輸入密碼'),
            ],
          ),
        ),

        SliverFillRemaining(
          hasScrollBody: false,
          child: Align(
            alignment: Alignment.bottomCenter,
            child: _button(),
          ),
        ),
      ],
    )
...

結(jié)尾

到這里就可以根據(jù)要求完成上面的效果了,由于代碼量很少,就不上傳git了,貼一下全部代碼吧

class AutoResizeSpacer extends StatelessWidget {
  const AutoResizeSpacer({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('自動壓縮空白區(qū)域 '),
      ),
      body: _body2(),
    );
  }

  Widget _body2() {
    return CustomScrollView(
      slivers: [
        SliverToBoxAdapter(
          child: Column(
            children: [
              _textField('請輸入賬號(手機/郵箱)'),
              _textField('請輸入驗證碼'),
              _textField('請輸入密碼'),
            ],
          ),
        ),

        SliverFillRemaining(
          hasScrollBody: false,
          child: Align(
            alignment: Alignment.bottomCenter,
            child: _button(),
          ),
        ),
      ],
    );
  }

  Widget _body1() {
    return Column(
      children: [
        Expanded(
          child: SingleChildScrollView(
            child: Column(
              children: [
                _textField('請輸入賬號(手機/郵箱)'),
                _textField('請輸入驗證碼'),
                _textField('請輸入密碼'),
              ],
            ),
          ),
        ),
        _button(),
      ],
    );
  }

  Widget _textField(String labelText) {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: TextField(
        decoration: InputDecoration(labelText: labelText),
      ),
    );
  }

  Widget _button() {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: ElevatedButton(onPressed: () {}, child: const Text('我是按鈕')),
    );
  }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容