DEV Community

My Car
My Car

Posted on

Why doesn't the email text field have focus?

My code writes the code where the email textfield needs to be focused. However, when I change the screen to the signup screen and back, the email textfield is not focused.

Here are two videos showing what I said:

Email textfield

Keyboard

As you can see, when I open the sign in screen, the keyboard pops up. When I change the screen to the sign up screen, the keyboard also pops up. However, when I change back to the sign in screen, there is no keyboard, but the email text field is weird:

Email textfield

Keyboard

Why is this result returned?

My code:

enum SignInFormType { signIn, signUp }

class SignInState {
  SignInState({
    this.isSubmitted = false,
    this.isPasswordObscured = true,
    this.formType = SignInFormType.signIn,
    this.value = const AsyncValue.data(null),
  });

  final bool isSubmitted;
  final bool isPasswordObscured;
  final SignInFormType formType;

  final AsyncValue<void> value;

  bool get isLoading => value.isLoading;

  SignInState copyWith({
    bool? isSubmitted,
    bool? isPasswordObscured,
    SignInFormType? formType,
    AsyncValue<void>? value,
  }) {
    return SignInState(
      isSubmitted: isSubmitted ?? this.isSubmitted,
      isPasswordObscured: isPasswordObscured ?? this.isPasswordObscured,
      formType: formType ?? this.formType,
      value: value ?? this.value,
    );
  }
}

extension SignInScreenState on SignInState {
  String? nameValidator(String? value) {
    if (value!.trim().isEmpty) {
      return "Please enter your name";
    }
    return null;
  }

  String? emailValidator(String? value) {
    if (value!.trim().isEmpty) {
      return "Please enter your email address";
    }
    if (!RegExp(r"\S+@\S+\.\S+").hasMatch(value)) {
      return "Please enter a valid email address";
    }
    return null;
  }

  String get title {
    if (formType == SignInFormType.signIn) {
      return "Sign in";
    } else {
      return "Sign up";
    }
  }

  String get secondaryButtonText {
    if (formType == SignInFormType.signIn) {
      return "Sign up";
    } else {
      return "Sign in";
    }
  }

  SignInFormType get secondaryActionFormType {
    if (formType == SignInFormType.signUp) {
      return SignInFormType.signIn;
    } else {
      return SignInFormType.signUp;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

class SignInController extends StateNotifier<SignInState> {
  SignInController({required SignInFormType formType}) : super(SignInState(formType: formType));

  void updateIsSubmitted(bool isSubmitted) => state = state.copyWith(isSubmitted: isSubmitted);

  void updateIsPasswordObscured(bool isPasswordObscured) => state = state.copyWith(isPasswordObscured: isPasswordObscured);

  void updateFormType(SignInFormType formType) {
    state = state.copyWith(formType: formType);
  }
}

final signInControllerProvider = StateNotifierProvider.autoDispose.family<SignInController, SignInState, SignInFormType>(
  (ref, formType) => SignInController(formType: formType);
);
Enter fullscreen mode Exit fullscreen mode

class CustomTextFormField extends StatelessWidget {
  const CustomTextFormField({
    this.focusNode,
    this.textFormFieldKey,
    this.controller,
    this.validator,
    this.icon,
    required this.labelText,
    required this.hintText,
    this.suffixIcon,
    this.obscureText,
    this.keyboardType,
    this.textInputAction,
    required this.readOnly,
    this.maxLines,
    this.onEditingComplete,
    this.enabled,
    this.onChanged,
    this.onFieldSubmitted,
    this.onSaved,
    this.onTap,
    this.initialValue,
    this.autofocus,
    super.key,
  });

  final FocusNode? focusNode;
  final Key? textFormFieldKey;
  final TextEditingController? controller;
  final String? Function(String?)? validator;
  final IconData? icon;
  final String labelText;
  final String hintText;
  final Widget? suffixIcon;
  final bool? obscureText;
  final TextInputType? keyboardType;
  final TextInputAction? textInputAction;
  final bool readOnly;
  final int? maxLines;
  final VoidCallback? onEditingComplete;
  final bool? enabled;
  final Function(String)? onChanged;
  final Function(String)? onFieldSubmitted;
  final Function(String?)? onSaved;
  final VoidCallback? onTap;
  final String? initialValue;
  final bool? autofocus;

  @override
  Widget build(BuildContext context) => Theme(
        data: ThemeData().copyWith(colorScheme: ThemeData().colorScheme.copyWith(primary: Colors.black.withOpacity(0.5))),
        child: TextFormField(
          autofocus: autofocus ?? false,
          focusNode: focusNode,
          key: textFormFieldKey,
          controller: controller,
          validator: validator,
          style: const TextStyle(color: Colors.black),
          textAlignVertical: TextAlignVertical.top,
          initialValue: initialValue,
          decoration: InputDecoration(
            alignLabelWithHint: true,
            floatingLabelAlignment: FloatingLabelAlignment.start,
            prefixIcon: icon == null ? null : Icon(icon, color: Colors.black),
            labelText: labelText,
            labelStyle: const TextStyle(color: Colors.black),
            hintText: hintText,
            hintStyle: const TextStyle(color: Colors.black),
            border: const OutlineInputBorder(),
            focusedBorder: const OutlineInputBorder(borderSide: BorderSide(color: Colors.black, width: 1.0)),
            enabledBorder: const OutlineInputBorder(borderSide: BorderSide(color: Colors.black, width: 1.0)),
            suffixIcon: suffixIcon,
            filled: true,
            fillColor: Colors.white,
          ),
          autocorrect: false,
          obscureText: obscureText == null ? false : obscureText!,
          keyboardType: keyboardType,
          textInputAction: textInputAction,
          readOnly: readOnly,
          maxLines: maxLines,
          onEditingComplete: onEditingComplete,
          enabled: enabled,
          onChanged: onChanged,
          onFieldSubmitted: onFieldSubmitted,
          onSaved: onSaved,
          onTap: onTap,
        ),
      );
}
Enter fullscreen mode Exit fullscreen mode

class SignInScreen extends ConsumerStatefulWidget {
  const SignInScreen({required this.formType, super.key});

  final SignInFormType formType;

  @override
  ConsumerState<SignInScreen> createState() => _SignInScreenState();
}

class _SignInScreenState extends ConsumerState<SignInScreen> {
  final formKey = GlobalKey<FormState>();

  final emailFocusNode = FocusNode();
  final nameFocusNode = FocusNode();

  final nameController = TextEditingController();
  final emailController = TextEditingController();
  final passwordController = TextEditingController();

  void updateFormType(SignInFormType formType) {
    ref.read(signInControllerProvider(widget.formType).notifier).updateFormType(formType);

    passwordController.clear();
  }

  @override
  void initState() {
    emailFocusNode.requestFocus();
    super.initState();
  }

  @override
  void dispose() {
    nameFocusNode.dispose();
    emailFocusNode.dispose();
    nameController.dispose();
    emailController.dispose();
    passwordController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final state = ref.watch(signInControllerProvider(widget.formType));
    final controller = ref.read(signInControllerProvider(widget.formType).notifier);

    return Scaffold(
      appBar: AppBar(title: Text(state.title)),
      body: Padding(
        padding: const EdgeInsets.all(10.0),
        child: Form(
          key: formKey,
          child: SingleChildScrollView(
            child: Column(
              children: [
                state.secondaryActionFormType == SignInFormType.signUp
                    ? Container()
                    : CustomTextFormField(
                        focusNode: nameFocusNode,
                        controller: nameController,
                        validator: (value) => state.nameValidator(value),
                        icon: FontAwesomeIcons.solidUser,
                        labelText: "Name",
                        hintText: "Type your name here...",
                        keyboardType: TextInputType.name,
                        textInputAction: TextInputAction.next,
                        readOnly: state.isSubmitted ? true : false,
                      ),
                state.secondaryActionFormType == SignInFormType.signUp ? Container() : const SizedBox(height: 10.0),
                CustomTextFormField(
                  focusNode: emailFocusNode,
                  controller: emailController,
                  validator: (value) => state.emailValidator(value),
                  icon: Icons.email,
                  labelText: "Email",
                  hintText: "Type your email here...",
                  keyboardType: TextInputType.emailAddress,
                  textInputAction: TextInputAction.next,
                  readOnly: state.isSubmitted,
                ),
                …
                InkWell(
                  onTap: state.isSubmitted == true
                      ? null
                      : () {
                          updateFormType(state.secondaryActionFormType);
                          if (state.formType == signInFormType.signIn) {
                            emailFocusNode.unfocus();
                            nameFocusNode.requestFocus();
                          } else {
                            nameFocusNode.unfocus();
                            emailFocusNode.requestFocus();
                          }
                        },
                  child: Text(state.secondaryButtonText, textAlign: TextAlign.center, style: const TextStyle(color: Colors.black)),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Feel free to leave a comment if you need more information.

Why doesn't the email text field have focus? I would appreciate any help. Thank you in advance!

Top comments (0)