1

I have created a custom TextField, where there need to be the following things:

  1. The possibility to pass a custom controller, so that when the user scans a barcode, I can update the text with the scanned test
  2. A focus node, so that I can customize the close button
  3. The possibility to pass an initial value

For this reason, I have created the following custom TextField:

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,
                      ),
                    ),
                  ),
                ],
              ),
            )
        ],
      ),
    );
  }
}

However, this causes a problem with the cursor, since it keeps going to the end of the string instead of staying where I'm writing. I tried removing all custom controller management and the initial value, and the problem is fixes, but I need those inputs. Moreover, if I don't set the selection in the build function, the text field keeps losing focus and it becomes impossible to write anything.

I would like to have the three points I listed at the beginning, but I would also like to fix the cursor and the selection so that if I want to edit the middle part of the string, I can do it without having to move the cursor at each character I type.

How can I achieve this?

2
  • I don't have a ValueNotifier, all the scanning process in managed with Intents and EventChannel. Using a ValueNotifier would mean to change the whole scanning process management
    – Pandruz
    Commented Jul 5 at 7:57
  • At the moment I use a controller, but if I use a listener with a controller, it means that each time a character is typed I would have to call the set state to apply the changes, wouldn't this be too much consuming?
    – Pandruz
    Commented Jul 5 at 12:06

0

Browse other questions tagged or ask your own question.