Flutter登录页切换输入框焦点时已输入内容消失问题
内容丢失的核心原因
- 你在
StatelessWidget的build方法内部直接创建TextEditingController实例。build方法会在页面焦点切换、键盘弹收、尺寸变化时多次触发,每次触发都会生成全新的空控制器,旧控制器存储的输入内容会被直接丢弃,绑定的TextField会自动关联新的空控制器,直观表现就是切换焦点时输入内容消失。 - 自定义
PasswordInput组件中,手动在onChanged回调里给绑定的TextEditingController赋值的逻辑属于重复操作。Controller和TextField绑定后,输入内容会自动同步到Controller,手动赋值会额外带来光标跳变、输入状态异常的问题。
修复步骤
1. 将LoginScreen转换为StatefulWidget,把控制器的生命周期和State绑定
绝对不要在build方法内初始化需要长期持有状态的控制器,要把控制器的初始化移到State类中,和State的生命周期保持一致,同时在页面销毁时释放控制器避免内存泄漏:
class LoginScreen extends StatefulWidget { static const routeName = '/login'; const LoginScreen({super.key}); @override State<LoginScreen> createState() => _LoginScreenState(); } class _LoginScreenState extends State<LoginScreen> { // 仅在State初始化时生成一次控制器实例 final _username = TextEditingController(); final _password = TextEditingController(); @override void dispose() { // 页面销毁时释放资源 _username.dispose(); _password.dispose(); super.dispose(); } void _clearValues() { _username.clear(); _password.clear(); } @override Widget build(BuildContext context) { final deviceSize = MediaQuery.of(context).size; // 原有页面布局逻辑全部平移到这个build方法内即可,无需改动结构 return Scaffold( appBar: AppBar( title: const Text('Duckanji'), ), body: SingleChildScrollView( scrollDirection: Axis.vertical, child: Container( child: Column( children: [ Container( margin: const EdgeInsets.symmetric(vertical: 30), child: Image.asset( 'assets/img/auth_logo.png', width: deviceSize.width * 0.5, ), ), const SizedBox(height: 10), Container( height: 100, margin: const EdgeInsets.symmetric(horizontal: 20), child: TextField( textInputAction: TextInputAction.next, controller: _username, maxLength: 35, decoration: const InputDecoration( prefixIcon: Icon(Icons.account_box_rounded), border: OutlineInputBorder(), labelText: 'Username', ), ), ), PasswordInput(_password), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ FlatButton( padding: const EdgeInsets.symmetric( vertical: 10, horizontal: 15, ), color: Theme.of(context).primaryColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(5), ), onPressed: () { print(_username.text); print(_password.text); _clearValues(); }, child: const Text( 'Login', style: TextStyle( fontSize: 20, color: Colors.white, ), ), ), FlatButton( padding: const EdgeInsets.symmetric( vertical: 10, horizontal: 15, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(5), side: BorderSide( color: Theme.of(context).primaryColor, ), ), onPressed: () {}, child: Text( 'Register', style: TextStyle( fontSize: 20, color: Theme.of(context).primaryColor, ), ), ), ], ), ], ), ), ), ); } }
2. 删除PasswordInput组件中多余的onChanged赋值逻辑
TextField和TextEditingController绑定后,输入流会自动双向同步,不需要手动在onChanged里给Controller的text属性赋值,删除这段逻辑即可:
class _PasswordInputState extends State<PasswordInput> { var hiddenContent = true; @override Widget build(BuildContext context) { return Container( height: 100, margin: const EdgeInsets.symmetric(horizontal: 20), child: TextField( // 完全删除这段冗余的onChanged逻辑 // onChanged: (value) { // print(value); // widget.password.text = value; // }, textInputAction: TextInputAction.done, obscureText: hiddenContent, autocorrect: false, controller: widget.password, keyboardType: TextInputType.text, maxLength: 35, decoration: InputDecoration( prefixIcon: const Icon(Icons.lock), suffixIcon: IconButton( icon: hiddenContent ? const Icon(Icons.visibility) : const Icon(Icons.visibility_off), onPressed: () { setState(() { hiddenContent = !hiddenContent; }); }, ), border: const OutlineInputBorder(), labelText: 'Password', ), ), ); } }
内容的提问来源于stack exchange,提问作者Yusof Antar
相关产品推荐
相关产品推荐

