前言
在日常开发中我们可能会遇到这种设计,上半部分的内容是固定的,然后中间是空白,下面是按钮,当屏幕空间显示不下的时候,就压缩空白区域,如果空白区域被压缩完了,就整体滚动(或者上半部分滚动)。
布局
根据上面的示意图,随意创建几个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(),
...
]
)
上面这个报错,对于Flutter开发来说那不是家常便饭,解决起来也和喝水一样简单,你可能马上就会想到SingleChildScrollView包裹起来,然后。。。你就会发现啥都没有了
这个也很好理解,就是Spacer惹的祸。
方案一:底部固定,上半部分滚动
对于上面那种情况,我们可以在外层在套用一个Column,同时把Button提到外面这个Column中,就可以完成这个设计
...
body:Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
_textField('请输入账号(手机/邮箱)'),
_textField('请输入验证码'),
_textField('请输入密码'),
],
),
),
),
_button(),
],
),
...
这个很好理解,把Column分为两个部分,一个是扩展区域,另外一个是按钮的空间,扩展区域(上半部分)在用一个SIngleChildScrollView进行包裹,当空间不够展示完整的child的时候,就可以滚动。
方案二:整体滚动
可以使用CustomScrollView,在配合SliverFillRemaining来完成。
使用之前先来了解一下SliverFillRemaining这个widget,点进去看到他的官方说明:A sliver that contains a single box child that fills the remaining space in the viewport.
,会填充一整屏(一整个viewport)的空间,可以用下面的gif来理解
SliverFillRemaining里面有一个属性是:hasScrollBody,意思就是要不要动态计算最大可用范围,默认是开启的。我们把它关闭,然后再观察一下下面这个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('我是按钮')),
);
}
}