Flutter 输入框在iOS打开密码填充后会闪烁问题

iOS如果在设置中打开了【自动填充密码】就会显示出这个密码栏,输入时会一闪一闪的

【原因】:在TextField里,只要obscureText为true时,也就是密码框显示全是【•】时,iOS的密码填充栏就会显示出来。

所以我的解决思路是把obscureText写死为False:

这时我们只要解决当他的小眼睛闭上,也就是隐藏密码时,输入框里的显示怎么变为【•】就行了。

上代码!!!

1、把obscureText写死成false
2、在onChange里判断是否隐藏或显示密码,如果是隐藏状态,就把输入框里的value全部替换成【•】;如果显示密码状态,我们只要把realPassword给显示就行--【需要留一个变量去存真实的密码】

TextField(
              //只要这个变成了true,也就是密码隐藏密码开关,这个填充密码栏就会显示出来
              obscureText: false,
              textAlign: TextAlign.start,
              inputFormatters: widget.inputFormatters,
              controller: widget.controller,
              focusNode: widget.focusNode,
              autocorrect: false,
              onChanged: (value) {
                if (widget.onChanged != null) {
                  widget.onChanged!(value);
                }
                if (widget.obscureText) {
                  realPassword = value;
                  String obscureText = value.replaceAll(RegExp('.'), '•');
                  if (widget.realPasswordListener != null) {
                    widget.realPasswordListener!(realPassword);
                  }
                  // 设置TextField显示遮蔽字符
                  widget.controller.value = TextEditingValue(
                    text: !isShowPsw ? obscureText : realPassword,
                    selection: TextSelection.collapsed(
                      offset: widget.controller.selection.baseOffset,
                    ),
                  );
                }
              },
              toolbarOptions: ToolbarOptions(
                copy: !widget.obscureText,
                cut: !widget.obscureText,
                paste: true,
                selectAll: true,
              ),
              cursorColor: Theme.of(context).textTheme.titleLarge!.color!,
              style: TextStyle(
                    color: Theme.of(context).textTheme.titleLarge!.color!,
                    fontSize: Dimens.font_sp15,
                  ),
              decoration: InputDecoration(
                hintText: widget.hintText,
                hintStyle: TextStyle(
                      fontSize: Dimens.font_sp15,
                      color: Theme.of(context).textTheme.bodyLarge!.color!,
                    ),
                enabledBorder: InputBorder.none,
                focusedBorder: InputBorder.none,
              ),
              keyboardType: widget.keyboardType,
              maxLines: 1,
              minLines: 1,
            ),

3、在小眼睛按钮处,如果切换状态,也要全部替换成【•】或者真实密码

          //小眼睛图标
          if (widget.obscureText)
            showPswButton(
              context,
              isShowPsw: isShowPsw,
              onTap: () {
                isShowPsw = !isShowPsw;
                if (widget.controller.text.isEmpty) {
                  realPassword = "";
                }
                if (widget.realPasswordListener != null) {
                  widget.realPasswordListener!(realPassword);
                }
                String obscureText = realPassword;
                widget.controller.value = TextEditingValue(
                  text: isShowPsw
                      ? realPassword
                      : obscureText.replaceAll(RegExp('.'), '•'),
                  selection: TextSelection.collapsed(
                    offset: widget.controller.selection.baseOffset,
                  ),
                );
                setState(() {});
              },
            ),

4、接下来我们在页面上调用封装的输入框:核心是inputFormatters里面TestPswFormatter,在这里面给真实密码去赋值

TestTextField(
          icon: 'ic_password',
          controller: pswOneController,
          focusNode: pswOneFocusNode,
          hintText: "请设置密码",
          keyboardType: Platform.isIOS
              ? TextInputType.datetime
              : TextInputType.visiblePassword,
          realPasswordListener: (value) {
            realPassword = value;
            setState(() {});
          },
          inputFormatters: [
            //只允许输入数字,字母
            FilteringTextInputFormatter(
              RegExp("^[a-z0-9A-Z•]+"),
              allow: true,
            ),
            LengthLimitingTextInputFormatter(20),
            //给真实密码realPassword赋值
            TestPswFormatter(realPassword: realPassword),
          ],
          obscureText: true,
        )

5、TestPswFormatter:处理真实密码,每次输入都给realPassword赋值,光标位置也需要处理

class TestPswFormatter extends TextInputFormatter {
  String realPassword;

  TestPswFormatter({this.realPassword = ""});

  @override
  TextEditingValue formatEditUpdate(
      TextEditingValue oldValue, TextEditingValue newValue) {
    print("realPassword:${realPassword}");
    print("oldzqhValue:${oldValue}");
    print("newzqhValue:$newValue");
    if (newValue.text == "") {
      realPassword = "";
    } else if (newValue.text.length - oldValue.text.length > 1) {
      String newText = newValue.text;
      newText = newText.replaceAll('•', '');
      realPassword = newText;
      return TextEditingValue(
        text: realPassword,
        selection: TextSelection.fromPosition(
          TextPosition(
            affinity: TextAffinity.downstream,
            offset: realPassword.length,
          ),
        ),
      );
    } else if (realPassword.length < newValue.text.length) {
      // 截取光标前的字符串和光标后的字符串
      String beforeCursor =
          realPassword.substring(0, newValue.selection.baseOffset - 1);
      String afterCursor = realPassword.substring(
          newValue.selection.baseOffset - 1, realPassword.length);
      realPassword = beforeCursor +
          newValue.text[newValue.selection.baseOffset - 1] +
          afterCursor;
    } else if (realPassword.length > newValue.text.length) {
      String beforeCursor =
          realPassword.substring(0, newValue.selection.baseOffset);
      String afterCursor = realPassword.substring(
          newValue.selection.baseOffset + 1, realPassword.length);
      realPassword = beforeCursor + afterCursor;
    }
    return TextEditingValue(
      text: realPassword,
      selection: TextSelection.collapsed(offset: newValue.selection.baseOffset),
    );
  }
}

这样就解决这个问题了,下面是完整代码:
 

页面代码:

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';

import '../common/res/colors.dart';
import '../utils/cache_util.dart';
import 'common/image/load_image.dart';
import 'common/res/dimens.dart';

class TestPage extends StatefulWidget {
  const TestPage({super.key});

  @override
  State<TestPage> createState() => _TestPageState();
}

class _TestPageState extends State<TestPage> {
  TextEditingController pswOneController = TextEditingController();
  FocusNode pswOneFocusNode = FocusNode();

  //用于记录真实密码,输入框内密码在隐藏的情况下全是•
  String realPassword = "";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey,
      body: Center(
        child: TestTextField(
          icon: 'ic_password',
          controller: pswOneController,
          focusNode: pswOneFocusNode,
          hintText: "请设置密码",
          keyboardType: Platform.isIOS
              ? TextInputType.datetime
              : TextInputType.visiblePassword,
          realPasswordListener: (value) {
            realPassword = value;
            setState(() {});
          },
          inputFormatters: [
            //只允许输入数字,字母
            FilteringTextInputFormatter(
              RegExp("^[a-z0-9A-Z•]+"),
              allow: true,
            ),
            LengthLimitingTextInputFormatter(20),
            //给真实密码realPassword赋值
            TestPswFormatter(realPassword: realPassword),
          ],
          obscureText: true,
        ),
      ),
    );
  }
}

输入框封装代码:

class TestTextField extends StatefulWidget {
  final String? icon;
  final List<TextInputFormatter>? inputFormatters;
  final TextEditingController controller;
  final FocusNode focusNode;
  final String hintText;
  final bool obscureText;
  final TextInputType keyboardType;
  final ValueChanged<String>? realPasswordListener;
  final ValueChanged<String>? onChanged;

  const TestTextField({
    super.key,
    this.icon,
    required this.controller,
    required this.focusNode,
    required this.hintText,
    required this.keyboardType,
    this.inputFormatters,
    this.obscureText = false,
    this.realPasswordListener,
    this.onChanged,
  });

  @override
  State<TestTextField> createState() => _TestTextFieldState();
}

class _TestTextFieldState extends State<TestTextField> {
  bool showClearButton = false;
  String realPassword = '';
  bool isShowPsw = false;

  @override
  void initState() {
    super.initState();
    widget.focusNode.addListener(() {
      showClearButton =
          widget.controller.text.isNotEmpty && widget.focusNode.hasFocus;
      setState(() {});
    });
    widget.controller.addListener(() {
      showClearButton =
          widget.controller.text.isNotEmpty && widget.focusNode.hasFocus;
      if (widget.controller.text.isEmpty) {
        realPassword = "";
        if (widget.realPasswordListener != null) {
          widget.realPasswordListener!(realPassword);
        }
      }
      setState(() {});
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        color: Theme.of(context).cardColor,
        borderRadius: BorderRadius.circular(4.r),
      ),
      margin: EdgeInsets.symmetric(
        horizontal: 36.w,
      ),
      padding: EdgeInsets.only(
        left: 16.w,
        right: 8.w,
      ),
      child: Row(
        children: [
          if (widget.icon != null)
            Padding(
              padding: EdgeInsets.only(right: 10.w),
              child: LoadAssetImage(
                widget.icon!,
                width: 20.w,
                height: 20.h,
                color: Theme.of(context).textTheme.titleMedium?.color,
              ),
            ),
          Expanded(
            child: TextField(
              //只要这个变成了true,也就是密码隐藏密码开关,这个填充密码栏就会显示出来
              obscureText: false,
              textAlign: TextAlign.start,
              inputFormatters: widget.inputFormatters,
              controller: widget.controller,
              focusNode: widget.focusNode,
              autocorrect: false,
              onChanged: (value) {
                if (widget.onChanged != null) {
                  widget.onChanged!(value);
                }
                if (widget.obscureText) {
                  realPassword = value;
                  String obscureText = value.replaceAll(RegExp('.'), '•');
                  if (widget.realPasswordListener != null) {
                    widget.realPasswordListener!(realPassword);
                  }
                  // 设置TextField显示遮蔽字符
                  widget.controller.value = TextEditingValue(
                    text: !isShowPsw ? obscureText : realPassword,
                    selection: TextSelection.collapsed(
                      offset: widget.controller.selection.baseOffset,
                    ),
                  );
                }
              },
              toolbarOptions: ToolbarOptions(
                copy: !widget.obscureText,
                cut: !widget.obscureText,
                paste: true,
                selectAll: true,
              ),
              cursorColor: Theme.of(context).textTheme.titleLarge!.color!,
              style: TextStyle(
                color: Theme.of(context).textTheme.titleLarge!.color!,
                fontSize: Dimens.font_sp15,
              ),
              decoration: InputDecoration(
                hintText: widget.hintText,
                hintStyle: TextStyle(
                  fontSize: Dimens.font_sp15,
                  color: Theme.of(context).textTheme.bodyLarge!.color!,
                ),
                enabledBorder: InputBorder.none,
                focusedBorder: InputBorder.none,
              ),
              keyboardType: widget.keyboardType,
              maxLines: 1,
              minLines: 1,
            ),
          ),
          //清除图标
          if (showClearButton)
            clearButton(
              context,
              onTap: () {
                widget.controller.clear();
                if (widget.realPasswordListener != null) {
                  widget.realPasswordListener!(widget.controller.text);
                }
              },
            ),
          //小眼睛图标
          if (widget.obscureText)
            showPswButton(
              context,
              isShowPsw: isShowPsw,
              onTap: () {
                isShowPsw = !isShowPsw;
                if (widget.controller.text.isEmpty) {
                  realPassword = "";
                }
                if (widget.realPasswordListener != null) {
                  widget.realPasswordListener!(realPassword);
                }
                String obscureText = realPassword;
                widget.controller.value = TextEditingValue(
                  text: isShowPsw
                      ? realPassword
                      : obscureText.replaceAll(RegExp('.'), '•'),
                  selection: TextSelection.collapsed(
                    offset: widget.controller.selection.baseOffset,
                  ),
                );
                setState(() {});
              },
            ),
        ],
      ),
    );
  }
}

//输入框清除按钮
Widget clearButton(BuildContext context,
    {required Function onTap, EdgeInsetsGeometry? margin}) {
  return InkWell(
    onTap: () => onTap(),
    child: Container(
      decoration: BoxDecoration(
        color: appIsDark ? COLOR_FF606062 : COLOR_FFD8D9DD,
        shape: BoxShape.circle,
      ),
      margin: margin ?? EdgeInsets.all(8.h),
      child: LoadAssetSvg(
        'ic_clear',
        width: 16.w,
        height: 16.h,
        color: Theme.of(context).secondaryHeaderColor,
      ),
    ),
  );
}

//显示或隐藏密码的按钮
Widget showPswButton(BuildContext context,
    {required bool isShowPsw, required Function onTap}) {
  return InkWell(
    onTap: () => onTap(),
    child: Padding(
      padding: const EdgeInsets.all(8.0),
      child: LoadAssetImage(
        isShowPsw ? 'ic_show' : 'ic_hide',
        width: 16.w,
        height: 16.h,
        color: Theme.of(context).textTheme.titleSmall?.color,
      ),
    ),
  );
}

TestPswFormatter:真实密码赋值

class TestPswFormatter extends TextInputFormatter {
  String realPassword;

  TestPswFormatter({this.realPassword = ""});

  @override
  TextEditingValue formatEditUpdate(
      TextEditingValue oldValue, TextEditingValue newValue) {
    print("realPassword:${realPassword}");
    print("oldzqhValue:${oldValue}");
    print("newzqhValue:$newValue");
    if (newValue.text == "") {
      realPassword = "";
    } else if (newValue.text.length - oldValue.text.length > 1) {
      String newText = newValue.text;
      newText = newText.replaceAll('•', '');
      realPassword = newText;
      return TextEditingValue(
        text: realPassword,
        selection: TextSelection.fromPosition(
          TextPosition(
            affinity: TextAffinity.downstream,
            offset: realPassword.length,
          ),
        ),
      );
    } else if (realPassword.length < newValue.text.length) {
      // 截取光标前的字符串和光标后的字符串
      String beforeCursor =
          realPassword.substring(0, newValue.selection.baseOffset - 1);
      String afterCursor = realPassword.substring(
          newValue.selection.baseOffset - 1, realPassword.length);
      realPassword = beforeCursor +
          newValue.text[newValue.selection.baseOffset - 1] +
          afterCursor;
    } else if (realPassword.length > newValue.text.length) {
      String beforeCursor =
          realPassword.substring(0, newValue.selection.baseOffset);
      String afterCursor = realPassword.substring(
          newValue.selection.baseOffset + 1, realPassword.length);
      realPassword = beforeCursor + afterCursor;
    }
    return TextEditingValue(
      text: realPassword,
      selection: TextSelection.collapsed(offset: newValue.selection.baseOffset),
    );
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值