删除Flutter中InputFormField上errorText创建的额外空间

mwecs4sa  于 2023-01-27  发布在  Flutter
关注(0)|答案(1)|浏览(143)

我正在使用CustomFormField类来定制TextFormField的默认行为。我想做的事情之一是移动errorText的位置,以防止它在发生错误时调整表单上其他字段的位置。通过使用堆栈并重新定位errorText使其出现在字段的下边界而不是下面的几个像素,我已经实现了一半的目标。不幸的是,默认行为仍然保留空间,其他字段仍然移动。
Text Fields without error condition
Text Fields with error condition
如您所见,errorText不再占用空间,但TextFormField的默认行为仍然占用空间。
有什么办法可以防止这种情况发生吗?
这是一个有点长,这仍然是一个正在进行中的工作,但这里是我使用的代码。

class CustomFormField extends StatefulWidget {
  final GlobalKey formKey;
  final TextEditingController controller;
  final FocusNode currentFocusNode;
  final FocusNode futureFocusNode;
  final TextInputType inputType;
  final TextInputAction inputAction;
  final bool allowShowPassword;
  final bool enabled;
  final bool autoFocus;
  final String labelText;
  final String hintText;
  final FormFieldValidator<String>? validator;
  final Function? onChanged;
  final FormFieldSetter<String>? onSaved;

  const CustomFormField(
      {super.key,
      required this.formKey,
      required this.controller,
      required this.currentFocusNode,
      required this.futureFocusNode,
      this.inputType = TextInputType.text,
      this.inputAction = TextInputAction.next,
      this.allowShowPassword = true,
      this.enabled = true,
      this.autoFocus = false,
      this.labelText = '',
      this.hintText = '',
      this.validator,
      this.onChanged,
      this.onSaved});

  @override
  State<CustomFormField> createState() => _CustomFormFieldState();
}

class _CustomFormFieldState extends State<CustomFormField> {
  bool _passwordHidden = true;
  bool onError = false;
  String _errorText = '';

  @override
  Widget build(BuildContext context) {
    // grab the application theme to use when colouring text fields
    ThemeData theme = Theme.of(context);

    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 5.0),
      child: Stack(
        children: [
          TextFormField(
            enabled: widget.enabled,
            autofocus: widget.autoFocus,
            textInputAction: widget.inputAction,
            onEditingComplete: () =>
                FocusScope.of(context).requestFocus(widget.futureFocusNode),
            focusNode: widget.currentFocusNode,
            keyboardType: widget.inputType,
            obscureText: (widget.inputType == TextInputType.visiblePassword &&
                    _passwordHidden)
                ? _passwordHidden
                : false,
            controller: widget.controller,
            validator: (value) {
              String? errorText = widget.validator!.call(value);
              if (errorText != null && Static.formIsValid) {
                Static.formIsValid = false;
                widget.currentFocusNode.requestFocus();
                setState(() {
                  onError = true;
                  _errorText = errorText;
                });
              } else {
                setState(() {
                  onError = false;
                  _errorText = '';
                });
              }
              // the validator still needs us to return null if there was no error
              // but if there is an error return empty string
              return errorText == null ? null : ''; 
            },
            onSaved: widget.onSaved,
            onChanged: (value) {
              if (widget.onChanged != null) {
                widget.onChanged!.call(value);
              }
            },
            style: TextStyle(
                color: widget.enabled
                    ? Constants.formFieldColor
                    : theme.disabledColor,
                fontSize: 16,
                fontWeight: FontWeight.normal,
                fontFamily: 'Ariel'),
            decoration: InputDecoration(
              labelText: widget.labelText,
              hintText: widget.hintText,
              hintStyle: const TextStyle(color: Colors.white38),
              contentPadding: const EdgeInsets.symmetric(horizontal: 25),
              // constraints: const BoxConstraints(maxHeight: 40, minHeight: 40),
              filled: true,
              border: const OutlineInputBorder(
                  borderRadius: BorderRadius.all(Radius.circular(25)),
                  borderSide: BorderSide(color: Colors.black)),
              enabledBorder: const OutlineInputBorder(
                  borderRadius: BorderRadius.all(Radius.circular(25)),
                  borderSide: BorderSide(color: Colors.black)),
              focusedBorder: const OutlineInputBorder(
                  borderRadius: BorderRadius.all(Radius.circular(25)),
                  borderSide: BorderSide(color: Colors.black)),
              errorBorder: const OutlineInputBorder(
                  borderRadius: BorderRadius.all(Radius.circular(25)),
                  borderSide: BorderSide(color: Colors.red)),
              focusedErrorBorder: const OutlineInputBorder(
                  borderRadius: BorderRadius.all(Radius.circular(25)),
                  borderSide: BorderSide(color: Colors.black)),
              disabledBorder: const OutlineInputBorder(
                  borderRadius: BorderRadius.all(Radius.circular(25)),
                  borderSide: BorderSide(color: Colors.black)),
              alignLabelWithHint: true,
              focusColor: Colors.black,
              suffixIcon: widget.inputType == TextInputType.visiblePassword &&
                      widget.allowShowPassword
                  ? InkWell(
                      onTap: () {
                        setState(() {
                          _passwordHidden = !_passwordHidden;
                        });
                      },
                      child: Icon(
                        _passwordHidden
                            ? Icons.visibility_off_outlined
                            : Icons.visibility_outlined,
                        color: Constants.passwordShowHideIconColor,
                        size: 18,
                      ),
                    )
                  : null,
            ),
          ),
          // this repositions the errorText to appear just on top of the lower border
          onError
              ? Positioned(
                  bottom: 17,
                  left: 25,
                  child: Container(
                    height: 15,
                    decoration: const BoxDecoration(color: Colors.white),
                    child: Padding(
                      padding: const EdgeInsets.symmetric(horizontal: 2),
                      child: Text(_errorText,
                          style: const TextStyle(
                              color: Colors.red,
                              fontSize: 13,
                              fontStyle: FontStyle.normal)),
                    ),
                  ),
                )
              : Container(),
        ],
      ),
    );
  }
}

我尝试过的一件事是将所有字段都 Package 在一个SizedBox中,这确实阻止了其他字段改变它们的位置,但它的副作用是缩小了包含错误的字段的大小,因为它总是希望为errorText保留空间。
任何帮助都将不胜感激。

m1m5dgzv

m1m5dgzv1#

已解决:
我可以通过添加以下内容来解决这个问题:

errorStyle: const TextStyle(height: 0),

这将阻止字段为错误文本占用额外空间。

相关问题