如何修复 Flutter 中自定义 TextField 中的光标行为?

问题描述 投票:0回答:1

我创建了一个自定义文本字段,其中需要有以下内容:

  1. 可以传递自定义控制器,以便当用户扫描条形码时,我可以使用扫描的测试更新文本
  2. 一个焦点节点,这样我就可以自定义关闭按钮
  3. 传递初始值的可能性

因此,我创建了以下自定义文本字段:

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:keyboard_actions/keyboard_actions.dart';
import 'package:myapp/app/components/material/icon.view.dart';
import 'package:myapp/app/components/text/text.view.dart';
import 'package:myapp/app/localizations/app_localizations.dart';
import 'package:myapp/theme/colors.extensions.dart';
import 'package:myapp/theme/icons.dart';
import 'package:myapp/theme/styling.extensions.dart';

class CustomTextField extends StatefulWidget {
  CustomTextField({
    super.key,
    this.initialValue,
    this.updateValue,
    this.textEditingController,
    required this.orientation,
  });

  dynamic initialValue;
  Function? updateValue;
  TextEditingController? textEditingController;
  Orientation orientation;

  @override
  State<CustomTextField> createState() => _CustomTextFieldState();
}

class _CustomTextFieldState extends State<CustomTextField> {
  TextEditingController controller = TextEditingController();
  FocusNode focusNode = FocusNode();

  @override
  void initState() {
    controller = TextEditingController(text: widget.initialValue == null ? null : '${widget.initialValue}');
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    (widget.textEditingController ?? controller).selection =
        TextSelection.fromPosition(TextPosition(offset: (widget.textEditingController ?? controller).text.length));
    return SizedBox(
      width: MediaQuery.of(context).size.width,
      height: 55,
      child: Stack(
        children: [
          KeyboardActions(
            tapOutsideBehavior: TapOutsideBehavior.opaqueDismiss,
            config: KeyboardActionsConfig(
              keyboardActionsPlatform: KeyboardActionsPlatform.ALL,
              nextFocus: false,
              actions: kIsWeb != true
                  ? [
                      KeyboardActionsItem(
                        focusNode: focusNode,
                        displayArrows: false,
                        toolbarButtons: [
                          (node) {
                            return GestureDetector(
                              onTap: () {
                                node.unfocus();
                              },
                              child: Padding(
                                padding: CustomPadding.p10(),
                                child: CustomText(
                                  AppLocalizations.instance.translate('Close'),
                                ),
                              ),
                            );
                          }
                        ],
                      ),
                    ]
                  : [],
            ),
            child: TextField(
              focusNode: focusNode,
              enableSuggestions: false,
              autocorrect: false,
              controller: widget.textEditingController ?? controller,
              cursorColor: CustomColors.blue,
              onChanged: (value) {
                setState(() {
                  (widget.textEditingController ?? controller).text = value;
                  if (widget.updateValue != null) {
                    widget.updateValue!(value);
                  }
                });
              },
              onSubmitted: (value) {
                FocusManager.instance.primaryFocus?.unfocus();
              },
              style: CustomTextStyle.custom(fontSize: 24, color: Colors.black),
              textAlignVertical: TextAlignVertical.top,
              decoration: InputDecoration(
                contentPadding: CustomPadding.ltrb(10, 8, 10 + ((widget.textEditingController ?? controller).text.isNotEmpty ? 28 : 0), 8),
                isDense: true,
                filled: true,
                hintStyle: CustomTextStyle.custom(color: CustomColors.darkGrey, fontSize: 20),
                fillColor: Colors.white,
                focusedBorder: OutlineInputBorder(
                  borderSide: BorderSide(color: CustomColors.blue, width: 2),
                  borderRadius: BorderRadius.circular(10),
                ),
                enabledBorder: OutlineInputBorder(
                  borderSide: BorderSide(
                    color: CustomColors.darkGrey,
                    width: 1.5,
                  ),
                  borderRadius: BorderRadius.circular(10),
                ),
              ),
            ),
          ),
          if ((widget.textEditingController ?? controller).text.isNotEmpty)
            SizedBox(
              height: 49,
              child: Row(
                mainAxisAlignment: MainAxisAlignment.end,
                children: [
                  Padding(
                    padding: CustomPadding.only10(PaddingPosition.r),
                    child: GestureDetector(
                      behavior: HitTestBehavior.opaque,
                      onTap: () {
                        HapticFeedback.heavyImpact();
                        if (widget.updateValue != null) {
                          widget.updateValue!('');
                        }
                        setState(() {
                          (widget.textEditingController ?? controller).text = '';
                        });
                      },
                      child: CustomIcon(
                        icon: SvgFont.xmark_solid,
                        color: CustomColors.red,
                      ),
                    ),
                  ),
                ],
              ),
            )
        ],
      ),
    );
  }
}

但是,这会导致光标出现问题,因为它一直移动到字符串的末尾,而不是停留在我正在写入的位置。我尝试删除所有自定义控制器管理和初始值,问题已修复,但我需要这些输入。此外,如果我不在构建函数中设置选择,文本字段会不断失去焦点,并且无法编写任何内容。

我想要我在开头列出的三个点,但我也想修复光标和选择,这样如果我想编辑字符串的中间部分,我可以做到这一点,而无需移动光标指向我输入的每个字符。

我怎样才能实现这个目标?

flutter focus cursor textfield
1个回答
0
投票

即使将光标放在中间,它总是在最后键入的原因是由于这一行:

 @override
  Widget build(BuildContext context) {
     // Within the build method

    (widget.textEditingController ?? controller).selection =
        TextSelection.fromPosition(
      TextPosition(
          offset: (widget.textEditingController ?? controller).text.length),
    );

每次构建小部件时,此行都会将光标位置设置到文本末尾,

现在,由于此代码位于

build
方法中,因此在每个小部件重建 (
setState
) 时,它将重新触发 并将光标重新插入末尾。

要解决此问题,您可以将逻辑移至

onChanged
内,而不是
build
方法中:

 onChanged: (value) {
                final cursorPosition = (widget.textEditingController ?? controller).selection.baseOffset;
                setState(() {
                  (widget.textEditingController ?? controller).text = value;
                  widget.updateValue?.call(value);
                  (widget.textEditingController ?? controller).selection = TextSelection.fromPosition(
                    TextPosition(offset: cursorPosition),
                  );
                });
              },
© www.soinside.com 2019 - 2024. All rights reserved.