Reputation: 3869
This is a part of form I use inside a card widget on my auth_screen.dart
file:
child: Obx(() => Form(
key: _formKey,
child: SingleChildScrollView(
child: Column(
children: <Widget>[
TextFormField(
decoration: const InputDecoration(labelText: 'E-Mail'),
keyboardType: TextInputType.emailAddress,
validator: (value) {
if (value!.isEmpty || !value.contains('@')) {
return 'Invalid email!';
}
},
onSaved: (value) {
_authData['email'] = value as String;
},
),
TextFormField(
decoration: const InputDecoration(labelText: 'Password'),
obscureText: true,
controller: _passwordController,
validator: (value) {
if (value!.isEmpty || value.length < 5) {
return 'Password is too short!';
}
},
onSaved: (value) {
_authData['password'] = value as String;
},
),
That has two TextFormField
for E-Mail an Password.
Also the related auth_controller.dart
file as following:
enum AuthMode { Signup, Login }
class AuthController extends GetxController
with GetSingleTickerProviderStateMixin {
static AuthController instance = Get.find();
Rx<dynamic>? authMode = AuthMode.Login.obs;
RxBool? isLoading = false.obs;
String? _token;
DateTime? _expiryDate;
String? _userId;
Timer? _authTimer;
final _isAuth = false.obs;
AnimationController? controller;
Animation<Offset>? slideAnimation;
Animation<double>? opacityAnimation;
late TextEditingController passwordController;
final key = GlobalKey<FormState>();
@override
void onInit() {
super.onInit();
tryAutoLogin();
controller = AnimationController(
vsync: this,
duration: const Duration(
milliseconds: 300,
),
);
slideAnimation = Tween<Offset>(
begin: const Offset(0, -1.5),
end: const Offset(0, 0),
).animate(
CurvedAnimation(
parent: controller as Animation<double>,
curve: Curves.fastOutSlowIn,
),
);
opacityAnimation = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller as Animation<double>,
curve: Curves.easeIn,
),
);
// _heightAnimation.addListener(() => setState(() {}));
passwordController = TextEditingController();
}
@override
void onClose() {
super.onClose();
passwordController.dispose();
}
bool get isAuth {
_isAuth.value = token != null;
return _isAuth.value;
}
String? get token {
if (_expiryDate != null &&
_expiryDate!.isAfter(DateTime.now()) &&
_token != null) {
return _token;
}
return null;
}
String? get userId {
return _userId;
}
Future<void> _authenticate(
String email, String password, String urlSegment) async {
// print('app is here!!!5555');
// const host = "localhost";
final host = UniversalPlatform.isAndroid ? '10.0.2.2' : '127.0.0.1';
final url = Uri.parse('http://$host:8000/api/$urlSegment');
try {
final http.Response response = await http.post(
url,
headers: {"Content-Type": "application/json"},
body: json.encode(
{
'email': email,
'password': password,
//'returnSecureToken': true,
},
),
);
// print('this is responsde ' );
// print(response);
final responseData = json.decode(response.body);
print(responseData);
if (responseData['error'] != null) {
throw HttpException(responseData['error']['message']);
} else {
_token = responseData['idToken'];
_userId = responseData['id'];
_expiryDate = DateTime.now().add(
Duration(
milliseconds: responseData['expiresIn'],
),
);
}
_autoLogout();
// update();
final prefs = await SharedPreferences.getInstance();
final userData = json.encode(
{
'token': _token,
'userId': _userId,
'expiryDate': _expiryDate!.toIso8601String(),
},
);
prefs.setString('userData', userData);
isLoading?.value = false;
// print(prefs.getString('userData'));
Get.toNamed(rootRoute);
} catch (error) {
throw error;
}
}
Future<void> signup(String email, String password) async {
return _authenticate(email, password, 'signup');
}
Future<void> login(String email, String password) async {
return _authenticate(email, password, 'sessions');
}
Future<bool> tryAutoLogin() async {
final prefs = await SharedPreferences.getInstance();
if (!prefs.containsKey('userData')) {
return false;
}
final Map<String, Object> extractedUserData = Map<String, Object>.from(
json.decode(prefs.getString('userData') as String));
final expiryDate =
DateTime.parse(extractedUserData['expiryDate'] as String);
if (expiryDate.isBefore(DateTime.now())) {
return false;
}
_token = extractedUserData['token'] as String;
_userId = extractedUserData['userId'] as String;
_expiryDate = expiryDate;
_isAuth.value = true;
_autoLogout();
return true;
}
Future<void> logout() async {
_token = null;
_userId = null;
_expiryDate = null;
if (_authTimer != null) {
_authTimer!.cancel();
_authTimer = null;
}
// update();
final prefs = await SharedPreferences.getInstance();
// prefs.remove('userData');
prefs.clear();
_isAuth.value = false;
}
void _autoLogout() {
if (_authTimer != null) {
_authTimer!.cancel();
}
final timeToExpiry = _expiryDate!.difference(DateTime.now()).inSeconds;
_authTimer = Timer(Duration(seconds: timeToExpiry), logout);
}
}
When I start the application it seems it runs with no errors but when as soon as I click on TextFormField
s to enter email or password, the virtual keyboard on the Android Emulator opens and closes immediately and doesn't let me enter anything. Also it shows the following message within the DEBUG CONSOLE:
D/InputConnectionAdaptor( 3218): The input method toggled cursor monitoring on
Upvotes: 2
Views: 11451
Reputation: 1486
I use hooks_riverpod. The following code fixed the issue for me inside the build method.
final formKey = useMemoized(() => GlobalKey<FormState>());
I hope this applies if you are using flutter_hooks as well.
Upvotes: 0
Reputation: 1
I was facing the same problem. Solution is to use Stateful Widget and put the _formkey direct under the class scope and not under Widget build.
class _SignupFormState extends State<SignupForm> {
final _formkey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
//rest of the code
}
Upvotes: 0
Reputation: 51
In my case, solution was taking GlobalKey() out of build () method
Closing keyboard:
Form(key: GlobalKey<FormState>());
Solution:
final _formKey = GlobalKey<FormState>();
Form(key: _formKey)
Upvotes: 2
Reputation: 1662
Setting the "key
" property on the TextFormField
solved this problem for me. I created the key in the initState
method (although it could probably be created in other places), using the GlobalKey<FormFieldState>()
constructor.
Upvotes: 0
Reputation: 814
After So many efforts I found that instead of StateLessWidget
Please use StatefullWidget
for the same.
I hope it helps you!
Upvotes: 8