Flutter-实现中间留白,空间不足支持滚动效果

2022-12-23  本文已影响0人  Codepgq

前言

在日常开发中我们可能会遇到这种设计,上半部分的内容是固定的,然后中间是空白,下面是按钮,当屏幕空间显示不下的时候,就压缩空白区域,如果空白区域被压缩完了,就整体滚动(或者上半部分滚动)。

如图所示

布局

根据上面的示意图,随意创建几个Widget出来,效果如下

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('自动压缩空白区域 '),
      ),
      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('我是按钮')),
    );
  }
}
默认布局

上面的代码就把布局给做出来了,但是是不符合设计要求的效果的,接下来就是完成设计的效果。

在Flutter里面,空白可以用Spacer来表示,这个小部件会撑满剩余的空间,所以我们就可以在Column中新增一个Spacer。效果如下

Column(
    children: [
        ...
        const Spacer(),
        ...
    ]
)
小屏幕 大屏 看起来一切安好,直到遇到特小屏设备 image-20221224092643703.png

上面这个报错,对于Flutter开发来说那不是家常便饭,解决起来也和喝水一样简单,你可能马上就会想到SingleChildScrollView包裹起来,然后。。。你就会发现啥都没有了

SingleChildScrollView

这个也很好理解,就是Spacer惹的祸。

方案一:底部固定,上半部分滚动

对于上面那种情况,我们可以在外层在套用一个Column,同时把Button提到外面这个Column中,就可以完成这个设计

...
body:Column(
        children: [
          Expanded(
            child: SingleChildScrollView(
              child: Column(
                children: [
                  _textField('请输入账号(手机/邮箱)'),
                  _textField('请输入验证码'),
                  _textField('请输入密码'),
                ],
              ),
            ),
          ),
          _button(),
        ],
     ),
...      

image-20221224093354270.png

这个很好理解,把Column分为两个部分,一个是扩展区域,另外一个是按钮的空间,扩展区域(上半部分)在用一个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,意思就是要不要动态计算最大可用范围,默认是开启的。我们把它关闭,然后再观察一下下面这个gif


fillRemaining-2.gif

到这里应该就知道如何处理了,把button放在SliverFillRemaining中,然后调整一下他的布局方式就可以完成上面的效果

...
body: CustomScrollView(
      slivers: [
        SliverToBoxAdapter(
          child: Column(
            children: [
              _textField('请输入账号(手机/邮箱)'),
              _textField('请输入验证码'),
              _textField('请输入密码'),
            ],
          ),
        ),

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

结尾

到这里就可以根据要求完成上面的效果了,由于代码量很少,就不上传git了,贴一下全部代码吧

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('自动压缩空白区域 '),
      ),
      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('我是按钮')),
    );
  }
}
上一篇下一篇

猜你喜欢

热点阅读