Flutter:键盘弹出时如何让Widget(2)自动占满屏幕并隐藏Widget(1)
Flutter 键盘弹出时自动调整组件占比需求
需求:键盘弹出时,强制让下方表单区域(Widget(2))填充几乎整个屏幕,隐藏上方欢迎文本区域(Widget(1))。目前手动将Widget(2)的flex改为9能达到预期效果,但需要实现键盘弹出/收起时自动切换该比例。
组件代码
class RegistrationForm extends StatelessWidget { const RegistrationForm({super.key}); @override Widget build(BuildContext context) { return Scaffold( resizeToAvoidBottomInset: true, body: Container( color: Colors.green, child: Form( child: Column( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ Flexible( flex: 1, child: Container( padding: const EdgeInsets.only( top: 60, left: 30, right: 30, bottom: 5, ), height: 240, child: const Text( "Welcome, please fill your data", style: TextStyle(fontSize: 35), ), ), ), Flexible( flex: 3, child: SingleChildScrollView( child: Container( padding: const EdgeInsets.symmetric( horizontal: 35.0, vertical: 50, ), decoration: const BoxDecoration( color: Colors.black, borderRadius: BorderRadius.only( topLeft: Radius.circular(40), topRight: Radius.circular(40), )), child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ const Padding( padding: EdgeInsets.symmetric(vertical: 20), child: TextField( decoration: InputDecoration(labelText: "Name"), ), ), const Padding( padding: EdgeInsets.symmetric(vertical: 20), child: TextField( decoration: InputDecoration(labelText: "Email"), ), ), const Padding( padding: EdgeInsets.symmetric(vertical: 20), child: TextField( decoration: InputDecoration(labelText: "Password"), ), ), const Padding( padding: EdgeInsets.symmetric(vertical: 20), child: TextField( decoration: InputDecoration(labelText: "Repeat password"), ), ), Padding( padding: EdgeInsets.symmetric(vertical: 40), child: ElevatedButton( child: Text( "Register", style: TextStyle(color: Colors.green), ), onPressed: () => print("register"), ), ) ], ), ), ), ), ], ), ), ), ); } }
行为对比
无键盘时默认效果

当前键盘弹出时效果

预期效果

实现方案
要实现自动切换,核心是监听键盘弹出/收起状态,动态调整两个Flexible的flex值,需将组件改为StatefulWidget维护状态:
修改后完整代码
class RegistrationForm extends StatefulWidget { const RegistrationForm({super.key}); @override State<RegistrationForm> createState() => _RegistrationFormState(); } class _RegistrationFormState extends State<RegistrationForm> with WidgetsBindingObserver { // 默认无键盘时的flex比例 int _topFlex = 1; int _bottomFlex = 3; @override void initState() { super.initState(); // 添加键盘监听 WidgetsBinding.instance.addObserver(this); } @override void dispose() { // 移除监听避免内存泄漏 WidgetsBinding.instance.removeObserver(this); super.dispose(); } @override void didChangeMetrics() { super.didChangeMetrics(); // 通过屏幕底部内边距判断键盘状态 final bottomInset = WidgetsBinding.instance.window.viewInsets.bottom; setState(() { if (bottomInset > 0) { // 键盘弹出,调整比例让表单区域占满屏幕 _topFlex = 1; _bottomFlex = 9; // 若需完全隐藏上方区域,可设置_topFlex=0 // _topFlex = 0; // _bottomFlex = 1; } else { // 键盘收起,恢复默认比例 _topFlex = 1; _bottomFlex = 3; } }); } @override Widget build(BuildContext context) { return Scaffold( resizeToAvoidBottomInset: true, body: Container( color: Colors.green, child: Form( child: Column( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ Flexible( flex: _topFlex, // 可选:当topFlex为0时完全隐藏组件 child: Visibility( visible: _topFlex != 0, child: Container( padding: const EdgeInsets.only( top: 60, left: 30, right: 30, bottom: 5, ), height: 240, child: const Text( "Welcome, please fill your data", style: TextStyle(fontSize: 35), ), ), ), ), Flexible( flex: _bottomFlex, child: SingleChildScrollView( child: Container( padding: const EdgeInsets.symmetric( horizontal: 35.0, vertical: 50, ), decoration: const BoxDecoration( color: Colors.black, borderRadius: BorderRadius.only( topLeft: Radius.circular(40), topRight: Radius.circular(40), )), child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ const Padding( padding: EdgeInsets.symmetric(vertical: 20), child: TextField( decoration: InputDecoration(labelText: "Name"), ), ), const Padding( padding: EdgeInsets.symmetric(vertical: 20), child: TextField( decoration: InputDecoration(labelText: "Email"), ), ), const Padding( padding: EdgeInsets.symmetric(vertical: 20), child: TextField( decoration: InputDecoration(labelText: "Password"), ), ), const Padding( padding: EdgeInsets.symmetric(vertical: 20), child: TextField( decoration: InputDecoration(labelText: "Repeat password"), ), ), Padding( padding: const EdgeInsets.symmetric(vertical: 40), child: ElevatedButton( child: const Text( "Register", style: TextStyle(color: Colors.green), ), onPressed: () => print("register"), ), ) ], ), ), ), ), ], ), ), ), ); } }
方案说明
- 通过
WidgetsBindingObserver的didChangeMetrics方法监听屏幕尺寸变化,利用viewInsets.bottom判断键盘是否弹出(值大于0则键盘显示)。 - 动态更新两个
Flexible的flex值,配合Visibility组件可完全隐藏上方区域,或保留小比例空间。 - 务必在
dispose中移除监听,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Librain
相关产品推荐
相关产品推荐

