You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 18:57:43