'Is it possible to use "formKey.currentState" inside a stateless widget with GetX?
I am trying to convert a code that uses provider package and stateful widget to use GetX package and stateless widget. One problem I do have is that I have a form that changes(from login to signup and vice versa) using animation, but when I check the result of the following line if (!_formKey.currentState!.validate()) it seems it doesn't work.
This is the related codes:
auth_controller.dart:
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;
@override
void onInit() {
super.onInit();
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(() {}));
}
.
.
.
auth_screen.dart:
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
elevation: 8.0,
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.easeIn,
height: _authMode!.value == AuthMode.Signup ? 320 : 260,
//height: _heightAnimation.value.height,
constraints: BoxConstraints(
minHeight: _authMode.value == AuthMode.Signup ? 320 : 260),
width: deviceSize.width * 0.75,
padding: const EdgeInsets.all(16.0),
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;
},
),
AnimatedContainer(
constraints: BoxConstraints(
minHeight: _authMode.value == AuthMode.Signup ? 60 : 0,
maxHeight: _authMode.value == AuthMode.Signup ? 120 : 0,
),
duration: const Duration(milliseconds: 300),
curve: Curves.easeIn,
child: FadeTransition(
opacity: _opacityAnimation as Animation<double>,
child: SlideTransition(
position: _slideAnimation as Animation<Offset>,
child: TextFormField(
enabled: _authMode.value == AuthMode.Signup,
decoration: const InputDecoration(
labelText: 'Confirm Password'),
obscureText: true,
validator: _authMode.value == AuthMode.Signup
? (value) {
if (value != _passwordController.text) {
return 'Passwords do not match!';
}
}
: null,
),
),
),
),
const SizedBox(
height: 20,
),
if (_isLoading!.value)
const CircularProgressIndicator()
else
ElevatedButton(
child: Text(_authMode.value == AuthMode.Login
? 'LOGIN'
: 'SIGN UP'),
onPressed: _submit,
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
primary: Theme.of(context).primaryColor,
padding: const EdgeInsets.symmetric(
horizontal: 30.0, vertical: 8.0),
onPrimary: Theme.of(context)
.primaryTextTheme
.button!
.color,
),
),
TextButton(
child: Text(
'${_authMode.value == AuthMode.Login ? 'SIGNUP' : 'LOGIN'} '),
onPressed: _switchAuthMode,
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 30.0, vertical: 4),
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
textStyle: TextStyle(
color: Theme.of(context).primaryColor),
),
),
],
),
),
)),
),
);
I don't know what is the problem and how can I convert the code in a right way? I also don't know if is it possible at all or not?
Solution 1:[1]
Yes, you can use FormState with GetX controllers and a stateless widget, however you need to instantiate the GlobalKey in your controller and then ensure the controller is "Put" on the UI for use.
class YourController extends GetXController {
final formKey = GlobalKey<FormState>();
}
Then inject the controller as a dependency in your stateless widget
class YourWidget extends StatelessWidget {
final getxController = Get.put(YourController());
}
Then add it to your Form Widget
child: Form(
key: getxController.formKey,
Once you have added the required validation functions to your controller and TextInput widgets you can call formKey.Validate() from within your Getx extended controller class
void submitForm() {
final isValid = formKey.currentState.validate();
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Yonkee |
