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:
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:
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
You need to provide a
key
for each text fieldCustomTextFormField
so that Flutter can determine the position of your fields and thus change their position in the list.Try the following code:
Source: https://github.com/flutter/flutter/issues/119200#issuecomment-1405219170
I solved your problem using autofocus.. Below the code..
Second Screen below the code
Please try two solution
readOnly: false