Flutter如何实现未聚焦时显示提示文本与标签的自定义文本框
Flutter 固定标签+hint同显文本框实现方案
Flutter 原生InputDecoration的浮动标签逻辑默认会在未输入失焦状态下让label占位到输入框内部,和hint位置冲突。要实现失焦时标签固定在输入框上方、和hint同时显示的效果,不需要修改框架源码,通过组合布局即可快速实现。
实现逻辑
- 外层用
Column做垂直排布,顶部单独放置始终显示的文本标签,标签颜色随输入框焦点状态切换 - 输入框组件仅配置
hintText属性,不设置任何label相关参数,避免触发内置的浮动标签动画 - 通过
FocusNode监听输入框焦点变化,同步更新标签、边框的显示样式,和原生Material输入框的交互体验保持一致
完整组件代码
import 'package:flutter/material.dart'; class FixedLabelTextField extends StatefulWidget { /// 输入框上方固定显示的标签 final String label; /// 输入框内部提示文本 final String hintText; final TextEditingController? controller; final TextInputType? keyboardType; final bool obscureText; final FormFieldValidator<String>? validator; const FixedLabelTextField({ super.key, required this.label, required this.hintText, this.controller, this.keyboardType, this.obscureText = false, this.validator, }); @override State<FixedLabelTextField> createState() => _FixedLabelTextFieldState(); } class _FixedLabelTextFieldState extends State<FixedLabelTextField> { late final FocusNode _focusNode; bool _hasFocus = false; @override void initState() { super.initState(); _focusNode = FocusNode(); _focusNode.addListener(_handleFocusChange); } @override void dispose() { _focusNode.removeListener(_handleFocusChange); _focusNode.dispose(); super.dispose(); } void _handleFocusChange() { setState(() { _hasFocus = _focusNode.hasFocus; }); } @override Widget build(BuildContext context) { final primaryColor = Theme.of(context).colorScheme.primary; return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( widget.label, style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: _hasFocus ? primaryColor : Colors.grey.shade700, ), ), const SizedBox(height: 8), TextFormField( focusNode: _focusNode, controller: widget.controller, keyboardType: widget.keyboardType, obscureText: widget.obscureText, validator: widget.validator, decoration: InputDecoration( hintText: widget.hintText, hintStyle: TextStyle( fontSize: 14, color: Colors.grey.shade400, ), contentPadding: const EdgeInsets.symmetric( horizontal: 12, vertical: 14, ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: Colors.grey.shade300, width: 1, ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: primaryColor, width: 2, ), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: Colors.red.shade400, width: 1, ), ), focusedErrorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: Colors.red.shade400, width: 2, ), ), ), ), ], ); } }
调用示例
// 页面中直接引入组件即可使用 FixedLabelTextField( label: "Email", hintText: "Enter your email address", keyboardType: TextInputType.emailAddress, validator: (val) { if (val == null || !val.contains('@')) return 'Please enter valid email'; return null; }, )
效果说明:
- 未聚焦状态下,输入框上方标签、框内hint文本同时展示,完全匹配需求
- 聚焦时标签自动切换为主题色,输入框边框高亮,交互逻辑和原生输入框无差异
- 保留了原生输入框的所有能力,包括表单校验、密码模式、输入格式限制等,可直接复用业务逻辑
内容的提问来源于stack exchange,提问作者Kavishka Rajapakshe
相关产品推荐
相关产品推荐

