skip to Main Content

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;
    }
  }
}

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);
);

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

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

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!

3

Answers


  1. Chosen as BEST ANSWER

    You need to provide a key for each text field CustomTextFormField so that Flutter can determine the position of your fields and thus change their position in the list.

    Try the following code:

    CustomTextFormField(
      key: const Key("signIn-name-field"),
    ),
    CustomTextFormField(
      key: const Key("signIn-email-field"),
    ),
    

    Source: https://github.com/flutter/flutter/issues/119200#issuecomment-1405219170

    @mycar98765 Thanks for the recording ✨ You need to provide a key to the individual text fields CustomTextFormField so that flutter knows for certain where your fields are and so it knows certain that they changed position on within the list.

     CustomTextFormField(
        key: const Key('signIn-name-field'), // add this to name field
     ),
     CustomTextFormField(
        key: const Key('signIn-email-field'), // add this to email field
      ),
    

    If you want to see the visual difference when you have and don't have keys on them, try entering a value on email or name fields then toggle the auth mode state. (The value will jump to the wrong field, because of missing keys) more on the importance of key here.


  2. I solved your problem using autofocus.. Below the code..

    import 'package:flutter/material.dart';
    import 'package:get/get.dart';
    import 'package:rezar_app/views/bin/home_view.dart';
    import 'package:rezar_app/views/homeViewFlow/home_view.dart';
    
    class BinFile extends StatefulWidget {
    const BinFile({super.key});
    
    @override
    State<BinFile> createState() => _BinFileState();
    }
    
     class _BinFileState extends State<BinFile> {
     @override
       Widget build(BuildContext context) {
      return Scaffold(
      appBar: AppBar(title: Text('Login View')),
      body: Column(
        children: [
          CustomTextFormField(
              labelText: 'Email',
              hintText: 'Enter Mail',
              readOnly: false,
              autofocus: true),
          CustomTextFormField(
              labelText: 'Password',
              hintText: 'Enter Password',
              readOnly: false),
          SizedBox(
            height: 10,
          ),
          SizedBox(
            height: 10,
          ),
          ElevatedButton(
              onPressed: () {
                Get.to(() => HomeNewScreen());
              },
              child: Text('Next Page'))
        ],
      ),
    );
         }
         }
    
     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,
        ),
      );
        }
    

    Second Screen below the code

    import 'package:flutter/material.dart';
    
    class HomeNewScreen extends StatefulWidget {
    const HomeNewScreen({super.key});
    
    @override
    State<HomeNewScreen> createState() => _HomeNewScreenState();
    }
    
    class _HomeNewScreenState extends State<HomeNewScreen> {
    @override
    Widget build(BuildContext context) {
    return Scaffold(
      body: Center(child: Text('Home Screen')),
     );
      }
      }
    
    Login or Signup to reply.
  3. Please try two solution

    1. remove readOnly in emailTextField either
    2. readOnly: false
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search