如何在 TextFormField 中显示/隐藏密码?

Posted

技术标签:

【中文标题】如何在 TextFormField 中显示/隐藏密码?【英文标题】:How to show/hide password in TextFormField? 【发布时间】:2018-08-13 23:17:14 【问题描述】:

目前我的密码TextFormField 是这样的:

TextFormField(
  decoration: const InputDecoration(
      labelText: 'Password',
      icon: const Padding(
        padding: const EdgeInsets.only(top: 15.0),
        child: const Icon(Icons.lock),
      )),
  validator: (val) => val.length < 6 ? 'Password too short.' : null,
  onSaved: (val) => _password = val,
  obscureText: true,
);

我想要一个像交互这样的按钮,它可以使密码可见和不可见。我可以在TextFormField 里面做吗?或者我将不得不制作一个Stack 小部件来获得我所需的用户界面。以及obscureText真/假的条件如何?

【问题讨论】:

只需将传递给obscureTexttrue 设为变量并切换它,例如在传递给`onPressed: if a button 的函数中 【参考方案1】:

我按照@Hemanth Raj 创建了一个解决方案,但采用了更强大的方式。

首先声明一个bool变量_passwordVisible

initState() 中启动_passwordVisiblefalse

@override
  void initState() 
    _passwordVisible = false;
  

下面是TextFormField 小部件:

TextFormField(
   keyboardType: TextInputType.text,
   controller: _userPasswordController,
   obscureText: !_passwordVisible,//This will obscure text dynamically
   decoration: InputDecoration(
       labelText: 'Password',
       hintText: 'Enter your password',
       // Here is key idea
       suffixIcon: IconButton(
            icon: Icon(
              // Based on passwordVisible state choose the icon
               _passwordVisible
               ? Icons.visibility
               : Icons.visibility_off,
               color: Theme.of(context).primaryColorDark,
               ),
            onPressed: () 
               // Update the state i.e. toogle the state of passwordVisible variable
               setState(() 
                   _passwordVisible = !_passwordVisible;
               );
             ,
            ),
          ),
        );

【讨论】:

在默认的 Normal 情况下,页面加载密码被隐藏,因此将 passwordVisible 初始化为 true 即 passwordVisible= true ,并且在这种情况下使其有意义将变量 passwordVisible 替换为 passwordObscured= true 除了 nelson 指出的内容之外,一个简单的解决方法是 obscureText: !passwordVisible 感谢这对我有用..!但验证器不工作 太棒了!像我想要的那样完美地工作【参考方案2】:

如果它是StatelessWidget,首先让你的小部件StatefulWidget

然后有一个变量bool _obscureText 并将其传递给您的TextFormField。根据需要使用setState 切换它。

例子:

class _FormFieldSampleState extends State<FormFieldSample> 

  // Initially password is obscure
  bool _obscureText = true;

  String _password;

  // Toggles the password show status
  void _toggle() 
    setState(() 
      _obscureText = !_obscureText;
    );
  

  @override
  Widget build(BuildContext context) 
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Sample"),
      ),
      body: new Container(
        child: new Column(
          children: <Widget>[
            new TextFormField(
              decoration: const InputDecoration(
                  labelText: 'Password',
                  icon: const Padding(
                      padding: const EdgeInsets.only(top: 15.0),
                      child: const Icon(Icons.lock))),
              validator: (val) => val.length < 6 ? 'Password too short.' : null,
              onSaved: (val) => _password = val,
              obscureText: _obscureText,
            ),
            new FlatButton(
                onPressed: _toggle,
                child: new Text(_obscureText ? "Show" : "Hide"))
          ],
        ),
      ),
    );
  

希望这会有所帮助!

【讨论】:

感谢您的帮助。我使用堆栈而不是容器。所以密码与按钮重叠。我将填充放在 FlatButton 容器中。还是重叠的。知道如何解决吗? 但是为什么要堆叠呢?水平排列试试Row,垂直排列试试Column 设计如下:i.stack.imgur.com/q8aQU.png 不是图标,而是平面按钮。因为它是我使用 Stack 的 TextFormField 的顶部。 不需要 Stack。 InputDecoration 有一个 suffixIcon 属性,这可能正是您正在寻找的。​​span> Cupertino 似乎仍需要 Stack? suffixIcon 似乎只适用于 Material。【参考方案3】:

感谢X-Wei,您可以将小部件创建为单独的password.dart

import 'package:flutter/material.dart';

class PasswordField extends StatefulWidget 
  const PasswordField(
    this.fieldKey,
    this.hintText,
    this.labelText,
    this.helperText,
    this.onSaved,
    this.validator,
    this.onFieldSubmitted,
  );

  final Key fieldKey;
  final String hintText;
  final String labelText;
  final String helperText;
  final FormFieldSetter<String> onSaved;
  final FormFieldValidator<String> validator;
  final ValueChanged<String> onFieldSubmitted;

  @override
  _PasswordFieldState createState() => new _PasswordFieldState();


class _PasswordFieldState extends State<PasswordField> 
  bool _obscureText = true;

  @override
  Widget build(BuildContext context) 
    return new TextFormField(
      key: widget.fieldKey,
      obscureText: _obscureText,
      maxLength: 8,
      onSaved: widget.onSaved,
      validator: widget.validator,
      onFieldSubmitted: widget.onFieldSubmitted,
      decoration: new InputDecoration(
        border: const UnderlineInputBorder(),
        filled: true,
        hintText: widget.hintText,
        labelText: widget.labelText,
        helperText: widget.helperText,
        suffixIcon: new GestureDetector(
          onTap: () 
            setState(() 
              _obscureText = !_obscureText;
            );
          ,
          child:
          new Icon(_obscureText ? Icons.visibility : Icons.visibility_off),
        ),
      ),
    );
  

称它为:

  import 'package:my_app/password.dart';

  String _password;
  final _passwordFieldKey = GlobalKey<FormFieldState<String>>();

  PasswordField(
    fieldKey: _passwordFieldKey,
    helperText: 'No more than 8 characters.',
    labelText: 'Password *',
    onFieldSubmitted: (String value) 
      setState(() 
        this._password = value;
      );
    ,
  ),

【讨论】:

【参考方案4】:

嗯,我个人喜欢一直隐藏密码,并在您想看到它们时看到,所以这是我用来隐藏/取消隐藏密码的方法,以防您希望密码在触摸时可见带有隐藏图标的联系人,并在您删除联系人后立即隐藏,那么这是给您的

//make it invisible globally
  bool invisible = true;

//wrap your toggle icon in Gesture Detector
  GestureDetector(
   onTapDown: inContact,//call this method when incontact
   onTapUp: outContact,//call this method when contact with screen is removed
   child: Icon(
   Icons.remove_red_eye,
   color: colorButton,
   ),
  ),

  void inContact(TapDownDetails details) 
    setState(() 
      invisible = false;
    );
  

  void outContact(TapUpDetails details) 
    setState(() 
      invisible=true;
    );
  

我的包中使用了这种方法 https://pub.dev/packages/passwordfield

以上代码的输出

【讨论】:

【参考方案5】:

我通过按住并释放 longTap 来做到这一点:

    bool _passwordVisible;

@override
void initState() 
    _passwordVisible = false;
    super.initState();


// ...
TextFormField(
  obscureText: !_passwordVisible,
  decoration: InputDecoration(
    hasFloatingPlaceholder: true,
    filled: true,
    fillColor: Colors.white.withOpacity(0.5),
    labelText: "Password",
    suffixIcon: GestureDetector(
      onLongPress: () 
        setState(() 
          _passwordVisible = true;
        );
      ,
      onLongPressUp: () 
        setState(() 
          _passwordVisible = false;
        );
      ,
      child: Icon(
          _passwordVisible ? Icons.visibility : Icons.visibility_off),
    ),
  ),
  validator: (String value) 
    if (value.isEmpty) 
      return "*Password needed";
    
  ,
  onSaved: (String value) 
    _setPassword(value);
  ,
);

【讨论】:

【参考方案6】:

此解决方案可防止点击 suffixIcon 将焦点赋予 TextField,但如果 TextField 已获得焦点并且用户想要显示/隐藏密码,则焦点不会丢失。

import 'package:flutter/material.dart';

class PasswordField extends StatefulWidget 
  const PasswordField(Key? key) : super(key: key);

  @override
  _PasswordFieldState createState() => _PasswordFieldState();


class _PasswordFieldState extends State<PasswordField> 
  final textFieldFocusNode = FocusNode();
  bool _obscured = false;

  void _toggleObscured() 
    setState(() 
      _obscured = !_obscured;
      if (textFieldFocusNode.hasPrimaryFocus) return; // If focus is on text field, dont unfocus
      textFieldFocusNode.canRequestFocus = false;     // Prevents focus if tap on eye
    );
  

  @override
  Widget build(BuildContext context) 
    return TextField(
      keyboardType: TextInputType.visiblePassword,
      obscureText: _obscured,
      focusNode: textFieldFocusNode,
      decoration: InputDecoration(
        floatingLabelBehavior: FloatingLabelBehavior.never, //Hides label on focus or if filled
        labelText: "Password",
        filled: true, // Needed for adding a fill color
        fillColor: Colors.grey.shade800, 
        isDense: true,  // Reduces height a bit
        border: OutlineInputBorder(
          borderSide: BorderSide.none,              // No border
          borderRadius: BorderRadius.circular(12),  // Apply corner radius
        ),
        prefixIcon: Icon(Icons.lock_rounded, size: 24),
        suffixIcon: Padding(
          padding: const EdgeInsets.fromLTRB(0, 0, 4, 0),
          child: GestureDetector(
            onTap: _toggleObscured,
            child: Icon(
              _obscured
                  ? Icons.visibility_rounded
                  : Icons.visibility_off_rounded,
              size: 24,
            ),
          ),
        ),
      ),
    );
  

【讨论】:

【参考方案7】:
class SignIn extends StatefulWidget 

  @override
  _SignInState createState() => _SignInState();


class _SignInState extends State<SignIn> 

        // Initially password is obscure
  bool _obscureText = true;
    // Toggles the password show status
  void _togglePasswordStatus() 
    setState(() 
      _obscureText = !_obscureText;
    );
  
  @override
  Widget build(BuildContext context) 
    return 
    Scaffold(
      backgroundColor: Colors.brown[100],
      appBar: AppBar(
        backgroundColor: Colors.brown[400],
        elevation: 0.0,
        title: Text('Sign In'),
      ),
      body: Container(
        padding: EdgeInsets.symmetric(vertical:20.0,horizontal:50.0),
        child: Form(
          key: _formKey,
          child: Column(children: <Widget>[
            TextFormField(
              decoration: InputDecoration(
                hintText: 'Password',
                suffixIcon:  IconButton(
                  icon:Icon(_obscureText ? Icons.visibility:Icons.visibility_off,),
                   onPressed: _togglePasswordStatus,
                   color: Colors.pink[400],
                   ),
              ),
              validator: (val)
                return
                val.length < 6 ? 'Enter A Password Longer Than 6 Charchters' :null;
              ,
              obscureText: _obscureText,
              onChanged: (val)
                setState(() 
                  password = val.trim();
                );
              ,
            ),
        ],),),
      ),
    );
  

【讨论】:

这个答案可以使用更多的上下文。解释它的作用以及它如何改进以前的答案。【参考方案8】:

这是一个带有内置材料设计图标的更简单示例:

child: TextFormField(
              decoration: InputDecoration(
                  fillColor: Color(0xFFFFFFFF), filled: true,
                  enabledBorder: UnderlineInputBorder(
                    borderSide: BorderSide(color: Color(0xFF808080)),
                  ),
                  suffixIcon: GestureDetector(
                    onTap: () 
                      setState(() 
                        _showPassword = !_showPassword;
                      );
                    ,
                    child: Icon(
                        _showPassword ? Icons.visibility : Icons.visibility_off,
                    ),
                  ),
                  labelText: 'Password'),
              obscureText: !_showPassword,
            ),

【讨论】:

【参考方案9】:
  bool _obscuredText = true; 

  _toggle()
    setState(() 
      _obscuredText = !_obscuredText;
    );
  

  Widget _createPassword()
    return TextField(
      obscureText: _obscuredText,
      cursorColor: Colors.black54,
      style: TextStyle( color: Colors.black54),
      decoration: InputDecoration(
        labelStyle: TextStyle(
            color: Colors.black54
        ),
        focusedBorder: OutlineInputBorder(
            borderSide: BorderSide(
                color: Colors.black54
            )
        ),
        border: OutlineInputBorder(
            borderRadius: BorderRadius.circular(5.0)
        ),
        labelText: 'Contraseña',
        hintText: 'Contraseña',
        suffixIcon: FlatButton(onPressed: _toggle, child:Icon(Icons.remove_red_eye, color: _obscuredText ? Colors.black12 : Colors.black54))
      ),
      onChanged: (value) 
        setState(() 
          _password = value;
        );
      ,
    );
  

希望这会有所帮助!

【讨论】:

【参考方案10】:

○ 只需简单的 3 个步骤,您就可以完成密码显示/隐藏。


第一步:创建变量

       bool _isHidden = true;

第2步:神奇的步骤,使图标可点击并查看/隐藏密码。

现在我将用 InkWell 包裹图标,使其可点击。所以,当我们点击它时,它会在 obscureText 参数的真假之间切换。

 @override
   Widget build(BuildContext context) 
     return Scaffold(
        backgroundColor: Theme.of(context).secondaryHeaderColor,
          body: Center(
            child: Container(
            height: 55,
            alignment: Alignment.center,
            padding: EdgeInsets.fromLTRB(10, 10, 10, 0),
            child: TextField(
              obscureText: _isHidden,
              decoration: InputDecoration(
                border: OutlineInputBorder(),
                labelText: 'Password',
                suffix: InkWell(
                  onTap: _togglePasswordView,  /// This is Magical Function
                  child: Icon(
                    _isHidden ?         /// CHeck Show & Hide.
                     Icons.visibility :
                     Icons.visibility_off,
                  ),
                ),
                /*icon: Icon(
                  Icons.password_sharp,
                  color: Colors.black,
                ),*/
              ),
            ),
          ),
        ),
     );
  

第 3 步:创建这个神奇的功能。

      void _togglePasswordView() 
    setState(() 
        _isHidden = !_isHidden;
    );

  

☻♥完成。

【讨论】:

【参考方案11】:

感谢@Parikshit Chalke 的回答。然而, 如果您只想更新您的TextFormFieldIconButtonsetState 的通话费用非常昂贵。相反,将其包装在 StatefulBuilder 中,并且只更新子项。

示例解决方案:

import 'package:flutter/material.dart';

class MyWidget extends StatefulWidget 
  @override
  _MyWidgetState createState() => _MyWidgetState();


class _MyWidgetState extends State<MyWidget> 
  // initially password is invisible
  bool _passwordVisible = false;
  String _password;

  @override
  Widget build(BuildContext context) 
    return Column(
      children: [
        // other widget that does not need update when password visibility is toggled
        Text("I do not require update"),
        
        StatefulBuilder(builder: (_context, _setState) 
          // only following widget gets update when _setState is used
          return TextFormField(
            decoration: InputDecoration(
              suffixIcon: IconButton(
                icon: Icon(
                  _passwordVisible ? Icons.visibility : Icons.visibility_off,
                ),
                onPressed: () 
                  // use _setState that belong to StatefulBuilder
                  _setState(() 
                    _passwordVisible = !_passwordVisible;
                  );
                ,
              ),
              labelText: 'Password',
              icon: const Padding(
                padding: const EdgeInsets.only(top: 15.0),
                child: const Icon(Icons.lock),
              ),
            ),
            validator: (val) => val.length < 6 ? 'Password too short.' : null,
            onSaved: (val) => _password = val,
            obscureText: true,
          );
        ),
      ],
    );
  


【讨论】:

【参考方案12】:

我有更有用的解决方案。您可以使用 Provider 并使用 Consumer Widget 监听 TextFormField

obscure_text_state.dart

import 'package:flutter/material.dart';

class ObscureTextState with ChangeNotifier 
  bool _isTrue = true;
  bool get isTrue => _isTrue;

  get switchObsIcon 
    return _isTrue ? Icon(Icons.visibility_off) : Icon(Icons.visibility);
  

  void toggleObs() 
    _isTrue = !_isTrue;
    notifyListeners();
  


那么你应该用 TextFromField 所在的 Consumer 监听那个状态。

            Consumer<ObscureTextState>(
              builder: (context, obs, child) 
                return TextFormField(
                  controller: _passwordController,
                  validator: (value) 
                    if (value.isEmpty) 
                      return "Alan boş bırakılamaz!";
                     else if (value.length < 6) 
                      return "Şifre en az 6 haneden oluşmalıdır.";
                     else 
                      return null;
                    
                  ,
                  obscureText:
                      Provider.of<ObscureTextState>(context, listen: false)
                          .isTrue,
                  decoration: InputDecoration(
                      prefixIcon: Icon(Icons.lock),
                      suffixIcon: IconButton(
                        onPressed: () 
                          Provider.of<ObscureTextState>(context, listen: false)
                              .toggleObs();
                        ,
                        icon: Provider.of<ObscureTextState>(context,
                                listen: false)
                            .switchObsIcon,
                      ),
                      hintText: "Şifre",
                      border: OutlineInputBorder(
                          borderRadius: BorderRadius.circular(20.0))),
                );
              ,
            ),

【讨论】:

【参考方案13】:
    TextFormFeild(
    decoration:InputDecoration(
     icon: _isSecurityIcon == true
  ? IconButton(
   icon: Icon(Icons.visibility_off_outlined),
onPressed: () 
   setState(() 
_isSecurityIcon = false;
   );
  ,
)
: IconButton(
icon: Icon(Icons.visibility_outlined),
onPressed: () 
setState(
() 
_isSecurityIcon = true;
    ,
    );
    ,
   ),
   ),
  );```

【讨论】:

以上是关于如何在 TextFormField 中显示/隐藏密码?的主要内容,如果未能解决你的问题,请参考以下文章

在 Web 上,如何控制自动显示在焦点 TextFormField 上的可见性图标,该 TextFormField 的 obscureText 属性设置为 true?

TextFormField 在验证时不显示错误

[译]TextField/TextFormField 如何显示/隐藏密码

更改 FLUTTER 中 TextFormField 的默认边框颜色

Flutter TextFormField 被键盘隐藏

如何使用 GetX 实现 Flutter TextFormField 验证器?