Flutter:无需封装StatefulWidget实现TextField聚焦背景变色
实现TextField聚焦切换背景色的简便方案
针对大量输入框的场景,无需为每个输入框单独创建StatefulWidget,推荐以下两种高效实现方式:
方法1:封装可复用的无状态组件(基于焦点监听)
创建通用的FocusableTextField组件,内部通过FocusNode监听焦点变化,结合ValueNotifier动态更新背景色,所有输入框可直接复用该组件:
class FocusableTextField extends StatelessWidget { final FocusNode? focusNode; final String labelText; final Color normalBgColor; final Color focusedBgColor; const FocusableTextField({ super.key, this.focusNode, required this.labelText, this.normalBgColor = const Color(0xFFF5F5F5), this.focusedBgColor = const Color(0xFFE3F2FD), }); @override Widget build(BuildContext context) { final bgColorNotifier = ValueNotifier<Color>(normalBgColor); final effectiveFocusNode = focusNode ?? FocusNode(); effectiveFocusNode.addListener(() { bgColorNotifier.value = effectiveFocusNode.hasFocus ? focusedBgColor : normalBgColor; }); return ValueListenableBuilder<Color>( valueListenable: bgColorNotifier, builder: (context, color, child) { return TextFormField( focusNode: effectiveFocusNode, decoration: InputDecoration( labelText: labelText, filled: true, fillColor: color, focusedBorder: const UnderlineInputBorder( borderSide: BorderSide(color: Colors.blue, width: 3), ), ), ); }, ); } }
使用示例:
// 多个输入框直接复用,无需额外状态管理 FocusableTextField(labelText: '用户名'), FocusableTextField(labelText: '密码'), FocusableTextField(labelText: '邮箱'),
方法2:全局主题包裹(统一配置所有输入框)
如果希望表单内所有输入框的聚焦样式完全统一,可通过自定义Theme结合焦点监听实现,仅需在外层包裹一次:
class FocusThemeForm extends StatefulWidget { final Widget child; const FocusThemeForm({super.key, required this.child}); @override State<FocusThemeForm> createState() => _FocusThemeFormState(); } class _FocusThemeFormState extends State<FocusThemeForm> { FocusNode? _currentFocus; @override void initState() { super.initState(); FocusScope.of(context).addListener(_updateFocusState); } void _updateFocusState() { setState(() { _currentFocus = FocusScope.of(context).focusedChild; }); } @override void dispose() { FocusScope.of(context).removeListener(_updateFocusState); super.dispose(); } @override Widget build(BuildContext context) { return Theme( data: Theme.of(context).copyWith( inputDecorationTheme: InputDecorationTheme( filled: true, fillColor: _currentFocus?.hasFocus == true ? const Color(0xFFE3F2FD) : const Color(0xFFF5F5F5), focusedBorder: const UnderlineInputBorder( borderSide: BorderSide(color: Colors.blue, width: 3), ), ), ), child: widget.child, ); } }
使用示例:
FocusThemeForm( child: Column( children: [ TextFormField(decoration: const InputDecoration(labelText: '输入框1')), TextFormField(decoration: const InputDecoration(labelText: '输入框2')), // 更多输入框... ], ), )
方案说明
- 方法1适合需要单独配置每个输入框聚焦颜色的场景,通过组件复用减少重复代码;
- 方法2适合全局统一样式的表单,仅需外层包裹一次,所有内部输入框自动应用聚焦效果;
- 两种方案都避免了为每个输入框创建独立StatefulWidget的繁琐操作。
内容的提问来源于stack exchange,提问作者Farsen
相关产品推荐
相关产品推荐

