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

Flutter中TextFormField密码字段完全隐藏:设置obscureText=true仍显示输入字符

Flutter彻底隐藏TextFormField密码输入(无短暂显示)

默认设置obscureText: true时,Flutter的TextFormField会短暂显示刚输入的字符,要实现完全隐藏所有输入字符的效果,可以用以下两种方案:

方案1:自定义TextEditingController(推荐)

这个方案把显示的掩码文本和真实密码分离,输入过程中始终只显示掩码,不会暴露任何输入内容:

class ObscuredTextController extends TextEditingController {
  final String obscureChar;
  String _realPassword = '';

  ObscuredTextController({this.obscureChar = '•'});

  @override
  TextSpan buildTextSpan({required BuildContext context, TextStyle? style, required bool withComposing}) {
    // 始终渲染全掩码的文本
    return TextSpan(style: style, text: obscureChar * _realPassword.length);
  }

  @override
  set text(String newText) {
    _realPassword = newText;
    super.text = obscureChar * newText.length;
  }

  @override
  String get text => _realPassword;

  @override
  void updateEditingValue(TextEditingValue newValue) {
    final textLengthDelta = newValue.text.length - value.text.length;
    if (textLengthDelta > 0) {
      // 新增字符:追加到真实密码
      _realPassword += newValue.text.substring(value.text.length);
    } else if (textLengthDelta < 0) {
      // 删除字符:截断真实密码
      _realPassword = _realPassword.substring(0, _realPassword.length + textLengthDelta);
    }
    // 更新显示内容为全掩码
    super.updateEditingValue(TextEditingValue(
      text: obscureChar * _realPassword.length,
      selection: TextSelection.collapsed(offset: _realPassword.length),
    ));
  }
}

使用示例:

final passwordController = ObscuredTextController();

TextFormField(
  controller: passwordController,
  decoration: const InputDecoration(labelText: '密码'),
  // 提交时直接通过controller.text获取真实密码
  onSaved: (value) {
    final realPassword = passwordController.text;
    // 处理密码逻辑
  },
)

方案2:自定义InputFormatter

如果不想自定义控制器,可以用输入格式化器实时替换输入内容为掩码,同时保留真实密码:

class ObscureInputFormatter extends TextInputFormatter {
  final String obscureChar;
  final StringBuffer _realPassword = StringBuffer();

  ObscureInputFormatter({this.obscureChar = '•'});

  @override
  TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
    final lengthDelta = newValue.text.length - oldValue.text.length;
    if (lengthDelta > 0) {
      // 记录新增的真实字符
      _realPassword.write(newValue.text.substring(oldValue.text.length));
    } else if (lengthDelta < 0) {
      // 删除字符时截断真实密码
      final newRealLength = _realPassword.length + lengthDelta;
      final tempText = _realPassword.toString().substring(0, newRealLength);
      _realPassword.clear();
      _realPassword.write(tempText);
    }
    // 返回掩码后的显示文本
    return TextEditingValue(
      text: obscureChar * _realPassword.length,
      selection: TextSelection.collapsed(offset: _realPassword.length),
    );
  }

  // 获取真实密码的方法
  String get realPassword => _realPassword.toString();
}

使用示例:

final obscureFormatter = ObscureInputFormatter();

TextFormField(
  inputFormatters: [obscureFormatter],
  decoration: const InputDecoration(labelText: '密码'),
  onFieldSubmitted: (_) {
    final realPassword = obscureFormatter.realPassword;
    // 处理密码逻辑
  },
)

两种方案都能彻底避免输入字符的短暂显示,方案1的控制器更便于全局管理和获取真实密码,方案2适合局部场景使用。

内容的提问来源于stack exchange,提问作者Suretion

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 13:30:41