Flutter中如何实现键盘弹出时修改指定Widget的显示状态
功能实现说明
该需求完全可以实现,核心逻辑是通过监听键盘弹出时的系统布局偏移判断键盘状态,动态控制logo组件的显隐即可,具体实现方案如下:
实现步骤
- 首先通过
MediaQuery.of(context).viewInsets.bottom判断键盘状态:该值大于0代表键盘弹出,等于0代表键盘收起 - 根据键盘状态,动态控制logo组件是否渲染,可搭配动画实现平滑的显隐过渡
- 注意保持Scaffold的
resizeToAvoidBottomInset属性为默认的true,否则无法正确检测键盘高度变化 - 建议外层嵌套
SingleChildScrollView,避免键盘弹出后表单内容溢出报错
修改后的完整代码
import 'package:flutter/material.dart'; import 'package:flutter_signin_button/button_list.dart'; import 'package:tembea/components/rounded_button.dart'; import 'package:tembea/constants.dart'; import 'package:flutter_signin_button/flutter_signin_button.dart'; class RegistrationScreen extends StatelessWidget { const RegistrationScreen({Key? key}) : super(key: key); static const String id = 'registration_screen'; @override Widget build(BuildContext context) { // 检测键盘是否弹出 final bool isKeyboardOpen = MediaQuery.of(context).viewInsets.bottom > 0; return Scaffold( backgroundColor: Colors.black, // 保持默认true,可响应键盘变化调整页面布局 resizeToAvoidBottomInset: true, body: Padding( padding: const EdgeInsets.symmetric(horizontal: 24.0), // 嵌套SingleChildScrollView防止内容溢出 child: SingleChildScrollView( physics: const BouncingScrollPhysics(), child: SizedBox( // 高度占满屏幕,保证表单垂直居中 height: MediaQuery.of(context).size.height, child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // 键盘弹出时隐藏logo,加淡入淡出动画更丝滑 AnimatedOpacity( opacity: isKeyboardOpen ? 0 : 1, duration: const Duration(milliseconds: 200), child: isKeyboardOpen ? const SizedBox.shrink() : Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Hero( tag: 'logo', child: SizedBox( height: 100, child: Image.asset('images/logo.png'), ), ), ], ), ), // 动态调整logo下方的间距 AnimatedSize( duration: const Duration(milliseconds: 200), alignment: Alignment.topCenter, child: SizedBox(height: isKeyboardOpen ? 0 : 20.0,), ), TextField( textAlign: TextAlign.center, style: const TextStyle( color: Colors.white, ), onChanged: (value){}, decoration: kInputDecoration.copyWith( hintText: 'Enter username', ), ), const SizedBox( height: 20.0, ), TextField( textAlign: TextAlign.center, style: const TextStyle( color: Colors.white, ), onChanged: (value){}, decoration: kInputDecoration.copyWith( hintText: 'Enter email', ), ), const SizedBox( height: 20.0, ), TextField( textAlign: TextAlign.center, obscureText: true, style: const TextStyle( color: Colors.white, ), onChanged: (value){}, decoration: kInputDecoration.copyWith( hintText: 'Enter password', ), ), const SizedBox( height: 20.0, ), TextField( textAlign: TextAlign.center, obscureText: true, style: const TextStyle( color: Colors.white, ), onChanged: (value){}, decoration: kInputDecoration.copyWith( hintText: 'Confirm password', ), ), const SizedBox( height: 20.0, ), RoundedButton( buttonName: 'Register', color: Colors.lightGreen, onPressed: (){}, ), const SizedBox( height: 20, ), SignInButton( Buttons.Google, text: 'Sign Up with Google', onPressed: (){}, ), const SizedBox( height: 20, ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text( 'Already have an account?', style: TextStyle( color: Colors.white, ), ), const SizedBox( width: 5.0, ), TextButton( onPressed: (){}, child: const Text( 'Sign In', style: TextStyle( color: Colors.green, ), ), ), ], ), ], ), ), ), ), ); } }
额外优化说明
上述代码同时修复了原代码中两处布局问题:
- 原代码中
Flexible组件直接作为Padding的子组件,不符合Flex组件的使用规范,会触发布局报错 - 原代码中
Row内的间距用了SizedBox(height:5),应该改为宽度间距,避免出现错位问题
内容的提问来源于stack exchange,提问作者Jimmyjbk
相关产品推荐
相关产品推荐

